]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0-or-later | |
2 | /* | |
3 | * IS-IS Rout(e)ing protocol - BFD support | |
4 | * Copyright (C) 2018 Christian Franke | |
5 | */ | |
6 | #include <zebra.h> | |
7 | ||
8 | #include "zclient.h" | |
9 | #include "nexthop.h" | |
10 | #include "bfd.h" | |
11 | #include "lib_errors.h" | |
12 | ||
13 | #include "isisd/isis_bfd.h" | |
14 | #include "isisd/isis_zebra.h" | |
15 | #include "isisd/isis_common.h" | |
16 | #include "isisd/isis_constants.h" | |
17 | #include "isisd/isis_adjacency.h" | |
18 | #include "isisd/isis_circuit.h" | |
19 | #include "isisd/isisd.h" | |
20 | #include "isisd/fabricd.h" | |
21 | ||
22 | DEFINE_MTYPE_STATIC(ISISD, BFD_SESSION, "ISIS BFD Session"); | |
23 | ||
24 | static void adj_bfd_cb(struct bfd_session_params *bsp, | |
25 | const struct bfd_session_status *bss, void *arg) | |
26 | { | |
27 | struct isis_adjacency *adj = arg; | |
28 | ||
29 | if (IS_DEBUG_BFD) | |
30 | zlog_debug( | |
31 | "ISIS-BFD: BFD changed status for adjacency %s old %s new %s", | |
32 | isis_adj_name(adj), | |
33 | bfd_get_status_str(bss->previous_state), | |
34 | bfd_get_status_str(bss->state)); | |
35 | ||
36 | if (bss->state == BFD_STATUS_DOWN | |
37 | && bss->previous_state == BFD_STATUS_UP) { | |
38 | adj->circuit->area->bfd_signalled_down = true; | |
39 | isis_adj_state_change(&adj, ISIS_ADJ_DOWN, | |
40 | "bfd session went down"); | |
41 | } | |
42 | } | |
43 | ||
44 | static void bfd_handle_adj_down(struct isis_adjacency *adj) | |
45 | { | |
46 | bfd_sess_free(&adj->bfd_session); | |
47 | } | |
48 | ||
49 | static void bfd_handle_adj_up(struct isis_adjacency *adj) | |
50 | { | |
51 | struct isis_circuit *circuit = adj->circuit; | |
52 | int family; | |
53 | union g_addr dst_ip; | |
54 | union g_addr src_ip; | |
55 | struct list *local_ips; | |
56 | struct prefix *local_ip; | |
57 | ||
58 | if (!circuit->bfd_config.enabled) { | |
59 | if (IS_DEBUG_BFD) | |
60 | zlog_debug( | |
61 | "ISIS-BFD: skipping BFD initialization on adjacency with %s because BFD is not enabled for the circuit", | |
62 | isis_adj_name(adj)); | |
63 | goto out; | |
64 | } | |
65 | ||
66 | /* If IS-IS IPv6 is configured wait for IPv6 address to be programmed | |
67 | * before starting up BFD | |
68 | */ | |
69 | if (circuit->ipv6_router | |
70 | && (listcount(circuit->ipv6_link) == 0 | |
71 | || adj->ll_ipv6_count == 0)) { | |
72 | if (IS_DEBUG_BFD) | |
73 | zlog_debug( | |
74 | "ISIS-BFD: skipping BFD initialization on adjacency with %s because IPv6 is enabled but not ready", | |
75 | isis_adj_name(adj)); | |
76 | return; | |
77 | } | |
78 | ||
79 | /* | |
80 | * If IS-IS is enabled for both IPv4 and IPv6 on the circuit, prefer | |
81 | * creating a BFD session over IPv6. | |
82 | */ | |
83 | if (circuit->ipv6_router && adj->ll_ipv6_count) { | |
84 | family = AF_INET6; | |
85 | dst_ip.ipv6 = adj->ll_ipv6_addrs[0]; | |
86 | local_ips = circuit->ipv6_link; | |
87 | if (list_isempty(local_ips)) { | |
88 | if (IS_DEBUG_BFD) | |
89 | zlog_debug( | |
90 | "ISIS-BFD: skipping BFD initialization: IPv6 enabled and no local IPv6 addresses"); | |
91 | goto out; | |
92 | } | |
93 | local_ip = listgetdata(listhead(local_ips)); | |
94 | src_ip.ipv6 = local_ip->u.prefix6; | |
95 | } else if (circuit->ip_router && adj->ipv4_address_count) { | |
96 | family = AF_INET; | |
97 | dst_ip.ipv4 = adj->ipv4_addresses[0]; | |
98 | local_ips = fabricd_ip_addrs(adj->circuit); | |
99 | if (!local_ips || list_isempty(local_ips)) { | |
100 | if (IS_DEBUG_BFD) | |
101 | zlog_debug( | |
102 | "ISIS-BFD: skipping BFD initialization: IPv4 enabled and no local IPv4 addresses"); | |
103 | goto out; | |
104 | } | |
105 | local_ip = listgetdata(listhead(local_ips)); | |
106 | src_ip.ipv4 = local_ip->u.prefix4; | |
107 | } else | |
108 | goto out; | |
109 | ||
110 | if (adj->bfd_session == NULL) | |
111 | adj->bfd_session = bfd_sess_new(adj_bfd_cb, adj); | |
112 | ||
113 | bfd_sess_set_timers(adj->bfd_session, BFD_DEF_DETECT_MULT, | |
114 | BFD_DEF_MIN_RX, BFD_DEF_MIN_TX); | |
115 | if (family == AF_INET) | |
116 | bfd_sess_set_ipv4_addrs(adj->bfd_session, &src_ip.ipv4, | |
117 | &dst_ip.ipv4); | |
118 | else | |
119 | bfd_sess_set_ipv6_addrs(adj->bfd_session, &src_ip.ipv6, | |
120 | &dst_ip.ipv6); | |
121 | bfd_sess_set_interface(adj->bfd_session, adj->circuit->interface->name); | |
122 | bfd_sess_set_vrf(adj->bfd_session, | |
123 | adj->circuit->interface->vrf->vrf_id); | |
124 | bfd_sess_set_profile(adj->bfd_session, circuit->bfd_config.profile); | |
125 | bfd_sess_install(adj->bfd_session); | |
126 | return; | |
127 | out: | |
128 | bfd_handle_adj_down(adj); | |
129 | } | |
130 | ||
131 | static int bfd_handle_adj_state_change(struct isis_adjacency *adj) | |
132 | { | |
133 | if (adj->adj_state == ISIS_ADJ_UP) | |
134 | bfd_handle_adj_up(adj); | |
135 | else | |
136 | bfd_handle_adj_down(adj); | |
137 | return 0; | |
138 | } | |
139 | ||
140 | static void bfd_adj_cmd(struct isis_adjacency *adj) | |
141 | { | |
142 | if (adj->adj_state == ISIS_ADJ_UP && adj->circuit->bfd_config.enabled) | |
143 | bfd_handle_adj_up(adj); | |
144 | else | |
145 | bfd_handle_adj_down(adj); | |
146 | } | |
147 | ||
148 | void isis_bfd_circuit_cmd(struct isis_circuit *circuit) | |
149 | { | |
150 | switch (circuit->circ_type) { | |
151 | case CIRCUIT_T_BROADCAST: | |
152 | for (int level = ISIS_LEVEL1; level <= ISIS_LEVEL2; level++) { | |
153 | struct list *adjdb = circuit->u.bc.adjdb[level - 1]; | |
154 | ||
155 | struct listnode *node; | |
156 | struct isis_adjacency *adj; | |
157 | ||
158 | if (!adjdb) | |
159 | continue; | |
160 | for (ALL_LIST_ELEMENTS_RO(adjdb, node, adj)) | |
161 | bfd_adj_cmd(adj); | |
162 | } | |
163 | break; | |
164 | case CIRCUIT_T_P2P: | |
165 | if (circuit->u.p2p.neighbor) | |
166 | bfd_adj_cmd(circuit->u.p2p.neighbor); | |
167 | break; | |
168 | default: | |
169 | break; | |
170 | } | |
171 | } | |
172 | ||
173 | static int bfd_handle_adj_ip_enabled(struct isis_adjacency *adj, int family, | |
174 | bool global) | |
175 | { | |
176 | ||
177 | if (family != AF_INET6 || global) | |
178 | return 0; | |
179 | ||
180 | if (adj->bfd_session) | |
181 | return 0; | |
182 | ||
183 | if (adj->adj_state != ISIS_ADJ_UP) | |
184 | return 0; | |
185 | ||
186 | bfd_handle_adj_up(adj); | |
187 | ||
188 | return 0; | |
189 | } | |
190 | ||
191 | static int bfd_handle_circuit_add_addr(struct isis_circuit *circuit) | |
192 | { | |
193 | struct isis_adjacency *adj; | |
194 | struct listnode *node; | |
195 | ||
196 | if (circuit->area == NULL) | |
197 | return 0; | |
198 | ||
199 | for (ALL_LIST_ELEMENTS_RO(circuit->area->adjacency_list, node, adj)) { | |
200 | if (adj->bfd_session) | |
201 | continue; | |
202 | ||
203 | if (adj->adj_state != ISIS_ADJ_UP) | |
204 | continue; | |
205 | ||
206 | bfd_handle_adj_up(adj); | |
207 | } | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | void isis_bfd_init(struct thread_master *tm) | |
213 | { | |
214 | bfd_protocol_integration_init(zclient, tm); | |
215 | ||
216 | hook_register(isis_adj_state_change_hook, bfd_handle_adj_state_change); | |
217 | hook_register(isis_adj_ip_enabled_hook, bfd_handle_adj_ip_enabled); | |
218 | hook_register(isis_circuit_add_addr_hook, bfd_handle_circuit_add_addr); | |
219 | } |