/src/frr/zebra/zebra_srte.c
Line | Count | Source |
1 | | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | | /* Zebra SR-TE code |
3 | | * Copyright (C) 2020 NetDEF, Inc. |
4 | | */ |
5 | | |
6 | | #include <zebra.h> |
7 | | |
8 | | #include "lib/zclient.h" |
9 | | #include "lib/lib_errors.h" |
10 | | |
11 | | #include "zebra/zebra_srte.h" |
12 | | #include "zebra/zebra_mpls.h" |
13 | | #include "zebra/zebra_rnh.h" |
14 | | #include "zebra/zapi_msg.h" |
15 | | |
16 | 2 | DEFINE_MTYPE_STATIC(ZEBRA, ZEBRA_SR_POLICY, "SR Policy"); |
17 | 2 | |
18 | 2 | static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy); |
19 | 2 | |
20 | 2 | /* Generate rb-tree of SR Policy instances. */ |
21 | 2 | static inline int |
22 | 2 | zebra_sr_policy_instance_compare(const struct zebra_sr_policy *a, |
23 | 2 | const struct zebra_sr_policy *b) |
24 | 2 | { |
25 | 0 | return sr_policy_compare(&a->endpoint, &b->endpoint, a->color, |
26 | 0 | b->color); |
27 | 0 | } |
28 | | RB_GENERATE(zebra_sr_policy_instance_head, zebra_sr_policy, entry, |
29 | | zebra_sr_policy_instance_compare) |
30 | | |
31 | | struct zebra_sr_policy_instance_head zebra_sr_policy_instances = |
32 | | RB_INITIALIZER(&zebra_sr_policy_instances); |
33 | | |
34 | | struct zebra_sr_policy *zebra_sr_policy_add(uint32_t color, |
35 | | struct ipaddr *endpoint, char *name) |
36 | 0 | { |
37 | 0 | struct zebra_sr_policy *policy; |
38 | |
|
39 | 0 | policy = XCALLOC(MTYPE_ZEBRA_SR_POLICY, sizeof(*policy)); |
40 | 0 | policy->color = color; |
41 | 0 | policy->endpoint = *endpoint; |
42 | 0 | strlcpy(policy->name, name, sizeof(policy->name)); |
43 | 0 | policy->status = ZEBRA_SR_POLICY_DOWN; |
44 | 0 | RB_INSERT(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, |
45 | 0 | policy); |
46 | |
|
47 | 0 | return policy; |
48 | 0 | } |
49 | | |
50 | | void zebra_sr_policy_del(struct zebra_sr_policy *policy) |
51 | 0 | { |
52 | 0 | if (policy->status == ZEBRA_SR_POLICY_UP) |
53 | 0 | zebra_sr_policy_deactivate(policy); |
54 | 0 | RB_REMOVE(zebra_sr_policy_instance_head, &zebra_sr_policy_instances, |
55 | 0 | policy); |
56 | 0 | XFREE(MTYPE_ZEBRA_SR_POLICY, policy); |
57 | 0 | } |
58 | | |
59 | | struct zebra_sr_policy *zebra_sr_policy_find(uint32_t color, |
60 | | struct ipaddr *endpoint) |
61 | 0 | { |
62 | 0 | struct zebra_sr_policy policy = {}; |
63 | |
|
64 | 0 | policy.color = color; |
65 | 0 | policy.endpoint = *endpoint; |
66 | 0 | return RB_FIND(zebra_sr_policy_instance_head, |
67 | 0 | &zebra_sr_policy_instances, &policy); |
68 | 0 | } |
69 | | |
70 | | struct zebra_sr_policy *zebra_sr_policy_find_by_name(char *name) |
71 | 0 | { |
72 | 0 | struct zebra_sr_policy *policy; |
73 | | |
74 | | // TODO: create index for policy names |
75 | 0 | RB_FOREACH (policy, zebra_sr_policy_instance_head, |
76 | 0 | &zebra_sr_policy_instances) { |
77 | 0 | if (strcmp(policy->name, name) == 0) |
78 | 0 | return policy; |
79 | 0 | } |
80 | | |
81 | 0 | return NULL; |
82 | 0 | } |
83 | | |
84 | | static int zebra_sr_policy_notify_update_client(struct zebra_sr_policy *policy, |
85 | | struct zserv *client) |
86 | 0 | { |
87 | 0 | const struct zebra_nhlfe *nhlfe; |
88 | 0 | struct stream *s; |
89 | 0 | uint32_t message = 0; |
90 | 0 | unsigned long nump = 0; |
91 | 0 | uint8_t num; |
92 | 0 | struct zapi_nexthop znh; |
93 | 0 | int ret; |
94 | | |
95 | | /* Get output stream. */ |
96 | 0 | s = stream_new(ZEBRA_MAX_PACKET_SIZ); |
97 | |
|
98 | 0 | zclient_create_header(s, ZEBRA_NEXTHOP_UPDATE, zvrf_id(policy->zvrf)); |
99 | | |
100 | | /* Message flags. */ |
101 | 0 | SET_FLAG(message, ZAPI_MESSAGE_SRTE); |
102 | 0 | stream_putl(s, message); |
103 | |
|
104 | 0 | stream_putw(s, SAFI_UNICAST); |
105 | | /* |
106 | | * The prefix is copied twice because the ZEBRA_NEXTHOP_UPDATE |
107 | | * code was modified to send back both the matched against |
108 | | * as well as the actual matched. There does not appear to |
109 | | * be an equivalent here so just send the same thing twice. |
110 | | */ |
111 | 0 | switch (policy->endpoint.ipa_type) { |
112 | 0 | case IPADDR_V4: |
113 | 0 | stream_putw(s, AF_INET); |
114 | 0 | stream_putc(s, IPV4_MAX_BITLEN); |
115 | 0 | stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); |
116 | 0 | stream_putw(s, AF_INET); |
117 | 0 | stream_putc(s, IPV4_MAX_BITLEN); |
118 | 0 | stream_put_in_addr(s, &policy->endpoint.ipaddr_v4); |
119 | 0 | break; |
120 | 0 | case IPADDR_V6: |
121 | 0 | stream_putw(s, AF_INET6); |
122 | 0 | stream_putc(s, IPV6_MAX_BITLEN); |
123 | 0 | stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); |
124 | 0 | stream_putw(s, AF_INET6); |
125 | 0 | stream_putc(s, IPV6_MAX_BITLEN); |
126 | 0 | stream_put(s, &policy->endpoint.ipaddr_v6, IPV6_MAX_BYTELEN); |
127 | 0 | break; |
128 | 0 | case IPADDR_NONE: |
129 | 0 | flog_warn(EC_LIB_DEVELOPMENT, |
130 | 0 | "%s: unknown policy endpoint address family: %u", |
131 | 0 | __func__, policy->endpoint.ipa_type); |
132 | 0 | exit(1); |
133 | 0 | } |
134 | 0 | stream_putl(s, policy->color); |
135 | |
|
136 | 0 | num = 0; |
137 | 0 | frr_each (nhlfe_list_const, &policy->lsp->nhlfe_list, nhlfe) { |
138 | 0 | if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) |
139 | 0 | || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) |
140 | 0 | continue; |
141 | | |
142 | 0 | if (num == 0) { |
143 | 0 | stream_putc(s, re_type_from_lsp_type(nhlfe->type)); |
144 | 0 | stream_putw(s, 0); /* instance - not available */ |
145 | 0 | stream_putc(s, nhlfe->distance); |
146 | 0 | stream_putl(s, 0); /* metric - not available */ |
147 | 0 | nump = stream_get_endp(s); |
148 | 0 | stream_putc(s, 0); |
149 | 0 | } |
150 | |
|
151 | 0 | zapi_nexthop_from_nexthop(&znh, nhlfe->nexthop); |
152 | 0 | ret = zapi_nexthop_encode(s, &znh, 0, message); |
153 | 0 | if (ret < 0) |
154 | 0 | goto failure; |
155 | | |
156 | 0 | num++; |
157 | 0 | } |
158 | 0 | stream_putc_at(s, nump, num); |
159 | 0 | stream_putw_at(s, 0, stream_get_endp(s)); |
160 | |
|
161 | 0 | client->nh_last_upd_time = monotime(NULL); |
162 | 0 | return zserv_send_message(client, s); |
163 | | |
164 | 0 | failure: |
165 | |
|
166 | 0 | stream_free(s); |
167 | 0 | return -1; |
168 | 0 | } |
169 | | |
170 | | static void zebra_sr_policy_notify_update(struct zebra_sr_policy *policy) |
171 | 0 | { |
172 | 0 | struct rnh *rnh; |
173 | 0 | struct prefix p = {}; |
174 | 0 | struct zebra_vrf *zvrf; |
175 | 0 | struct listnode *node; |
176 | 0 | struct zserv *client; |
177 | |
|
178 | 0 | zvrf = policy->zvrf; |
179 | 0 | switch (policy->endpoint.ipa_type) { |
180 | 0 | case IPADDR_V4: |
181 | 0 | p.family = AF_INET; |
182 | 0 | p.prefixlen = IPV4_MAX_BITLEN; |
183 | 0 | p.u.prefix4 = policy->endpoint.ipaddr_v4; |
184 | 0 | break; |
185 | 0 | case IPADDR_V6: |
186 | 0 | p.family = AF_INET6; |
187 | 0 | p.prefixlen = IPV6_MAX_BITLEN; |
188 | 0 | p.u.prefix6 = policy->endpoint.ipaddr_v6; |
189 | 0 | break; |
190 | 0 | case IPADDR_NONE: |
191 | 0 | flog_warn(EC_LIB_DEVELOPMENT, |
192 | 0 | "%s: unknown policy endpoint address family: %u", |
193 | 0 | __func__, policy->endpoint.ipa_type); |
194 | 0 | exit(1); |
195 | 0 | } |
196 | | |
197 | 0 | rnh = zebra_lookup_rnh(&p, zvrf_id(zvrf), SAFI_UNICAST); |
198 | 0 | if (!rnh) |
199 | 0 | return; |
200 | | |
201 | 0 | for (ALL_LIST_ELEMENTS_RO(rnh->client_list, node, client)) { |
202 | 0 | if (policy->status == ZEBRA_SR_POLICY_UP) |
203 | 0 | zebra_sr_policy_notify_update_client(policy, client); |
204 | 0 | else |
205 | | /* Fallback to the IGP shortest path. */ |
206 | 0 | zebra_send_rnh_update(rnh, client, zvrf_id(zvrf), |
207 | 0 | policy->color); |
208 | 0 | } |
209 | 0 | } |
210 | | |
211 | | static void zebra_sr_policy_activate(struct zebra_sr_policy *policy, |
212 | | struct zebra_lsp *lsp) |
213 | 0 | { |
214 | 0 | policy->status = ZEBRA_SR_POLICY_UP; |
215 | 0 | policy->lsp = lsp; |
216 | 0 | (void)zebra_sr_policy_bsid_install(policy); |
217 | 0 | zsend_sr_policy_notify_status(policy->color, &policy->endpoint, |
218 | 0 | policy->name, ZEBRA_SR_POLICY_UP); |
219 | 0 | zebra_sr_policy_notify_update(policy); |
220 | 0 | } |
221 | | |
222 | | static void zebra_sr_policy_update(struct zebra_sr_policy *policy, |
223 | | struct zebra_lsp *lsp, |
224 | | struct zapi_srte_tunnel *old_tunnel) |
225 | 0 | { |
226 | 0 | bool bsid_changed; |
227 | 0 | bool segment_list_changed; |
228 | |
|
229 | 0 | policy->lsp = lsp; |
230 | |
|
231 | 0 | bsid_changed = |
232 | 0 | policy->segment_list.local_label != old_tunnel->local_label; |
233 | 0 | segment_list_changed = |
234 | 0 | policy->segment_list.label_num != old_tunnel->label_num |
235 | 0 | || memcmp(policy->segment_list.labels, old_tunnel->labels, |
236 | 0 | sizeof(mpls_label_t) |
237 | 0 | * policy->segment_list.label_num); |
238 | | |
239 | | /* Re-install label stack if necessary. */ |
240 | 0 | if (bsid_changed || segment_list_changed) { |
241 | 0 | zebra_sr_policy_bsid_uninstall(policy, old_tunnel->local_label); |
242 | 0 | (void)zebra_sr_policy_bsid_install(policy); |
243 | 0 | } |
244 | |
|
245 | 0 | zsend_sr_policy_notify_status(policy->color, &policy->endpoint, |
246 | 0 | policy->name, ZEBRA_SR_POLICY_UP); |
247 | | |
248 | | /* Handle segment-list update. */ |
249 | 0 | if (segment_list_changed) |
250 | 0 | zebra_sr_policy_notify_update(policy); |
251 | 0 | } |
252 | | |
253 | | static void zebra_sr_policy_deactivate(struct zebra_sr_policy *policy) |
254 | 0 | { |
255 | 0 | policy->status = ZEBRA_SR_POLICY_DOWN; |
256 | 0 | policy->lsp = NULL; |
257 | 0 | zebra_sr_policy_bsid_uninstall(policy, |
258 | 0 | policy->segment_list.local_label); |
259 | 0 | zsend_sr_policy_notify_status(policy->color, &policy->endpoint, |
260 | 0 | policy->name, ZEBRA_SR_POLICY_DOWN); |
261 | 0 | zebra_sr_policy_notify_update(policy); |
262 | 0 | } |
263 | | |
264 | | int zebra_sr_policy_validate(struct zebra_sr_policy *policy, |
265 | | struct zapi_srte_tunnel *new_tunnel) |
266 | 0 | { |
267 | 0 | struct zapi_srte_tunnel old_tunnel = policy->segment_list; |
268 | 0 | struct zebra_lsp *lsp; |
269 | |
|
270 | 0 | if (new_tunnel) |
271 | 0 | policy->segment_list = *new_tunnel; |
272 | | |
273 | | /* Try to resolve the Binding-SID nexthops. */ |
274 | 0 | lsp = mpls_lsp_find(policy->zvrf, policy->segment_list.labels[0]); |
275 | 0 | if (!lsp || !lsp->best_nhlfe |
276 | 0 | || lsp->addr_family != ipaddr_family(&policy->endpoint)) { |
277 | 0 | if (policy->status == ZEBRA_SR_POLICY_UP) |
278 | 0 | zebra_sr_policy_deactivate(policy); |
279 | 0 | return -1; |
280 | 0 | } |
281 | | |
282 | | /* First label was resolved successfully. */ |
283 | 0 | if (policy->status == ZEBRA_SR_POLICY_DOWN) |
284 | 0 | zebra_sr_policy_activate(policy, lsp); |
285 | 0 | else |
286 | 0 | zebra_sr_policy_update(policy, lsp, &old_tunnel); |
287 | |
|
288 | 0 | return 0; |
289 | 0 | } |
290 | | |
291 | | int zebra_sr_policy_bsid_install(struct zebra_sr_policy *policy) |
292 | 0 | { |
293 | 0 | struct zapi_srte_tunnel *zt = &policy->segment_list; |
294 | 0 | struct zebra_nhlfe *nhlfe; |
295 | |
|
296 | 0 | if (zt->local_label == MPLS_LABEL_NONE) |
297 | 0 | return 0; |
298 | | |
299 | 0 | frr_each_safe (nhlfe_list, &policy->lsp->nhlfe_list, nhlfe) { |
300 | 0 | uint8_t num_out_labels; |
301 | 0 | mpls_label_t *out_labels; |
302 | 0 | mpls_label_t null_label = MPLS_LABEL_IMPLICIT_NULL; |
303 | |
|
304 | 0 | if (!CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_SELECTED) |
305 | 0 | || CHECK_FLAG(nhlfe->flags, NHLFE_FLAG_DELETED)) |
306 | 0 | continue; |
307 | | |
308 | | /* |
309 | | * Don't push the first SID if the corresponding action in the |
310 | | * LFIB is POP. |
311 | | */ |
312 | 0 | if (!nhlfe->nexthop->nh_label |
313 | 0 | || !nhlfe->nexthop->nh_label->num_labels |
314 | 0 | || nhlfe->nexthop->nh_label->label[0] |
315 | 0 | == MPLS_LABEL_IMPLICIT_NULL) { |
316 | 0 | if (zt->label_num > 1) { |
317 | 0 | num_out_labels = zt->label_num - 1; |
318 | 0 | out_labels = &zt->labels[1]; |
319 | 0 | } else { |
320 | 0 | num_out_labels = 1; |
321 | 0 | out_labels = &null_label; |
322 | 0 | } |
323 | 0 | } else { |
324 | 0 | num_out_labels = zt->label_num; |
325 | 0 | out_labels = zt->labels; |
326 | 0 | } |
327 | |
|
328 | 0 | if (mpls_lsp_install( |
329 | 0 | policy->zvrf, zt->type, zt->local_label, |
330 | 0 | num_out_labels, out_labels, nhlfe->nexthop->type, |
331 | 0 | &nhlfe->nexthop->gate, nhlfe->nexthop->ifindex) |
332 | 0 | < 0) |
333 | 0 | return -1; |
334 | 0 | } |
335 | | |
336 | 0 | return 0; |
337 | 0 | } |
338 | | |
339 | | void zebra_sr_policy_bsid_uninstall(struct zebra_sr_policy *policy, |
340 | | mpls_label_t old_bsid) |
341 | 0 | { |
342 | 0 | struct zapi_srte_tunnel *zt = &policy->segment_list; |
343 | |
|
344 | 0 | mpls_lsp_uninstall_all_vrf(policy->zvrf, zt->type, old_bsid); |
345 | 0 | } |
346 | | |
347 | | int zebra_sr_policy_label_update(mpls_label_t label, |
348 | | enum zebra_sr_policy_update_label_mode mode) |
349 | 0 | { |
350 | 0 | struct zebra_sr_policy *policy; |
351 | |
|
352 | 0 | RB_FOREACH (policy, zebra_sr_policy_instance_head, |
353 | 0 | &zebra_sr_policy_instances) { |
354 | 0 | mpls_label_t next_hop_label; |
355 | |
|
356 | 0 | next_hop_label = policy->segment_list.labels[0]; |
357 | 0 | if (next_hop_label != label) |
358 | 0 | continue; |
359 | | |
360 | 0 | switch (mode) { |
361 | 0 | case ZEBRA_SR_POLICY_LABEL_CREATED: |
362 | 0 | case ZEBRA_SR_POLICY_LABEL_UPDATED: |
363 | 0 | case ZEBRA_SR_POLICY_LABEL_REMOVED: |
364 | 0 | zebra_sr_policy_validate(policy, NULL); |
365 | 0 | break; |
366 | 0 | } |
367 | 0 | } |
368 | | |
369 | 0 | return 0; |
370 | 0 | } |
371 | | |
372 | | static int zebra_srte_client_close_cleanup(struct zserv *client) |
373 | 0 | { |
374 | 0 | int sock = client->sock; |
375 | 0 | struct zebra_sr_policy *policy, *policy_temp; |
376 | |
|
377 | 0 | if (!sock) |
378 | 0 | return 0; |
379 | | |
380 | 0 | RB_FOREACH_SAFE (policy, zebra_sr_policy_instance_head, |
381 | 0 | &zebra_sr_policy_instances, policy_temp) { |
382 | 0 | if (policy->sock == sock) |
383 | 0 | zebra_sr_policy_del(policy); |
384 | 0 | } |
385 | 0 | return 1; |
386 | 0 | } |
387 | | |
388 | | void zebra_srte_init(void) |
389 | 0 | { |
390 | 0 | hook_register(zserv_client_close, zebra_srte_client_close_cleanup); |
391 | 0 | } |