]>
Commit | Line | Data |
---|---|---|
5435a2bf | 1 | /* |
63d4bd12 QY |
2 | * VRRP CLI commands. |
3 | * Copyright (C) 2018-2019 Cumulus Networks, Inc. | |
4 | * Quentin Young | |
5435a2bf QY |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the Free | |
8 | * Software Foundation; either version 2 of the License, or (at your option) | |
9 | * any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
14 | * more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; see the file COPYING; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | #include <zebra.h> | |
21 | ||
862f2f37 QY |
22 | #include "lib/command.h" |
23 | #include "lib/if.h" | |
24 | #include "lib/ipaddr.h" | |
6e21b5ae | 25 | #include "lib/json.h" |
862f2f37 QY |
26 | #include "lib/prefix.h" |
27 | #include "lib/termtable.h" | |
28 | #include "lib/vty.h" | |
5435a2bf QY |
29 | |
30 | #include "vrrp.h" | |
78fb3dbe | 31 | #include "vrrp_debug.h" |
78fb3dbe | 32 | #include "vrrp_vty.h" |
c23edd74 QY |
33 | #ifndef VTYSH_EXTRACT_PL |
34 | #include "vrrpd/vrrp_vty_clippy.c" | |
35 | #endif | |
5435a2bf QY |
36 | |
37 | ||
38 | #define VRRP_STR "Virtual Router Redundancy Protocol\n" | |
39 | #define VRRP_VRID_STR "Virtual Router ID\n" | |
c23edd74 | 40 | #define VRRP_PRIORITY_STR "Virtual Router Priority\n" |
6287cefe | 41 | #define VRRP_ADVINT_STR "Virtual Router Advertisement Interval\n" |
c23edd74 | 42 | #define VRRP_IP_STR "Virtual Router IPv4 address\n" |
99966840 | 43 | #define VRRP_VERSION_STR "VRRP protocol version\n" |
c23edd74 | 44 | |
4f0b6b45 | 45 | #define VROUTER_GET_VTY(_vty, _ifp, _vrid, _vr) \ |
c23edd74 | 46 | do { \ |
4f0b6b45 | 47 | _vr = vrrp_lookup(_ifp, _vrid); \ |
c23edd74 QY |
48 | if (!_vr) { \ |
49 | vty_out(_vty, \ | |
50 | "%% Please configure VRRP instance %u\n", \ | |
51 | (unsigned int)_vrid); \ | |
52 | return CMD_WARNING_CONFIG_FAILED; \ | |
53 | } \ | |
2fff50ec | 54 | } while (0) |
5435a2bf | 55 | |
b637bcd4 QY |
56 | /* clang-format off */ |
57 | ||
c23edd74 | 58 | DEFPY(vrrp_vrid, |
5435a2bf | 59 | vrrp_vrid_cmd, |
99966840 | 60 | "[no] vrrp (1-255)$vrid [version (2-3)]", |
5435a2bf QY |
61 | NO_STR |
62 | VRRP_STR | |
99966840 QY |
63 | VRRP_VRID_STR |
64 | VRRP_VERSION_STR | |
65 | VRRP_VERSION_STR) | |
5435a2bf QY |
66 | { |
67 | VTY_DECLVAR_CONTEXT(interface, ifp); | |
5435a2bf | 68 | |
4f0b6b45 | 69 | struct vrrp_vrouter *vr = vrrp_lookup(ifp, vrid); |
6287cefe | 70 | |
99966840 QY |
71 | if (version == 0) |
72 | version = 3; | |
73 | ||
6287cefe QY |
74 | if (no && vr) |
75 | vrrp_vrouter_destroy(vr); | |
76 | else if (no && !vr) | |
77 | vty_out(vty, "%% VRRP instance %ld does not exist on %s\n", | |
78 | vrid, ifp->name); | |
79 | else if (!vr) | |
99966840 | 80 | vrrp_vrouter_create(ifp, vrid, version); |
6287cefe QY |
81 | else if (vr) |
82 | vty_out(vty, "%% VRRP instance %ld already exists on %s\n", | |
83 | vrid, ifp->name); | |
5435a2bf QY |
84 | |
85 | return CMD_SUCCESS; | |
86 | } | |
87 | ||
f96a183b QY |
88 | DEFPY(vrrp_shutdown, |
89 | vrrp_shutdown_cmd, | |
90 | "[no] vrrp (1-255)$vrid shutdown", | |
91 | NO_STR | |
92 | VRRP_STR | |
93 | VRRP_VRID_STR | |
94 | "Force VRRP router into administrative shutdown\n") | |
95 | { | |
96 | VTY_DECLVAR_CONTEXT(interface, ifp); | |
97 | ||
98 | struct vrrp_vrouter *vr; | |
99 | ||
100 | VROUTER_GET_VTY(vty, ifp, vrid, vr); | |
101 | ||
102 | if (!no) { | |
103 | if (vr->v4->fsm.state != VRRP_STATE_INITIALIZE) | |
104 | vrrp_event(vr->v4, VRRP_EVENT_SHUTDOWN); | |
105 | if (vr->v6->fsm.state != VRRP_STATE_INITIALIZE) | |
106 | vrrp_event(vr->v6, VRRP_EVENT_SHUTDOWN); | |
107 | vr->shutdown = true; | |
108 | } else { | |
109 | vr->shutdown = false; | |
110 | vrrp_check_start(vr); | |
111 | } | |
112 | ||
113 | return CMD_SUCCESS; | |
114 | } | |
115 | ||
c23edd74 QY |
116 | DEFPY(vrrp_priority, |
117 | vrrp_priority_cmd, | |
d7cfcdc0 | 118 | "[no] vrrp (1-255)$vrid priority (1-254)", |
c23edd74 QY |
119 | NO_STR |
120 | VRRP_STR | |
121 | VRRP_VRID_STR | |
122 | VRRP_PRIORITY_STR | |
bac08ded | 123 | "Priority value") |
c23edd74 | 124 | { |
4f0b6b45 QY |
125 | VTY_DECLVAR_CONTEXT(interface, ifp); |
126 | ||
c23edd74 | 127 | struct vrrp_vrouter *vr; |
8cd1d277 | 128 | uint8_t newprio = no ? vd.priority : priority; |
c23edd74 | 129 | |
4f0b6b45 | 130 | VROUTER_GET_VTY(vty, ifp, vrid, vr); |
1d21789e | 131 | |
6287cefe | 132 | vrrp_set_priority(vr, newprio); |
1d21789e | 133 | |
1d21789e QY |
134 | return CMD_SUCCESS; |
135 | } | |
136 | ||
137 | DEFPY(vrrp_advertisement_interval, | |
138 | vrrp_advertisement_interval_cmd, | |
cb44d476 | 139 | "[no] vrrp (1-255)$vrid advertisement-interval (10-40950)", |
6287cefe | 140 | NO_STR VRRP_STR VRRP_VRID_STR VRRP_ADVINT_STR |
cb44d476 | 141 | "Advertisement interval in milliseconds; must be multiple of 10") |
1d21789e | 142 | { |
4f0b6b45 QY |
143 | VTY_DECLVAR_CONTEXT(interface, ifp); |
144 | ||
1d21789e | 145 | struct vrrp_vrouter *vr; |
19c38250 QY |
146 | uint16_t newadvint = |
147 | no ? vd.advertisement_interval * 10 : advertisement_interval; | |
1d21789e | 148 | |
cb44d476 QY |
149 | if (newadvint % 10 != 0) { |
150 | vty_out(vty, "%% Value must be a multiple of 10\n"); | |
151 | return CMD_WARNING_CONFIG_FAILED; | |
152 | } | |
153 | ||
154 | /* all internal computations are in centiseconds */ | |
155 | newadvint /= CS2MS; | |
156 | ||
4f0b6b45 | 157 | VROUTER_GET_VTY(vty, ifp, vrid, vr); |
6287cefe | 158 | vrrp_set_advertisement_interval(vr, newadvint); |
c23edd74 QY |
159 | |
160 | return CMD_SUCCESS; | |
161 | } | |
162 | ||
163 | DEFPY(vrrp_ip, | |
164 | vrrp_ip_cmd, | |
862f2f37 | 165 | "[no] vrrp (1-255)$vrid ip A.B.C.D", |
c23edd74 QY |
166 | NO_STR |
167 | VRRP_STR | |
168 | VRRP_VRID_STR | |
862f2f37 | 169 | "Add IPv4 address\n" |
c23edd74 QY |
170 | VRRP_IP_STR) |
171 | { | |
4f0b6b45 QY |
172 | VTY_DECLVAR_CONTEXT(interface, ifp); |
173 | ||
c23edd74 | 174 | struct vrrp_vrouter *vr; |
2cd90902 QY |
175 | bool deactivated = false; |
176 | bool activated = false; | |
177 | bool failed = false; | |
178 | int ret = CMD_SUCCESS; | |
2fff50ec | 179 | int oldstate; |
c23edd74 | 180 | |
4f0b6b45 | 181 | VROUTER_GET_VTY(vty, ifp, vrid, vr); |
c23edd74 | 182 | |
2cd90902 | 183 | bool will_activate = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE); |
862f2f37 | 184 | |
2cd90902 | 185 | if (no) { |
2fff50ec | 186 | oldstate = vr->v4->fsm.state; |
6e93585e QY |
187 | failed = vrrp_del_ipv4(vr, ip); |
188 | vrrp_check_start(vr); | |
2cd90902 QY |
189 | deactivated = (vr->v4->fsm.state == VRRP_STATE_INITIALIZE |
190 | && oldstate != VRRP_STATE_INITIALIZE); | |
862f2f37 | 191 | } else { |
2fff50ec | 192 | oldstate = vr->v4->fsm.state; |
6e93585e QY |
193 | failed = vrrp_add_ipv4(vr, ip); |
194 | vrrp_check_start(vr); | |
2cd90902 QY |
195 | activated = (vr->v4->fsm.state != VRRP_STATE_INITIALIZE |
196 | && oldstate == VRRP_STATE_INITIALIZE); | |
197 | } | |
198 | ||
199 | if (activated) | |
200 | vty_out(vty, "%% Activated IPv4 Virtual Router %ld\n", vrid); | |
201 | if (deactivated) | |
202 | vty_out(vty, "%% Deactivated IPv4 Virtual Router %ld\n", vrid); | |
203 | if (failed) { | |
1af0eb1f | 204 | vty_out(vty, "%% Failed to %s virtual IP\n", |
2cd90902 QY |
205 | no ? "remove" : "add"); |
206 | ret = CMD_WARNING_CONFIG_FAILED; | |
207 | if (will_activate && !activated) { | |
208 | vty_out(vty, | |
209 | "%% Failed to activate IPv4 Virtual Router %ld\n", | |
210 | vrid); | |
211 | } | |
862f2f37 QY |
212 | } |
213 | ||
214 | return ret; | |
215 | } | |
216 | ||
217 | DEFPY(vrrp_ip6, | |
218 | vrrp_ip6_cmd, | |
219 | "[no] vrrp (1-255)$vrid ipv6 X:X::X:X", | |
220 | NO_STR | |
221 | VRRP_STR | |
222 | VRRP_VRID_STR | |
223 | "Add IPv6 address\n" | |
224 | VRRP_IP_STR) | |
225 | { | |
4f0b6b45 QY |
226 | VTY_DECLVAR_CONTEXT(interface, ifp); |
227 | ||
862f2f37 | 228 | struct vrrp_vrouter *vr; |
2cd90902 QY |
229 | bool deactivated = false; |
230 | bool activated = false; | |
231 | bool failed = false; | |
232 | int ret = CMD_SUCCESS; | |
2fff50ec | 233 | int oldstate; |
862f2f37 | 234 | |
4f0b6b45 | 235 | VROUTER_GET_VTY(vty, ifp, vrid, vr); |
862f2f37 | 236 | |
99966840 QY |
237 | if (vr->version != 3) { |
238 | vty_out(vty, | |
239 | "%% Cannot add IPv6 address to VRRPv2 virtual router\n"); | |
240 | return CMD_WARNING_CONFIG_FAILED; | |
241 | } | |
242 | ||
2cd90902 | 243 | bool will_activate = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE); |
1d21789e | 244 | |
2cd90902 | 245 | if (no) { |
2fff50ec | 246 | oldstate = vr->v6->fsm.state; |
6e93585e | 247 | failed = vrrp_del_ipv6(vr, ipv6); |
29ef66fa | 248 | vrrp_check_start(vr); |
2cd90902 QY |
249 | deactivated = (vr->v6->fsm.state == VRRP_STATE_INITIALIZE |
250 | && oldstate != VRRP_STATE_INITIALIZE); | |
1d21789e | 251 | } else { |
2fff50ec | 252 | oldstate = vr->v6->fsm.state; |
6e93585e | 253 | failed = vrrp_add_ipv6(vr, ipv6); |
29ef66fa | 254 | vrrp_check_start(vr); |
2cd90902 QY |
255 | activated = (vr->v6->fsm.state != VRRP_STATE_INITIALIZE |
256 | && oldstate == VRRP_STATE_INITIALIZE); | |
257 | } | |
258 | ||
259 | if (activated) | |
260 | vty_out(vty, "%% Activated IPv6 Virtual Router %ld\n", vrid); | |
261 | if (deactivated) | |
262 | vty_out(vty, "%% Deactivated IPv6 Virtual Router %ld\n", vrid); | |
263 | if (failed) { | |
d5a6ff43 | 264 | vty_out(vty, "%% Failed to %s virtual IP\n", |
2cd90902 QY |
265 | no ? "remove" : "add"); |
266 | ret = CMD_WARNING_CONFIG_FAILED; | |
267 | if (will_activate && !activated) { | |
268 | vty_out(vty, | |
d5a6ff43 | 269 | "%% Failed to activate IPv6 Virtual Router %ld\n", |
2cd90902 QY |
270 | vrid); |
271 | } | |
1d21789e QY |
272 | } |
273 | ||
274 | return ret; | |
c23edd74 QY |
275 | } |
276 | ||
8ec51216 QY |
277 | DEFPY(vrrp_preempt, |
278 | vrrp_preempt_cmd, | |
279 | "[no] vrrp (1-255)$vrid preempt", | |
280 | NO_STR | |
281 | VRRP_STR | |
282 | VRRP_VRID_STR | |
283 | "Preempt mode\n") | |
284 | { | |
285 | VTY_DECLVAR_CONTEXT(interface, ifp); | |
286 | ||
287 | struct vrrp_vrouter *vr; | |
288 | ||
289 | VROUTER_GET_VTY(vty, ifp, vrid, vr); | |
290 | ||
291 | vr->preempt_mode = !no; | |
292 | ||
293 | return CMD_SUCCESS; | |
294 | } | |
295 | ||
27fd8827 QY |
296 | DEFPY(vrrp_autoconfigure, |
297 | vrrp_autoconfigure_cmd, | |
298 | "[no] vrrp autoconfigure [version (2-3)]", | |
53e60e5c QY |
299 | NO_STR |
300 | VRRP_STR | |
301 | "Automatically set up VRRP instances on VRRP-compatible interfaces\n" | |
302 | "Version for automatically configured instances\n" | |
303 | VRRP_VERSION_STR) | |
304 | { | |
53e60e5c QY |
305 | version = version ? version : 3; |
306 | ||
27fd8827 QY |
307 | if (!no) |
308 | vrrp_autoconfig_on(version); | |
309 | else | |
310 | vrrp_autoconfig_off(); | |
53e60e5c QY |
311 | |
312 | return CMD_SUCCESS; | |
313 | } | |
314 | ||
8cd1d277 QY |
315 | DEFPY(vrrp_default, |
316 | vrrp_default_cmd, | |
33b010a9 | 317 | "[no] vrrp default <advertisement-interval$adv (10-40950)$advint|preempt$p|priority$prio (1-254)$prioval|shutdown$s>", |
8cd1d277 QY |
318 | NO_STR |
319 | VRRP_STR | |
320 | "Configure defaults for new VRRP instances\n" | |
321 | VRRP_ADVINT_STR | |
33b010a9 | 322 | "Advertisement interval in milliseconds\n" |
8cd1d277 QY |
323 | "Preempt mode\n" |
324 | VRRP_PRIORITY_STR | |
325 | "Priority value\n" | |
326 | "Force VRRP router into administrative shutdown\n") | |
327 | { | |
33b010a9 QY |
328 | if (adv) { |
329 | if (advint % 10 != 0) { | |
330 | vty_out(vty, "%% Value must be a multiple of 10\n"); | |
331 | return CMD_WARNING_CONFIG_FAILED; | |
332 | } | |
333 | /* all internal computations are in centiseconds */ | |
334 | advint /= CS2MS; | |
8cd1d277 | 335 | vd.advertisement_interval = no ? VRRP_DEFAULT_ADVINT : advint; |
33b010a9 | 336 | } |
8cd1d277 QY |
337 | if (p) |
338 | vd.preempt_mode = !no; | |
339 | if (prio) | |
340 | vd.priority = no ? VRRP_DEFAULT_PRIORITY : prioval; | |
341 | if (s) | |
342 | vd.shutdown = !no; | |
343 | ||
344 | return CMD_SUCCESS; | |
345 | } | |
346 | ||
6332c77f QY |
347 | /* clang-format on */ |
348 | ||
6e21b5ae QY |
349 | /* |
350 | * Build JSON representation of VRRP instance. | |
351 | * | |
352 | * vr | |
353 | * VRRP router to build json object from | |
354 | * | |
355 | * Returns: | |
356 | * JSON representation of VRRP instance. Must be freed by caller. | |
357 | */ | |
358 | static struct json_object *vrrp_build_json(struct vrrp_vrouter *vr) | |
359 | { | |
360 | char ethstr4[ETHER_ADDR_STRLEN]; | |
361 | char ethstr6[ETHER_ADDR_STRLEN]; | |
362 | char ipstr[INET6_ADDRSTRLEN]; | |
363 | const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; | |
364 | const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; | |
23804b4f QY |
365 | char sipstr4[INET6_ADDRSTRLEN] = {}; |
366 | char sipstr6[INET6_ADDRSTRLEN] = {}; | |
6e21b5ae QY |
367 | struct listnode *ln; |
368 | struct ipaddr *ip; | |
369 | struct json_object *j = json_object_new_object(); | |
370 | struct json_object *v4 = json_object_new_object(); | |
6332c77f | 371 | struct json_object *v4_stats = json_object_new_object(); |
6e21b5ae QY |
372 | struct json_object *v4_addrs = json_object_new_array(); |
373 | struct json_object *v6 = json_object_new_object(); | |
6332c77f | 374 | struct json_object *v6_stats = json_object_new_object(); |
6e21b5ae QY |
375 | struct json_object *v6_addrs = json_object_new_array(); |
376 | ||
377 | prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); | |
378 | prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); | |
379 | ||
380 | json_object_int_add(j, "vrid", vr->vrid); | |
381 | json_object_int_add(j, "version", vr->version); | |
382 | json_object_boolean_add(j, "autoconfigured", vr->autoconf); | |
383 | json_object_boolean_add(j, "shutdown", vr->shutdown); | |
b1d7f513 QY |
384 | json_object_boolean_add(j, "preemptMode", vr->preempt_mode); |
385 | json_object_boolean_add(j, "acceptMode", vr->accept_mode); | |
6e21b5ae | 386 | json_object_string_add(j, "interface", vr->ifp->name); |
0a7b203e QY |
387 | json_object_int_add(j, "advertisementInterval", |
388 | vr->advertisement_interval * CS2MS); | |
6e21b5ae QY |
389 | /* v4 */ |
390 | json_object_string_add(v4, "interface", | |
391 | vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : ""); | |
392 | json_object_string_add(v4, "vmac", ethstr4); | |
23804b4f QY |
393 | ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); |
394 | json_object_string_add(v4, "primaryAddress", sipstr4); | |
6e21b5ae | 395 | json_object_string_add(v4, "status", stastr4); |
b1d7f513 QY |
396 | json_object_int_add(v4, "effectivePriority", vr->v4->priority); |
397 | json_object_int_add(v4, "masterAdverInterval", | |
cb44d476 QY |
398 | vr->v4->master_adver_interval * CS2MS); |
399 | json_object_int_add(v4, "skewTime", vr->v4->skew_time * CS2MS); | |
b1d7f513 | 400 | json_object_int_add(v4, "masterDownInterval", |
cb44d476 | 401 | vr->v4->master_down_interval * CS2MS); |
6332c77f QY |
402 | /* v4 stats */ |
403 | json_object_int_add(v4_stats, "adverTx", vr->v4->stats.adver_tx_cnt); | |
404 | json_object_int_add(v4_stats, "adverRx", vr->v4->stats.adver_rx_cnt); | |
405 | json_object_int_add(v4_stats, "garpTx", vr->v4->stats.garp_tx_cnt); | |
406 | json_object_int_add(v4_stats, "transitions", vr->v4->stats.trans_cnt); | |
407 | json_object_object_add(v4, "stats", v4_stats); | |
408 | /* v4 addrs */ | |
6e21b5ae QY |
409 | if (vr->v4->addrs->count) { |
410 | for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { | |
411 | inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, | |
412 | sizeof(ipstr)); | |
413 | json_object_array_add(v4_addrs, | |
414 | json_object_new_string(ipstr)); | |
415 | } | |
416 | } | |
417 | json_object_object_add(v4, "addresses", v4_addrs); | |
418 | json_object_object_add(j, "v4", v4); | |
419 | ||
420 | /* v6 */ | |
421 | json_object_string_add(v6, "interface", | |
422 | vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : ""); | |
423 | json_object_string_add(v6, "vmac", ethstr6); | |
23804b4f QY |
424 | ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); |
425 | if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) | |
426 | strlcat(sipstr6, "::", sizeof(sipstr6)); | |
427 | json_object_string_add(v6, "primaryAddress", sipstr6); | |
6e21b5ae | 428 | json_object_string_add(v6, "status", stastr6); |
b1d7f513 QY |
429 | json_object_int_add(v6, "effectivePriority", vr->v6->priority); |
430 | json_object_int_add(v6, "masterAdverInterval", | |
cb44d476 QY |
431 | vr->v6->master_adver_interval * CS2MS); |
432 | json_object_int_add(v6, "skewTime", vr->v6->skew_time * CS2MS); | |
b1d7f513 | 433 | json_object_int_add(v6, "masterDownInterval", |
cb44d476 | 434 | vr->v6->master_down_interval * CS2MS); |
6332c77f QY |
435 | /* v6 stats */ |
436 | json_object_int_add(v6_stats, "adverTx", vr->v6->stats.adver_tx_cnt); | |
437 | json_object_int_add(v6_stats, "adverRx", vr->v6->stats.adver_rx_cnt); | |
438 | json_object_int_add(v6_stats, "neighborAdverTx", | |
439 | vr->v6->stats.una_tx_cnt); | |
440 | json_object_int_add(v6_stats, "transitions", vr->v6->stats.trans_cnt); | |
441 | json_object_object_add(v6, "stats", v6_stats); | |
442 | /* v6 addrs */ | |
6e21b5ae QY |
443 | if (vr->v6->addrs->count) { |
444 | for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { | |
445 | inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, | |
446 | sizeof(ipstr)); | |
447 | json_object_array_add(v6_addrs, | |
448 | json_object_new_string(ipstr)); | |
449 | } | |
450 | } | |
451 | json_object_object_add(v6, "addresses", v6_addrs); | |
452 | json_object_object_add(j, "v6", v6); | |
453 | ||
454 | return j; | |
455 | } | |
456 | ||
789ce3af QY |
457 | /* |
458 | * Dump VRRP instance status to VTY. | |
459 | * | |
460 | * vty | |
461 | * vty to dump to | |
462 | * | |
463 | * vr | |
464 | * VRRP router to dump | |
465 | */ | |
1d21789e | 466 | static void vrrp_show(struct vty *vty, struct vrrp_vrouter *vr) |
c23edd74 | 467 | { |
862f2f37 QY |
468 | char ethstr4[ETHER_ADDR_STRLEN]; |
469 | char ethstr6[ETHER_ADDR_STRLEN]; | |
470 | char ipstr[INET6_ADDRSTRLEN]; | |
471 | const char *stastr4 = vrrp_state_names[vr->v4->fsm.state]; | |
472 | const char *stastr6 = vrrp_state_names[vr->v6->fsm.state]; | |
23804b4f QY |
473 | char sipstr4[INET6_ADDRSTRLEN] = {}; |
474 | char sipstr6[INET6_ADDRSTRLEN] = {}; | |
862f2f37 QY |
475 | struct listnode *ln; |
476 | struct ipaddr *ip; | |
c23edd74 QY |
477 | |
478 | struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); | |
479 | ||
480 | ttable_add_row(tt, "%s|%" PRIu32, "Virtual Router ID", vr->vrid); | |
99966840 | 481 | ttable_add_row(tt, "%s|%" PRIu8, "Protocol Version", vr->version); |
27fd8827 QY |
482 | ttable_add_row(tt, "%s|%s", "Autoconfigured", |
483 | vr->autoconf ? "Yes" : "No"); | |
f96a183b | 484 | ttable_add_row(tt, "%s|%s", "Shutdown", vr->shutdown ? "Yes" : "No"); |
5302f67b | 485 | ttable_add_row(tt, "%s|%s", "Interface", vr->ifp->name); |
862f2f37 QY |
486 | prefix_mac2str(&vr->v4->vmac, ethstr4, sizeof(ethstr4)); |
487 | prefix_mac2str(&vr->v6->vmac, ethstr6, sizeof(ethstr6)); | |
5302f67b QY |
488 | ttable_add_row(tt, "%s|%s", "VRRP interface (v4)", |
489 | vr->v4->mvl_ifp ? vr->v4->mvl_ifp->name : "None"); | |
490 | ttable_add_row(tt, "%s|%s", "VRRP interface (v6)", | |
491 | vr->v6->mvl_ifp ? vr->v6->mvl_ifp->name : "None"); | |
c7e65c4f QY |
492 | ipaddr2str(&vr->v4->src, sipstr4, sizeof(sipstr4)); |
493 | ipaddr2str(&vr->v6->src, sipstr6, sizeof(sipstr6)); | |
23804b4f QY |
494 | if (strlen(sipstr6) == 0 && vr->v6->src.ip.addr == 0x00) |
495 | strlcat(sipstr6, "::", sizeof(sipstr6)); | |
c7e65c4f QY |
496 | ttable_add_row(tt, "%s|%s", "Primary IP (v4)", sipstr4); |
497 | ttable_add_row(tt, "%s|%s", "Primary IP (v6)", sipstr6); | |
862f2f37 QY |
498 | ttable_add_row(tt, "%s|%s", "Virtual MAC (v4)", ethstr4); |
499 | ttable_add_row(tt, "%s|%s", "Virtual MAC (v6)", ethstr6); | |
500 | ttable_add_row(tt, "%s|%s", "Status (v4)", stastr4); | |
501 | ttable_add_row(tt, "%s|%s", "Status (v6)", stastr6); | |
c23edd74 | 502 | ttable_add_row(tt, "%s|%" PRIu8, "Priority", vr->priority); |
862f2f37 QY |
503 | ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v4)", |
504 | vr->v4->priority); | |
505 | ttable_add_row(tt, "%s|%" PRIu8, "Effective Priority (v6)", | |
506 | vr->v6->priority); | |
c23edd74 QY |
507 | ttable_add_row(tt, "%s|%s", "Preempt Mode", |
508 | vr->preempt_mode ? "Yes" : "No"); | |
509 | ttable_add_row(tt, "%s|%s", "Accept Mode", | |
510 | vr->accept_mode ? "Yes" : "No"); | |
cb44d476 QY |
511 | ttable_add_row(tt, "%s|%d ms", "Advertisement Interval", |
512 | vr->advertisement_interval * CS2MS); | |
513 | ttable_add_row(tt, "%s|%d ms", | |
789ce3af | 514 | "Master Advertisement Interval (v4)", |
cb44d476 QY |
515 | vr->v4->master_adver_interval * CS2MS); |
516 | ttable_add_row(tt, "%s|%d ms", | |
789ce3af | 517 | "Master Advertisement Interval (v6)", |
cb44d476 | 518 | vr->v6->master_adver_interval * CS2MS); |
6332c77f QY |
519 | ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v4)", |
520 | vr->v4->stats.adver_tx_cnt); | |
521 | ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Tx (v6)", | |
522 | vr->v6->stats.adver_tx_cnt); | |
523 | ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v4)", | |
524 | vr->v4->stats.adver_rx_cnt); | |
525 | ttable_add_row(tt, "%s|%" PRIu32, "Advertisements Rx (v6)", | |
526 | vr->v6->stats.adver_rx_cnt); | |
527 | ttable_add_row(tt, "%s|%" PRIu32, "Gratuitous ARP Tx (v4)", | |
528 | vr->v4->stats.garp_tx_cnt); | |
529 | ttable_add_row(tt, "%s|%" PRIu32, "Neigh. Adverts Tx (v6)", | |
530 | vr->v6->stats.una_tx_cnt); | |
531 | ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v4)", | |
532 | vr->v4->stats.trans_cnt); | |
533 | ttable_add_row(tt, "%s|%" PRIu32, "State transitions (v6)", | |
534 | vr->v6->stats.trans_cnt); | |
cb44d476 QY |
535 | ttable_add_row(tt, "%s|%d ms", "Skew Time (v4)", |
536 | vr->v4->skew_time * CS2MS); | |
537 | ttable_add_row(tt, "%s|%d ms", "Skew Time (v6)", | |
538 | vr->v6->skew_time * CS2MS); | |
539 | ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v4)", | |
540 | vr->v4->master_down_interval * CS2MS); | |
541 | ttable_add_row(tt, "%s|%d ms", "Master Down Interval (v6)", | |
542 | vr->v6->master_down_interval * CS2MS); | |
862f2f37 | 543 | ttable_add_row(tt, "%s|%u", "IPv4 Addresses", vr->v4->addrs->count); |
c23edd74 | 544 | |
92c399a4 | 545 | char fill[35]; |
2fff50ec | 546 | |
5302f67b QY |
547 | memset(fill, '.', sizeof(fill)); |
548 | fill[sizeof(fill) - 1] = 0x00; | |
862f2f37 | 549 | if (vr->v4->addrs->count) { |
862f2f37 QY |
550 | for (ALL_LIST_ELEMENTS_RO(vr->v4->addrs, ln, ip)) { |
551 | inet_ntop(vr->v4->family, &ip->ipaddr_v4, ipstr, | |
552 | sizeof(ipstr)); | |
5302f67b | 553 | ttable_add_row(tt, "%s|%s", fill, ipstr); |
862f2f37 | 554 | } |
862f2f37 QY |
555 | } |
556 | ||
5302f67b QY |
557 | ttable_add_row(tt, "%s|%u", "IPv6 Addresses", vr->v6->addrs->count); |
558 | ||
862f2f37 | 559 | if (vr->v6->addrs->count) { |
862f2f37 QY |
560 | for (ALL_LIST_ELEMENTS_RO(vr->v6->addrs, ln, ip)) { |
561 | inet_ntop(vr->v6->family, &ip->ipaddr_v6, ipstr, | |
562 | sizeof(ipstr)); | |
5302f67b | 563 | ttable_add_row(tt, "%s|%s", fill, ipstr); |
c23edd74 | 564 | } |
c23edd74 | 565 | } |
5302f67b QY |
566 | |
567 | char *table = ttable_dump(tt, "\n"); | |
2fff50ec | 568 | |
5302f67b QY |
569 | vty_out(vty, "\n%s\n", table); |
570 | XFREE(MTYPE_TMP, table); | |
571 | ttable_del(tt); | |
1d21789e QY |
572 | } |
573 | ||
30a1595d QY |
574 | /* |
575 | * Sort comparator, used when sorting VRRP instances for display purposes. | |
576 | * | |
577 | * Sorts by interface name first, then by VRID ascending. | |
578 | */ | |
579 | static int vrrp_instance_display_sort_cmp(const void **d1, const void **d2) | |
580 | { | |
581 | const struct vrrp_vrouter *vr1 = *d1; | |
582 | const struct vrrp_vrouter *vr2 = *d2; | |
583 | int result; | |
584 | ||
585 | result = strcmp(vr1->ifp->name, vr2->ifp->name); | |
586 | result += !result * (vr1->vrid - vr2->vrid); | |
587 | ||
588 | return result; | |
589 | } | |
590 | ||
6332c77f QY |
591 | /* clang-format off */ |
592 | ||
1d21789e QY |
593 | DEFPY(vrrp_vrid_show, |
594 | vrrp_vrid_show_cmd, | |
6e21b5ae | 595 | "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] [json$json]", |
1d21789e QY |
596 | SHOW_STR |
597 | VRRP_STR | |
4f0b6b45 QY |
598 | INTERFACE_STR |
599 | "Only show VRRP instances on this interface\n" | |
6e21b5ae QY |
600 | VRRP_VRID_STR |
601 | JSON_STR) | |
1d21789e QY |
602 | { |
603 | struct vrrp_vrouter *vr; | |
4f0b6b45 QY |
604 | struct listnode *ln; |
605 | struct list *ll = hash_to_list(vrrp_vrouters_hash); | |
6e21b5ae | 606 | struct json_object *j = json_object_new_array(); |
1d21789e | 607 | |
30a1595d QY |
608 | list_sort(ll, vrrp_instance_display_sort_cmp); |
609 | ||
4f0b6b45 QY |
610 | for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { |
611 | if (ifn && !strmatch(ifn, vr->ifp->name)) | |
612 | continue; | |
48cd8f13 | 613 | if (vrid && ((uint8_t) vrid) != vr->vrid) |
4f0b6b45 | 614 | continue; |
5302f67b | 615 | |
6e21b5ae QY |
616 | if (!json) |
617 | vrrp_show(vty, vr); | |
618 | else | |
619 | json_object_array_add(j, vrrp_build_json(vr)); | |
1d21789e | 620 | } |
c23edd74 | 621 | |
6e21b5ae QY |
622 | if (json) |
623 | vty_out(vty, "%s\n", | |
624 | json_object_to_json_string_ext( | |
625 | j, JSON_C_TO_STRING_PRETTY)); | |
626 | ||
627 | json_object_free(j); | |
628 | ||
4f0b6b45 QY |
629 | list_delete(&ll); |
630 | ||
c23edd74 QY |
631 | return CMD_SUCCESS; |
632 | } | |
633 | ||
30a1595d QY |
634 | DEFPY(vrrp_vrid_show_summary, |
635 | vrrp_vrid_show_summary_cmd, | |
636 | "show vrrp [interface INTERFACE$ifn] [(1-255)$vrid] summary", | |
637 | SHOW_STR | |
638 | VRRP_STR | |
639 | INTERFACE_STR | |
640 | "Only show VRRP instances on this interface\n" | |
641 | VRRP_VRID_STR | |
642 | "Summarize all VRRP instances\n") | |
643 | { | |
644 | struct vrrp_vrouter *vr; | |
645 | struct listnode *ln; | |
646 | struct list *ll = hash_to_list(vrrp_vrouters_hash); | |
647 | ||
648 | list_sort(ll, vrrp_instance_display_sort_cmp); | |
649 | ||
650 | struct ttable *tt = ttable_new(&ttable_styles[TTSTYLE_BLANK]); | |
651 | ||
652 | ttable_add_row( | |
653 | tt, "Interface|VRID|Priority|IPv4|IPv6|State (v4)|State (v6)"); | |
654 | ttable_rowseps(tt, 0, BOTTOM, true, '-'); | |
655 | ||
656 | for (ALL_LIST_ELEMENTS_RO(ll, ln, vr)) { | |
657 | if (ifn && !strmatch(ifn, vr->ifp->name)) | |
658 | continue; | |
659 | if (vrid && ((uint8_t)vrid) != vr->vrid) | |
660 | continue; | |
661 | ||
662 | ttable_add_row( | |
663 | tt, "%s|%" PRIu8 "|%" PRIu8 "|%d|%d|%s|%s", | |
664 | vr->ifp->name, vr->vrid, vr->priority, | |
665 | vr->v4->addrs->count, vr->v6->addrs->count, | |
666 | vr->v4->fsm.state == VRRP_STATE_MASTER ? "Master" | |
667 | : "Backup", | |
668 | vr->v6->fsm.state == VRRP_STATE_MASTER ? "Master" | |
669 | : "Backup"); | |
670 | } | |
671 | ||
672 | char *table = ttable_dump(tt, "\n"); | |
673 | ||
674 | vty_out(vty, "\n%s\n", table); | |
675 | XFREE(MTYPE_TMP, table); | |
676 | ttable_del(tt); | |
677 | ||
678 | list_delete(&ll); | |
679 | ||
680 | return CMD_SUCCESS; | |
681 | } | |
682 | ||
78fb3dbe QY |
683 | |
684 | DEFPY(debug_vrrp, | |
685 | debug_vrrp_cmd, | |
686 | "[no] debug vrrp [{protocol$proto|autoconfigure$ac|packets$pkt|sockets$sock|ndisc$ndisc|arp$arp|zebra$zebra}]", | |
687 | NO_STR | |
688 | DEBUG_STR | |
689 | VRRP_STR | |
690 | "Debug protocol state\n" | |
691 | "Debug autoconfiguration\n" | |
692 | "Debug sent and received packets\n" | |
693 | "Debug socket creation and configuration\n" | |
694 | "Debug Neighbor Discovery\n" | |
695 | "Debug ARP\n" | |
696 | "Debug Zebra events\n") | |
697 | { | |
698 | /* If no specific are given on/off them all */ | |
699 | if (strmatch(argv[argc - 1]->text, "vrrp")) | |
700 | vrrp_debug_set(NULL, 0, vty->node, !no, true, true, true, true, | |
701 | true, true, true); | |
702 | else | |
703 | vrrp_debug_set(NULL, 0, vty->node, !no, !!proto, !!ac, !!pkt, | |
704 | !!sock, !!ndisc, !!arp, !!zebra); | |
705 | ||
706 | return CMD_SUCCESS; | |
707 | } | |
708 | ||
709 | DEFUN_NOSH (show_debugging_vrrp, | |
710 | show_debugging_vrrp_cmd, | |
711 | "show debugging [vrrp]", | |
712 | SHOW_STR | |
713 | DEBUG_STR | |
714 | "VRRP information\n") | |
715 | { | |
716 | vty_out(vty, "VRRP debugging status:\n"); | |
717 | ||
718 | vrrp_debug_status_write(vty); | |
719 | ||
720 | return CMD_SUCCESS; | |
721 | } | |
722 | ||
b637bcd4 QY |
723 | /* clang-format on */ |
724 | ||
f828842a | 725 | static struct cmd_node interface_node = {INTERFACE_NODE, "%s(config-if)# ", 1}; |
78fb3dbe | 726 | static struct cmd_node debug_node = {DEBUG_NODE, "", 1}; |
f828842a | 727 | static struct cmd_node vrrp_node = {VRRP_NODE, "", 1}; |
78fb3dbe | 728 | |
5435a2bf QY |
729 | void vrrp_vty_init(void) |
730 | { | |
f828842a QY |
731 | install_node(&debug_node, vrrp_config_write_debug); |
732 | install_node(&interface_node, vrrp_config_write_interface); | |
733 | install_node(&vrrp_node, vrrp_config_write_global); | |
5435a2bf | 734 | if_cmd_init(); |
78fb3dbe | 735 | |
c23edd74 | 736 | install_element(VIEW_NODE, &vrrp_vrid_show_cmd); |
30a1595d | 737 | install_element(VIEW_NODE, &vrrp_vrid_show_summary_cmd); |
78fb3dbe QY |
738 | install_element(VIEW_NODE, &show_debugging_vrrp_cmd); |
739 | install_element(VIEW_NODE, &debug_vrrp_cmd); | |
740 | install_element(CONFIG_NODE, &debug_vrrp_cmd); | |
27fd8827 | 741 | install_element(CONFIG_NODE, &vrrp_autoconfigure_cmd); |
8cd1d277 | 742 | install_element(CONFIG_NODE, &vrrp_default_cmd); |
5435a2bf | 743 | install_element(INTERFACE_NODE, &vrrp_vrid_cmd); |
f96a183b | 744 | install_element(INTERFACE_NODE, &vrrp_shutdown_cmd); |
c23edd74 | 745 | install_element(INTERFACE_NODE, &vrrp_priority_cmd); |
1d21789e | 746 | install_element(INTERFACE_NODE, &vrrp_advertisement_interval_cmd); |
c23edd74 | 747 | install_element(INTERFACE_NODE, &vrrp_ip_cmd); |
862f2f37 | 748 | install_element(INTERFACE_NODE, &vrrp_ip6_cmd); |
8ec51216 | 749 | install_element(INTERFACE_NODE, &vrrp_preempt_cmd); |
5435a2bf | 750 | } |