]>
Commit | Line | Data |
---|---|---|
acddc0ed | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
8a71d93d DS |
2 | /* |
3 | * Zebra connect code. | |
4 | * Copyright (C) Cumulus Networks, Inc. | |
5 | * Donald Sharp | |
8a71d93d DS |
6 | */ |
7 | #include <zebra.h> | |
8 | ||
24a58196 | 9 | #include "frrevent.h" |
8a71d93d DS |
10 | #include "command.h" |
11 | #include "network.h" | |
12 | #include "prefix.h" | |
8a71d93d DS |
13 | #include "stream.h" |
14 | #include "memory.h" | |
15 | #include "zclient.h" | |
8a71d93d | 16 | #include "nexthop.h" |
694b242f | 17 | #include "nexthop_group.h" |
1888e243 | 18 | #include "link_state.h" |
04bc334e | 19 | #include "tc.h" |
8a71d93d | 20 | |
547dc642 | 21 | #include "sharp_globals.h" |
86da53ab | 22 | #include "sharp_nht.h" |
8a71d93d DS |
23 | #include "sharp_zebra.h" |
24 | ||
25 | /* Zebra structure to hold current status. */ | |
26 | struct zclient *zclient = NULL; | |
27 | ||
28 | /* For registering threads. */ | |
cd9d0537 | 29 | extern struct event_loop *master; |
8a71d93d | 30 | |
2be4d61a | 31 | /* Privs info */ |
04cbc08f | 32 | extern struct zebra_privs_t sharp_privs; |
2be4d61a MS |
33 | |
34 | DEFINE_MTYPE_STATIC(SHARPD, ZC, "Test zclients"); | |
35 | ||
36 | /* Struct to hold list of test zclients */ | |
37 | struct sharp_zclient { | |
38 | struct sharp_zclient *prev; | |
39 | struct sharp_zclient *next; | |
40 | struct zclient *client; | |
41 | }; | |
42 | ||
43 | /* Head of test zclient list */ | |
44 | static struct sharp_zclient *sharp_clients_head; | |
45 | ||
46 | static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS); | |
47 | ||
48 | /* Utility to add a test zclient struct to the list */ | |
49 | static void add_zclient(struct zclient *client) | |
50 | { | |
51 | struct sharp_zclient *node; | |
52 | ||
53 | node = XCALLOC(MTYPE_ZC, sizeof(struct sharp_zclient)); | |
54 | ||
55 | node->client = client; | |
56 | ||
57 | node->next = sharp_clients_head; | |
58 | if (sharp_clients_head) | |
59 | sharp_clients_head->prev = node; | |
60 | sharp_clients_head = node; | |
61 | } | |
62 | ||
63 | /* Interface addition message from zebra. */ | |
ef7bd2a3 | 64 | static int sharp_ifp_create(struct interface *ifp) |
8a71d93d | 65 | { |
8a71d93d DS |
66 | return 0; |
67 | } | |
68 | ||
3c3c3252 | 69 | static int sharp_ifp_destroy(struct interface *ifp) |
8a71d93d | 70 | { |
8a71d93d DS |
71 | return 0; |
72 | } | |
73 | ||
121f9dee | 74 | static int interface_address_add(ZAPI_CALLBACK_ARGS) |
8a71d93d | 75 | { |
121f9dee | 76 | zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); |
8a71d93d DS |
77 | |
78 | return 0; | |
79 | } | |
80 | ||
121f9dee | 81 | static int interface_address_delete(ZAPI_CALLBACK_ARGS) |
8a71d93d DS |
82 | { |
83 | struct connected *c; | |
84 | ||
121f9dee | 85 | c = zebra_interface_address_read(cmd, zclient->ibuf, vrf_id); |
8a71d93d DS |
86 | |
87 | if (!c) | |
88 | return 0; | |
89 | ||
721c0857 | 90 | connected_free(&c); |
8a71d93d DS |
91 | return 0; |
92 | } | |
93 | ||
ddbf3e60 | 94 | static int sharp_ifp_up(struct interface *ifp) |
8a71d93d | 95 | { |
8a71d93d DS |
96 | return 0; |
97 | } | |
98 | ||
b0b69e59 | 99 | static int sharp_ifp_down(struct interface *ifp) |
8a71d93d | 100 | { |
8a71d93d DS |
101 | return 0; |
102 | } | |
103 | ||
faa75dfa MS |
104 | int sharp_install_lsps_helper(bool install_p, bool update_p, |
105 | const struct prefix *p, uint8_t type, | |
106 | int instance, uint32_t in_label, | |
665edffd MS |
107 | const struct nexthop_group *nhg, |
108 | const struct nexthop_group *backup_nhg) | |
c9e5adba MS |
109 | { |
110 | struct zapi_labels zl = {}; | |
111 | struct zapi_nexthop *znh; | |
112 | const struct nexthop *nh; | |
faa75dfa | 113 | int i, cmd, ret; |
c9e5adba MS |
114 | |
115 | zl.type = ZEBRA_LSP_SHARP; | |
116 | zl.local_label = in_label; | |
117 | ||
118 | if (p) { | |
119 | SET_FLAG(zl.message, ZAPI_LABELS_FTN); | |
120 | prefix_copy(&zl.route.prefix, p); | |
121 | zl.route.type = type; | |
122 | zl.route.instance = instance; | |
123 | } | |
124 | ||
665edffd | 125 | /* List of nexthops is optional for delete */ |
c9e5adba | 126 | i = 0; |
665edffd MS |
127 | if (nhg) { |
128 | for (ALL_NEXTHOPS_PTR(nhg, nh)) { | |
129 | znh = &zl.nexthops[i]; | |
130 | ||
131 | /* Must have labels to be useful */ | |
132 | if (nh->nh_label == NULL || | |
133 | nh->nh_label->num_labels == 0) | |
134 | continue; | |
135 | ||
136 | if (nh->type == NEXTHOP_TYPE_IFINDEX || | |
137 | nh->type == NEXTHOP_TYPE_BLACKHOLE) | |
138 | /* Hmm - can't really deal with these types */ | |
139 | continue; | |
140 | ||
141 | ret = zapi_nexthop_from_nexthop(znh, nh); | |
142 | if (ret < 0) | |
143 | return -1; | |
144 | ||
145 | i++; | |
474aebd9 MS |
146 | if (i >= MULTIPATH_NUM) |
147 | break; | |
665edffd MS |
148 | } |
149 | } | |
c9e5adba | 150 | |
665edffd MS |
151 | /* Whoops - no nexthops isn't very useful for install */ |
152 | if (i == 0 && install_p) | |
153 | return -1; | |
c9e5adba | 154 | |
665edffd | 155 | zl.nexthop_num = i; |
c9e5adba | 156 | |
665edffd MS |
157 | /* Add optional backup nexthop info. Since these are used by index, |
158 | * we can't just skip over an invalid backup nexthop: we will | |
159 | * invalidate the entire operation. | |
160 | */ | |
161 | if (backup_nhg != NULL) { | |
162 | i = 0; | |
163 | for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { | |
164 | znh = &zl.backup_nexthops[i]; | |
c9e5adba | 165 | |
665edffd MS |
166 | /* Must have labels to be useful */ |
167 | if (nh->nh_label == NULL || | |
168 | nh->nh_label->num_labels == 0) | |
169 | return -1; | |
c9e5adba | 170 | |
665edffd MS |
171 | if (nh->type == NEXTHOP_TYPE_IFINDEX || |
172 | nh->type == NEXTHOP_TYPE_BLACKHOLE) | |
173 | /* Hmm - can't really deal with these types */ | |
174 | return -1; | |
c9e5adba | 175 | |
665edffd MS |
176 | ret = zapi_nexthop_from_nexthop(znh, nh); |
177 | if (ret < 0) | |
178 | return -1; | |
179 | ||
180 | i++; | |
474aebd9 MS |
181 | if (i >= MULTIPATH_NUM) |
182 | break; | |
665edffd MS |
183 | } |
184 | ||
185 | if (i > 0) | |
186 | SET_FLAG(zl.message, ZAPI_LABELS_HAS_BACKUPS); | |
187 | ||
188 | zl.backup_nexthop_num = i; | |
189 | } | |
c9e5adba | 190 | |
faa75dfa MS |
191 | |
192 | if (install_p) { | |
193 | if (update_p) | |
194 | cmd = ZEBRA_MPLS_LABELS_REPLACE; | |
195 | else | |
196 | cmd = ZEBRA_MPLS_LABELS_ADD; | |
197 | } else { | |
198 | cmd = ZEBRA_MPLS_LABELS_DELETE; | |
199 | } | |
200 | ||
7cfdb485 DS |
201 | if (zebra_send_mpls_labels(zclient, cmd, &zl) == ZCLIENT_SEND_FAILURE) |
202 | return -1; | |
c9e5adba | 203 | |
7cfdb485 | 204 | return 0; |
c9e5adba MS |
205 | } |
206 | ||
07414912 DS |
207 | enum where_to_restart { |
208 | SHARP_INSTALL_ROUTES_RESTART, | |
209 | SHARP_DELETE_ROUTES_RESTART, | |
210 | }; | |
211 | ||
212 | struct buffer_delay { | |
213 | struct prefix p; | |
214 | uint32_t count; | |
215 | uint32_t routes; | |
216 | vrf_id_t vrf_id; | |
217 | uint8_t instance; | |
218 | uint32_t nhgid; | |
c27b47d7 | 219 | uint32_t flags; |
07414912 DS |
220 | const struct nexthop_group *nhg; |
221 | const struct nexthop_group *backup_nhg; | |
222 | enum where_to_restart restart; | |
cfa2a35d | 223 | char *opaque; |
07414912 DS |
224 | } wb; |
225 | ||
241e5df1 DS |
226 | /* |
227 | * route_add - Encodes a route to zebra | |
228 | * | |
229 | * This function returns true when the route was buffered | |
230 | * by the underlying stream system | |
231 | */ | |
232 | static bool route_add(const struct prefix *p, vrf_id_t vrf_id, uint8_t instance, | |
233 | uint32_t nhgid, const struct nexthop_group *nhg, | |
c27b47d7 HS |
234 | const struct nexthop_group *backup_nhg, uint32_t flags, |
235 | char *opaque) | |
241e5df1 DS |
236 | { |
237 | struct zapi_route api; | |
238 | struct zapi_nexthop *api_nh; | |
239 | struct nexthop *nh; | |
240 | int i = 0; | |
241 | ||
242 | memset(&api, 0, sizeof(api)); | |
243 | api.vrf_id = vrf_id; | |
244 | api.type = ZEBRA_ROUTE_SHARP; | |
245 | api.instance = instance; | |
246 | api.safi = SAFI_UNICAST; | |
247 | memcpy(&api.prefix, p, sizeof(*p)); | |
248 | ||
c27b47d7 | 249 | api.flags = flags; |
241e5df1 DS |
250 | SET_FLAG(api.flags, ZEBRA_FLAG_ALLOW_RECURSION); |
251 | SET_FLAG(api.message, ZAPI_MESSAGE_NEXTHOP); | |
252 | ||
253 | /* Only send via ID if nhgroup has been successfully installed */ | |
254 | if (nhgid && sharp_nhgroup_id_is_installed(nhgid)) { | |
255 | SET_FLAG(api.message, ZAPI_MESSAGE_NHG); | |
256 | api.nhgid = nhgid; | |
257 | } else { | |
258 | for (ALL_NEXTHOPS_PTR(nhg, nh)) { | |
90aaed16 | 259 | /* Check if we set a VNI label */ |
5b59f4a1 SW |
260 | if (nh->nh_label && |
261 | (nh->nh_label_type == ZEBRA_LSP_EVPN)) | |
90aaed16 SW |
262 | SET_FLAG(api.flags, ZEBRA_FLAG_EVPN_ROUTE); |
263 | ||
241e5df1 DS |
264 | api_nh = &api.nexthops[i]; |
265 | ||
266 | zapi_nexthop_from_nexthop(api_nh, nh); | |
267 | ||
268 | i++; | |
269 | } | |
270 | api.nexthop_num = i; | |
271 | } | |
272 | ||
273 | /* Include backup nexthops, if present */ | |
274 | if (backup_nhg && backup_nhg->nexthop) { | |
275 | SET_FLAG(api.message, ZAPI_MESSAGE_BACKUP_NEXTHOPS); | |
276 | ||
277 | i = 0; | |
278 | for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { | |
279 | api_nh = &api.backup_nexthops[i]; | |
280 | ||
281 | zapi_backup_nexthop_from_nexthop(api_nh, nh); | |
282 | ||
283 | i++; | |
284 | } | |
285 | ||
286 | api.backup_nexthop_num = i; | |
287 | } | |
288 | ||
cfa2a35d DS |
289 | if (strlen(opaque)) { |
290 | SET_FLAG(api.message, ZAPI_MESSAGE_OPAQUE); | |
291 | api.opaque.length = strlen(opaque) + 1; | |
292 | assert(api.opaque.length <= ZAPI_MESSAGE_OPAQUE_LENGTH); | |
293 | memcpy(api.opaque.data, opaque, api.opaque.length); | |
294 | } | |
295 | ||
04bc334e SY |
296 | if (zclient_route_send(ZEBRA_ROUTE_ADD, zclient, &api) == |
297 | ZCLIENT_SEND_BUFFERED) | |
241e5df1 DS |
298 | return true; |
299 | else | |
300 | return false; | |
301 | } | |
302 | ||
303 | /* | |
304 | * route_delete - Encodes a route for deletion to zebra | |
305 | * | |
306 | * This function returns true when the route sent was | |
307 | * buffered by the underlying stream system. | |
308 | */ | |
309 | static bool route_delete(struct prefix *p, vrf_id_t vrf_id, uint8_t instance) | |
310 | { | |
311 | struct zapi_route api; | |
312 | ||
313 | memset(&api, 0, sizeof(api)); | |
314 | api.vrf_id = vrf_id; | |
315 | api.type = ZEBRA_ROUTE_SHARP; | |
316 | api.safi = SAFI_UNICAST; | |
317 | api.instance = instance; | |
318 | memcpy(&api.prefix, p, sizeof(*p)); | |
319 | ||
04bc334e SY |
320 | if (zclient_route_send(ZEBRA_ROUTE_DELETE, zclient, &api) == |
321 | ZCLIENT_SEND_BUFFERED) | |
241e5df1 DS |
322 | return true; |
323 | else | |
324 | return false; | |
325 | } | |
326 | ||
07414912 DS |
327 | static void sharp_install_routes_restart(struct prefix *p, uint32_t count, |
328 | vrf_id_t vrf_id, uint8_t instance, | |
329 | uint32_t nhgid, | |
330 | const struct nexthop_group *nhg, | |
331 | const struct nexthop_group *backup_nhg, | |
4df9d859 HS |
332 | uint32_t routes, uint32_t flags, |
333 | char *opaque) | |
6b98d34f DS |
334 | { |
335 | uint32_t temp, i; | |
dbc1bf46 | 336 | bool v4 = false; |
6b98d34f | 337 | |
dbc1bf46 DS |
338 | if (p->family == AF_INET) { |
339 | v4 = true; | |
340 | temp = ntohl(p->u.prefix4.s_addr); | |
341 | } else | |
342 | temp = ntohl(p->u.val32[3]); | |
343 | ||
07414912 DS |
344 | for (i = count; i < routes; i++) { |
345 | bool buffered = route_add(p, vrf_id, (uint8_t)instance, nhgid, | |
c27b47d7 | 346 | nhg, backup_nhg, flags, opaque); |
dbc1bf46 DS |
347 | if (v4) |
348 | p->u.prefix4.s_addr = htonl(++temp); | |
349 | else | |
350 | p->u.val32[3] = htonl(++temp); | |
07414912 DS |
351 | |
352 | if (buffered) { | |
353 | wb.p = *p; | |
04bc334e | 354 | wb.count = i + 1; |
07414912 DS |
355 | wb.routes = routes; |
356 | wb.vrf_id = vrf_id; | |
357 | wb.instance = instance; | |
358 | wb.nhgid = nhgid; | |
359 | wb.nhg = nhg; | |
c27b47d7 | 360 | wb.flags = flags; |
07414912 | 361 | wb.backup_nhg = backup_nhg; |
cfa2a35d | 362 | wb.opaque = opaque; |
07414912 DS |
363 | wb.restart = SHARP_INSTALL_ROUTES_RESTART; |
364 | ||
365 | return; | |
366 | } | |
6b98d34f DS |
367 | } |
368 | } | |
369 | ||
07414912 DS |
370 | void sharp_install_routes_helper(struct prefix *p, vrf_id_t vrf_id, |
371 | uint8_t instance, uint32_t nhgid, | |
372 | const struct nexthop_group *nhg, | |
373 | const struct nexthop_group *backup_nhg, | |
c27b47d7 | 374 | uint32_t routes, uint32_t flags, char *opaque) |
07414912 DS |
375 | { |
376 | zlog_debug("Inserting %u routes", routes); | |
377 | ||
378 | /* Only use backup route/nexthops if present */ | |
379 | if (backup_nhg && (backup_nhg->nexthop == NULL)) | |
380 | backup_nhg = NULL; | |
381 | ||
382 | monotime(&sg.r.t_start); | |
383 | sharp_install_routes_restart(p, 0, vrf_id, instance, nhgid, nhg, | |
c27b47d7 | 384 | backup_nhg, routes, flags, opaque); |
07414912 DS |
385 | } |
386 | ||
387 | static void sharp_remove_routes_restart(struct prefix *p, uint32_t count, | |
388 | vrf_id_t vrf_id, uint8_t instance, | |
389 | uint32_t routes) | |
6b98d34f DS |
390 | { |
391 | uint32_t temp, i; | |
dbc1bf46 | 392 | bool v4 = false; |
6b98d34f | 393 | |
dbc1bf46 DS |
394 | if (p->family == AF_INET) { |
395 | v4 = true; | |
396 | temp = ntohl(p->u.prefix4.s_addr); | |
397 | } else | |
398 | temp = ntohl(p->u.val32[3]); | |
399 | ||
07414912 DS |
400 | for (i = count; i < routes; i++) { |
401 | bool buffered = route_delete(p, vrf_id, (uint8_t)instance); | |
402 | ||
dbc1bf46 DS |
403 | if (v4) |
404 | p->u.prefix4.s_addr = htonl(++temp); | |
405 | else | |
406 | p->u.val32[3] = htonl(++temp); | |
07414912 DS |
407 | |
408 | if (buffered) { | |
409 | wb.p = *p; | |
410 | wb.count = i + 1; | |
411 | wb.vrf_id = vrf_id; | |
412 | wb.instance = instance; | |
413 | wb.routes = routes; | |
414 | wb.restart = SHARP_DELETE_ROUTES_RESTART; | |
415 | ||
416 | return; | |
417 | } | |
6b98d34f | 418 | } |
07414912 DS |
419 | } |
420 | ||
421 | void sharp_remove_routes_helper(struct prefix *p, vrf_id_t vrf_id, | |
422 | uint8_t instance, uint32_t routes) | |
423 | { | |
424 | zlog_debug("Removing %u routes", routes); | |
425 | ||
426 | monotime(&sg.r.t_start); | |
427 | ||
428 | sharp_remove_routes_restart(p, 0, vrf_id, instance, routes); | |
6b98d34f DS |
429 | } |
430 | ||
b939f6ff | 431 | static void handle_repeated(bool installed) |
6b98d34f | 432 | { |
547dc642 DS |
433 | struct prefix p = sg.r.orig_prefix; |
434 | sg.r.repeat--; | |
6b98d34f | 435 | |
547dc642 | 436 | if (sg.r.repeat <= 0) |
6b98d34f DS |
437 | return; |
438 | ||
439 | if (installed) { | |
547dc642 | 440 | sg.r.removed_routes = 0; |
04bc334e SY |
441 | sharp_remove_routes_helper(&p, sg.r.vrf_id, sg.r.inst, |
442 | sg.r.total_routes); | |
6b98d34f DS |
443 | } |
444 | ||
f54f37c1 | 445 | if (!installed) { |
547dc642 | 446 | sg.r.installed_routes = 0; |
04bc334e SY |
447 | sharp_install_routes_helper( |
448 | &p, sg.r.vrf_id, sg.r.inst, sg.r.nhgid, | |
449 | &sg.r.nhop_group, &sg.r.backup_nhop_group, | |
450 | sg.r.total_routes, sg.r.flags, sg.r.opaque); | |
6b98d34f DS |
451 | } |
452 | } | |
453 | ||
07414912 DS |
454 | static void sharp_zclient_buffer_ready(void) |
455 | { | |
456 | switch (wb.restart) { | |
457 | case SHARP_INSTALL_ROUTES_RESTART: | |
cfa2a35d DS |
458 | sharp_install_routes_restart( |
459 | &wb.p, wb.count, wb.vrf_id, wb.instance, wb.nhgid, | |
04bc334e | 460 | wb.nhg, wb.backup_nhg, wb.routes, wb.flags, wb.opaque); |
07414912 | 461 | return; |
07414912 DS |
462 | case SHARP_DELETE_ROUTES_RESTART: |
463 | sharp_remove_routes_restart(&wb.p, wb.count, wb.vrf_id, | |
464 | wb.instance, wb.routes); | |
465 | return; | |
07414912 DS |
466 | } |
467 | } | |
468 | ||
121f9dee | 469 | static int route_notify_owner(ZAPI_CALLBACK_ARGS) |
8a71d93d | 470 | { |
25c84d86 | 471 | struct timeval r; |
8a71d93d DS |
472 | struct prefix p; |
473 | enum zapi_route_notify_owner note; | |
28610f7e | 474 | uint32_t table; |
8a71d93d | 475 | |
04bc334e SY |
476 | if (!zapi_route_notify_decode(zclient->ibuf, &p, &table, ¬e, NULL, |
477 | NULL)) | |
8a71d93d DS |
478 | return -1; |
479 | ||
5e54c602 DS |
480 | switch (note) { |
481 | case ZAPI_ROUTE_INSTALLED: | |
547dc642 DS |
482 | sg.r.installed_routes++; |
483 | if (sg.r.total_routes == sg.r.installed_routes) { | |
484 | monotime(&sg.r.t_end); | |
485 | timersub(&sg.r.t_end, &sg.r.t_start, &r); | |
051a0be4 DL |
486 | zlog_debug("Installed All Items %jd.%ld", |
487 | (intmax_t)r.tv_sec, (long)r.tv_usec); | |
6b98d34f DS |
488 | handle_repeated(true); |
489 | } | |
5e54c602 DS |
490 | break; |
491 | case ZAPI_ROUTE_FAIL_INSTALL: | |
492 | zlog_debug("Failed install of route"); | |
493 | break; | |
494 | case ZAPI_ROUTE_BETTER_ADMIN_WON: | |
495 | zlog_debug("Better Admin Distance won over us"); | |
496 | break; | |
497 | case ZAPI_ROUTE_REMOVED: | |
547dc642 DS |
498 | sg.r.removed_routes++; |
499 | if (sg.r.total_routes == sg.r.removed_routes) { | |
500 | monotime(&sg.r.t_end); | |
501 | timersub(&sg.r.t_end, &sg.r.t_start, &r); | |
051a0be4 DL |
502 | zlog_debug("Removed all Items %jd.%ld", |
503 | (intmax_t)r.tv_sec, (long)r.tv_usec); | |
6b98d34f DS |
504 | handle_repeated(false); |
505 | } | |
5e54c602 DS |
506 | break; |
507 | case ZAPI_ROUTE_REMOVE_FAIL: | |
508 | zlog_debug("Route removal Failure"); | |
509 | break; | |
510 | } | |
8a71d93d DS |
511 | return 0; |
512 | } | |
513 | ||
514 | static void zebra_connected(struct zclient *zclient) | |
515 | { | |
516 | zclient_send_reg_requests(zclient, VRF_DEFAULT); | |
67a9eda9 DS |
517 | |
518 | /* | |
519 | * Do not actually turn this on yet | |
520 | * This is just the start of the infrastructure needed here | |
521 | * This can be fixed at a later time. | |
522 | * | |
523 | * zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, | |
524 | * ZEBRA_ROUTE_ALL, 0, VRF_DEFAULT); | |
525 | */ | |
8a71d93d DS |
526 | } |
527 | ||
7d061b3c | 528 | void vrf_label_add(vrf_id_t vrf_id, afi_t afi, mpls_label_t label) |
ab18a495 | 529 | { |
7d061b3c | 530 | zclient_send_vrf_label(zclient, vrf_id, afi, label, ZEBRA_LSP_SHARP); |
ab18a495 DS |
531 | } |
532 | ||
21735352 SW |
533 | void nhg_add(uint32_t id, const struct nexthop_group *nhg, |
534 | const struct nexthop_group *backup_nhg) | |
569e87c0 | 535 | { |
c6ce9334 | 536 | struct zapi_nhg api_nhg = {}; |
569e87c0 | 537 | struct zapi_nexthop *api_nh; |
569e87c0 | 538 | struct nexthop *nh; |
5a9c0931 | 539 | bool is_valid = true; |
569e87c0 | 540 | |
c6ce9334 | 541 | api_nhg.id = id; |
ca2b3467 DS |
542 | |
543 | api_nhg.resilience = nhg->nhgr; | |
544 | ||
569e87c0 | 545 | for (ALL_NEXTHOPS_PTR(nhg, nh)) { |
c6ce9334 | 546 | if (api_nhg.nexthop_num >= MULTIPATH_NUM) { |
54a701e4 SW |
547 | zlog_warn( |
548 | "%s: number of nexthops greater than max multipath size, truncating", | |
549 | __func__); | |
550 | break; | |
551 | } | |
552 | ||
5a9c0931 MS |
553 | /* Unresolved nexthops will lead to failure - only send |
554 | * nexthops that zebra will consider valid. | |
555 | */ | |
556 | if (nh->ifindex == 0) | |
557 | continue; | |
558 | ||
c6ce9334 | 559 | api_nh = &api_nhg.nexthops[api_nhg.nexthop_num]; |
569e87c0 DS |
560 | |
561 | zapi_nexthop_from_nexthop(api_nh, nh); | |
c6ce9334 | 562 | api_nhg.nexthop_num++; |
569e87c0 DS |
563 | } |
564 | ||
5a9c0931 | 565 | if (api_nhg.nexthop_num == 0) { |
04bc334e SY |
566 | zlog_debug("%s: nhg %u not sent: no valid nexthops", __func__, |
567 | id); | |
5a9c0931 MS |
568 | is_valid = false; |
569 | goto done; | |
570 | } | |
571 | ||
21735352 SW |
572 | if (backup_nhg) { |
573 | for (ALL_NEXTHOPS_PTR(backup_nhg, nh)) { | |
574 | if (api_nhg.backup_nexthop_num >= MULTIPATH_NUM) { | |
575 | zlog_warn( | |
576 | "%s: number of backup nexthops greater than max multipath size, truncating", | |
577 | __func__); | |
578 | break; | |
579 | } | |
5a9c0931 MS |
580 | |
581 | /* Unresolved nexthop: will be rejected by zebra. | |
582 | * That causes a problem, since the primary nexthops | |
583 | * rely on array indexing into the backup nexthops. If | |
584 | * that array isn't valid, the backup indexes won't be | |
585 | * valid. | |
586 | */ | |
587 | if (nh->ifindex == 0) { | |
588 | zlog_debug("%s: nhg %u: invalid backup nexthop", | |
589 | __func__, id); | |
590 | is_valid = false; | |
591 | break; | |
592 | } | |
593 | ||
21735352 SW |
594 | api_nh = &api_nhg.backup_nexthops |
595 | [api_nhg.backup_nexthop_num]; | |
596 | ||
597 | zapi_backup_nexthop_from_nexthop(api_nh, nh); | |
598 | api_nhg.backup_nexthop_num++; | |
599 | } | |
600 | } | |
601 | ||
5a9c0931 MS |
602 | done: |
603 | if (is_valid) | |
604 | zclient_nhg_send(zclient, ZEBRA_NHG_ADD, &api_nhg); | |
569e87c0 DS |
605 | } |
606 | ||
607 | void nhg_del(uint32_t id) | |
608 | { | |
c6ce9334 SW |
609 | struct zapi_nhg api_nhg = {}; |
610 | ||
611 | api_nhg.id = id; | |
612 | ||
613 | zclient_nhg_send(zclient, ZEBRA_NHG_DEL, &api_nhg); | |
569e87c0 DS |
614 | } |
615 | ||
91529dc8 | 616 | void sharp_zebra_nexthop_watch(struct prefix *p, vrf_id_t vrf_id, bool import, |
b47dc61a | 617 | bool watch, bool connected) |
0ae8130d | 618 | { |
b47dc61a | 619 | int command; |
0ae8130d | 620 | |
3d174ce0 | 621 | command = ZEBRA_NEXTHOP_REGISTER; |
b47dc61a | 622 | |
3d174ce0 DS |
623 | if (!watch) |
624 | command = ZEBRA_NEXTHOP_UNREGISTER; | |
0ae8130d | 625 | |
eb3c9d97 | 626 | if (zclient_send_rnh(zclient, command, p, SAFI_UNICAST, connected, |
04bc334e | 627 | false, vrf_id) == ZCLIENT_SEND_FAILURE) |
15569c58 | 628 | zlog_warn("%s: Failure to send nexthop to zebra", __func__); |
0ae8130d DS |
629 | } |
630 | ||
67a9eda9 | 631 | static int sharp_debug_nexthops(struct zapi_route *api) |
0ae8130d | 632 | { |
0ae8130d DS |
633 | int i; |
634 | ||
8b85b2cb | 635 | if (api->nexthop_num == 0) { |
04bc334e | 636 | zlog_debug(" Not installed"); |
8b85b2cb MS |
637 | return 0; |
638 | } | |
639 | ||
67a9eda9 DS |
640 | for (i = 0; i < api->nexthop_num; i++) { |
641 | struct zapi_nexthop *znh = &api->nexthops[i]; | |
0ae8130d DS |
642 | |
643 | switch (znh->type) { | |
644 | case NEXTHOP_TYPE_IPV4_IFINDEX: | |
645 | case NEXTHOP_TYPE_IPV4: | |
646 | zlog_debug( | |
6dd43a35 DS |
647 | " Nexthop %pI4, type: %d, ifindex: %d, vrf: %d, label_num: %d", |
648 | &znh->gate.ipv4.s_addr, znh->type, znh->ifindex, | |
649 | znh->vrf_id, znh->label_num); | |
0ae8130d DS |
650 | break; |
651 | case NEXTHOP_TYPE_IPV6_IFINDEX: | |
652 | case NEXTHOP_TYPE_IPV6: | |
653 | zlog_debug( | |
6dd43a35 DS |
654 | " Nexthop %pI6, type: %d, ifindex: %d, vrf: %d, label_num: %d", |
655 | &znh->gate.ipv6, znh->type, znh->ifindex, | |
656 | znh->vrf_id, znh->label_num); | |
0ae8130d DS |
657 | break; |
658 | case NEXTHOP_TYPE_IFINDEX: | |
d6951e5e | 659 | zlog_debug(" Nexthop IFINDEX: %d, ifindex: %d", |
0ae8130d DS |
660 | znh->type, znh->ifindex); |
661 | break; | |
662 | case NEXTHOP_TYPE_BLACKHOLE: | |
d6951e5e | 663 | zlog_debug(" Nexthop blackhole"); |
0ae8130d DS |
664 | break; |
665 | } | |
666 | } | |
67a9eda9 DS |
667 | |
668 | return i; | |
669 | } | |
670 | static int sharp_nexthop_update(ZAPI_CALLBACK_ARGS) | |
671 | { | |
672 | struct sharp_nh_tracker *nht; | |
673 | struct zapi_route nhr; | |
06e4e901 | 674 | struct prefix matched; |
67a9eda9 | 675 | |
06e4e901 | 676 | if (!zapi_nexthop_update_decode(zclient->ibuf, &matched, &nhr)) { |
6c83dded | 677 | zlog_err("%s: Decode of update failed", __func__); |
67a9eda9 DS |
678 | return 0; |
679 | } | |
680 | ||
06e4e901 DS |
681 | zlog_debug("Received update for %pFX actual match: %pFX metric: %u", |
682 | &matched, &nhr.prefix, nhr.metric); | |
67a9eda9 | 683 | |
06e4e901 | 684 | nht = sharp_nh_tracker_get(&matched); |
67a9eda9 DS |
685 | nht->nhop_num = nhr.nexthop_num; |
686 | nht->updates++; | |
687 | ||
688 | sharp_debug_nexthops(&nhr); | |
689 | ||
690 | return 0; | |
691 | } | |
692 | ||
693 | static int sharp_redistribute_route(ZAPI_CALLBACK_ARGS) | |
694 | { | |
695 | struct zapi_route api; | |
696 | ||
697 | if (zapi_route_decode(zclient->ibuf, &api) < 0) | |
15569c58 | 698 | zlog_warn("%s: Decode of redistribute failed: %d", __func__, |
67a9eda9 DS |
699 | ZEBRA_REDISTRIBUTE_ROUTE_ADD); |
700 | ||
04bc334e SY |
701 | zlog_debug("%s: %pFX (%s)", zserv_command_string(cmd), &api.prefix, |
702 | zebra_route_string(api.type)); | |
67a9eda9 DS |
703 | |
704 | sharp_debug_nexthops(&api); | |
705 | ||
0ae8130d DS |
706 | return 0; |
707 | } | |
708 | ||
921af54d DS |
709 | void sharp_redistribute_vrf(struct vrf *vrf, int type) |
710 | { | |
711 | zebra_redistribute_send(ZEBRA_REDISTRIBUTE_ADD, zclient, AFI_IP, type, | |
712 | 0, vrf->vrf_id); | |
713 | } | |
714 | ||
a243d1db DL |
715 | static zclient_handler *const sharp_opaque_handlers[] = { |
716 | [ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler, | |
717 | }; | |
718 | ||
2be4d61a MS |
719 | /* Add a zclient with a specified session id, for testing. */ |
720 | int sharp_zclient_create(uint32_t session_id) | |
721 | { | |
722 | struct zclient *client; | |
723 | struct sharp_zclient *node; | |
724 | ||
725 | /* Check for duplicates */ | |
726 | for (node = sharp_clients_head; node != NULL; node = node->next) { | |
727 | if (node->client->session_id == session_id) | |
728 | return -1; | |
729 | } | |
730 | ||
a243d1db DL |
731 | client = zclient_new(master, &zclient_options_default, |
732 | sharp_opaque_handlers, | |
733 | array_size(sharp_opaque_handlers)); | |
2be4d61a MS |
734 | client->sock = -1; |
735 | client->session_id = session_id; | |
736 | ||
737 | zclient_init(client, ZEBRA_ROUTE_SHARP, 0, &sharp_privs); | |
738 | ||
2be4d61a MS |
739 | /* Enqueue on the list of test clients */ |
740 | add_zclient(client); | |
741 | ||
742 | return 0; | |
743 | } | |
744 | ||
745 | /* Delete one of the extra test zclients */ | |
746 | int sharp_zclient_delete(uint32_t session_id) | |
747 | { | |
748 | struct sharp_zclient *node; | |
749 | ||
750 | /* Search for session */ | |
751 | for (node = sharp_clients_head; node != NULL; node = node->next) { | |
752 | if (node->client->session_id == session_id) { | |
753 | /* Dequeue from list */ | |
754 | if (node->next) | |
755 | node->next->prev = node->prev; | |
756 | if (node->prev) | |
757 | node->prev->next = node->next; | |
758 | if (node == sharp_clients_head) | |
759 | sharp_clients_head = node->next; | |
760 | ||
761 | /* Clean up zclient */ | |
762 | zclient_stop(node->client); | |
763 | zclient_free(node->client); | |
764 | ||
765 | /* Free memory */ | |
766 | XFREE(MTYPE_ZC, node); | |
767 | break; | |
768 | } | |
769 | } | |
770 | ||
771 | return 0; | |
772 | } | |
773 | ||
04bc334e SY |
774 | static const char *const type2txt[] = {"Generic", "Vertex", "Edge", "Subnet"}; |
775 | static const char *const status2txt[] = {"Unknown", "New", "Update", | |
776 | "Delete", "Sync", "Orphan"}; | |
7f5ac773 MS |
777 | /* Handler for opaque messages */ |
778 | static int sharp_opaque_handler(ZAPI_CALLBACK_ARGS) | |
779 | { | |
7f5ac773 | 780 | struct stream *s; |
387831ff | 781 | struct zapi_opaque_msg info; |
1888e243 | 782 | struct ls_element *lse; |
7f5ac773 MS |
783 | |
784 | s = zclient->ibuf; | |
785 | ||
387831ff MS |
786 | if (zclient_opaque_decode(s, &info) != 0) |
787 | return -1; | |
7f5ac773 | 788 | |
2be4d61a | 789 | zlog_debug("%s: [%u] received opaque type %u", __func__, |
c8b27f2a | 790 | zclient->session_id, info.type); |
7f5ac773 | 791 | |
1888e243 | 792 | if (info.type == LINK_STATE_UPDATE) { |
b10e5cf2 | 793 | lse = ls_stream2ted(sg.ted, s, true); |
62f79ac1 | 794 | if (lse) { |
1888e243 OD |
795 | zlog_debug(" |- Got %s %s from Link State Database", |
796 | status2txt[lse->status], | |
797 | type2txt[lse->type]); | |
62f79ac1 | 798 | lse->status = SYNC; |
04bc334e | 799 | } else |
1888e243 OD |
800 | zlog_debug( |
801 | "%s: Error to convert Stream into Link State", | |
802 | __func__); | |
803 | } | |
804 | ||
7f5ac773 MS |
805 | return 0; |
806 | } | |
807 | ||
2ac6c90d MS |
808 | /* |
809 | * Send OPAQUE messages, using subtype 'type'. | |
810 | */ | |
c8b27f2a MS |
811 | void sharp_opaque_send(uint32_t type, uint32_t proto, uint32_t instance, |
812 | uint32_t session_id, uint32_t count) | |
2ac6c90d | 813 | { |
7f5ac773 | 814 | uint8_t buf[32]; |
2ac6c90d MS |
815 | int ret; |
816 | uint32_t i; | |
817 | ||
818 | /* Prepare a small payload */ | |
819 | for (i = 0; i < sizeof(buf); i++) { | |
820 | if (type < 255) | |
821 | buf[i] = type; | |
822 | else | |
823 | buf[i] = 255; | |
824 | } | |
825 | ||
c8b27f2a | 826 | /* Send some messages - broadcast and unicast are supported */ |
2ac6c90d | 827 | for (i = 0; i < count; i++) { |
c8b27f2a MS |
828 | if (proto == 0) |
829 | ret = zclient_send_opaque(zclient, type, buf, | |
830 | sizeof(buf)); | |
831 | else | |
832 | ret = zclient_send_opaque_unicast(zclient, type, proto, | |
833 | instance, session_id, | |
834 | buf, sizeof(buf)); | |
7cfdb485 | 835 | if (ret == ZCLIENT_SEND_FAILURE) { |
04bc334e SY |
836 | zlog_debug("%s: send_opaque() failed => %d", __func__, |
837 | ret); | |
2ac6c90d MS |
838 | break; |
839 | } | |
840 | } | |
2ac6c90d MS |
841 | } |
842 | ||
939b2339 MS |
843 | /* |
844 | * Send OPAQUE registration messages, using subtype 'type'. | |
845 | */ | |
846 | void sharp_opaque_reg_send(bool is_reg, uint32_t proto, uint32_t instance, | |
847 | uint32_t session_id, uint32_t type) | |
848 | { | |
849 | struct stream *s; | |
850 | ||
851 | s = zclient->obuf; | |
852 | stream_reset(s); | |
853 | ||
854 | if (is_reg) | |
855 | zclient_create_header(s, ZEBRA_OPAQUE_REGISTER, VRF_DEFAULT); | |
856 | else | |
857 | zclient_create_header(s, ZEBRA_OPAQUE_UNREGISTER, VRF_DEFAULT); | |
858 | ||
859 | /* Send sub-type */ | |
860 | stream_putl(s, type); | |
861 | ||
862 | /* Add zclient info */ | |
863 | stream_putc(s, proto); | |
864 | stream_putw(s, instance); | |
865 | stream_putl(s, session_id); | |
866 | ||
867 | /* Put length at the first point of the stream. */ | |
868 | stream_putw_at(s, 0, stream_get_endp(s)); | |
869 | ||
870 | (void)zclient_send_message(zclient); | |
939b2339 MS |
871 | } |
872 | ||
1888e243 OD |
873 | /* Link State registration */ |
874 | void sharp_zebra_register_te(void) | |
875 | { | |
876 | /* First register to received Link State Update messages */ | |
877 | zclient_register_opaque(zclient, LINK_STATE_UPDATE); | |
878 | ||
879 | /* Then, request initial TED with SYNC message */ | |
880 | ls_request_sync(zclient); | |
881 | } | |
882 | ||
da187b77 JU |
883 | void sharp_zebra_send_arp(const struct interface *ifp, const struct prefix *p) |
884 | { | |
885 | zclient_send_neigh_discovery_req(zclient, ifp, p); | |
886 | } | |
887 | ||
2053061b SW |
888 | static int nhg_notify_owner(ZAPI_CALLBACK_ARGS) |
889 | { | |
890 | enum zapi_nhg_notify_owner note; | |
891 | uint32_t id; | |
892 | ||
893 | if (!zapi_nhg_notify_decode(zclient->ibuf, &id, ¬e)) | |
894 | return -1; | |
895 | ||
896 | switch (note) { | |
897 | case ZAPI_NHG_INSTALLED: | |
898 | sharp_nhgroup_id_set_installed(id, true); | |
899 | zlog_debug("Installed nhg %u", id); | |
900 | break; | |
901 | case ZAPI_NHG_FAIL_INSTALL: | |
902 | zlog_debug("Failed install of nhg %u", id); | |
903 | break; | |
904 | case ZAPI_NHG_REMOVED: | |
905 | zlog_debug("Removed nhg %u", id); | |
906 | break; | |
907 | case ZAPI_NHG_REMOVE_FAIL: | |
908 | zlog_debug("Failed removal of nhg %u", id); | |
909 | break; | |
910 | } | |
911 | ||
912 | return 0; | |
913 | } | |
914 | ||
4df9d859 | 915 | int sharp_zebra_srv6_manager_get_locator_chunk(const char *locator_name) |
ade3eebc HS |
916 | { |
917 | return srv6_manager_get_locator_chunk(zclient, locator_name); | |
918 | } | |
919 | ||
920 | int sharp_zebra_srv6_manager_release_locator_chunk(const char *locator_name) | |
921 | { | |
922 | return srv6_manager_release_locator_chunk(zclient, locator_name); | |
923 | } | |
924 | ||
a243d1db | 925 | static int sharp_zebra_process_srv6_locator_chunk(ZAPI_CALLBACK_ARGS) |
ade3eebc HS |
926 | { |
927 | struct stream *s = NULL; | |
05485926 HS |
928 | struct srv6_locator_chunk s6c = {}; |
929 | struct listnode *node, *nnode; | |
930 | struct sharp_srv6_locator *loc; | |
ade3eebc HS |
931 | |
932 | s = zclient->ibuf; | |
05485926 | 933 | zapi_srv6_locator_chunk_decode(s, &s6c); |
ade3eebc | 934 | |
05485926 HS |
935 | for (ALL_LIST_ELEMENTS(sg.srv6_locators, node, nnode, loc)) { |
936 | struct prefix_ipv6 *chunk = NULL; | |
4df9d859 HS |
937 | struct listnode *chunk_node; |
938 | struct prefix_ipv6 *c; | |
939 | ||
05485926 HS |
940 | if (strcmp(loc->name, s6c.locator_name) != 0) { |
941 | zlog_err("%s: Locator name unmatch %s:%s", __func__, | |
942 | loc->name, s6c.locator_name); | |
ade3eebc | 943 | continue; |
05485926 | 944 | } |
ade3eebc | 945 | |
4df9d859 | 946 | for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, c)) |
05485926 | 947 | if (!prefix_cmp(c, &s6c.prefix)) |
a243d1db | 948 | return 0; |
05485926 HS |
949 | |
950 | chunk = prefix_ipv6_new(); | |
951 | *chunk = s6c.prefix; | |
ade3eebc | 952 | listnode_add(loc->chunks, chunk); |
a243d1db | 953 | return 0; |
ade3eebc | 954 | } |
ade3eebc HS |
955 | |
956 | zlog_err("%s: can't get locator_chunk!!", __func__); | |
a243d1db | 957 | return 0; |
f9a1140c SW |
958 | } |
959 | ||
960 | int sharp_zebra_send_interface_protodown(struct interface *ifp, bool down) | |
961 | { | |
962 | zlog_debug("Sending zebra to set %s protodown %s", ifp->name, | |
963 | down ? "on" : "off"); | |
964 | ||
965 | if (zclient_send_interface_protodown(zclient, ifp->vrf->vrf_id, ifp, | |
966 | down) == ZCLIENT_SEND_FAILURE) | |
967 | return -1; | |
968 | ||
969 | return 0; | |
ade3eebc HS |
970 | } |
971 | ||
04bc334e SY |
972 | int sharp_zebra_send_tc_filter_rate(struct interface *ifp, |
973 | const struct prefix *source, | |
974 | const struct prefix *destination, | |
975 | uint8_t ip_proto, uint16_t src_port, | |
976 | uint16_t dst_port, uint64_t rate) | |
977 | { | |
978 | #define SHARPD_TC_HANDLE 0x0001 | |
979 | struct stream *s; | |
980 | ||
981 | s = zclient->obuf; | |
982 | ||
983 | struct tc_qdisc q = {.ifindex = ifp->ifindex, .kind = TC_QDISC_HTB}; | |
984 | ||
985 | zapi_tc_qdisc_encode(ZEBRA_TC_QDISC_INSTALL, s, &q); | |
986 | if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE) | |
987 | return -1; | |
988 | ||
989 | struct tc_class c = {.ifindex = ifp->ifindex, | |
990 | .handle = SHARPD_TC_HANDLE & 0xffff, | |
991 | .kind = TC_QDISC_HTB, | |
992 | .u.htb.ceil = rate, | |
993 | .u.htb.rate = rate}; | |
994 | ||
995 | zapi_tc_class_encode(ZEBRA_TC_CLASS_ADD, s, &c); | |
996 | if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE) | |
997 | return -1; | |
998 | ||
999 | struct tc_filter f = {.ifindex = ifp->ifindex, | |
1000 | .handle = SHARPD_TC_HANDLE, | |
1001 | .priority = 0x1, | |
1002 | .kind = TC_FILTER_FLOWER, | |
1003 | .u.flower.filter_bm = 0}; | |
1004 | ||
1005 | #ifdef ETH_P_IP | |
1006 | f.protocol = ETH_P_IP; | |
1007 | #else | |
1008 | f.protocol = 0x0800; | |
1009 | #endif | |
1010 | ||
1011 | f.u.flower.filter_bm |= TC_FLOWER_IP_PROTOCOL; | |
1012 | f.u.flower.ip_proto = ip_proto; | |
1013 | f.u.flower.filter_bm |= TC_FLOWER_SRC_IP; | |
1014 | prefix_copy(&f.u.flower.src_ip, source); | |
1015 | f.u.flower.filter_bm |= TC_FLOWER_DST_IP; | |
1016 | prefix_copy(&f.u.flower.dst_ip, destination); | |
1017 | f.u.flower.filter_bm |= TC_FLOWER_SRC_PORT; | |
1018 | f.u.flower.src_port_min = f.u.flower.src_port_max = src_port; | |
1019 | f.u.flower.filter_bm |= TC_FLOWER_DST_PORT; | |
1020 | f.u.flower.dst_port_min = f.u.flower.dst_port_max = dst_port; | |
1021 | f.u.flower.classid = SHARPD_TC_HANDLE & 0xffff; | |
1022 | ||
1023 | zapi_tc_filter_encode(ZEBRA_TC_FILTER_ADD, s, &f); | |
1024 | if (zclient_send_message(zclient) == ZCLIENT_SEND_FAILURE) | |
1025 | return -1; | |
1026 | ||
1027 | return 0; | |
1028 | } | |
1029 | ||
a243d1db DL |
1030 | static zclient_handler *const sharp_handlers[] = { |
1031 | [ZEBRA_INTERFACE_ADDRESS_ADD] = interface_address_add, | |
1032 | [ZEBRA_INTERFACE_ADDRESS_DELETE] = interface_address_delete, | |
1033 | [ZEBRA_ROUTE_NOTIFY_OWNER] = route_notify_owner, | |
1034 | [ZEBRA_NEXTHOP_UPDATE] = sharp_nexthop_update, | |
1035 | [ZEBRA_NHG_NOTIFY_OWNER] = nhg_notify_owner, | |
1036 | [ZEBRA_REDISTRIBUTE_ROUTE_ADD] = sharp_redistribute_route, | |
1037 | [ZEBRA_REDISTRIBUTE_ROUTE_DEL] = sharp_redistribute_route, | |
1038 | [ZEBRA_OPAQUE_MESSAGE] = sharp_opaque_handler, | |
1039 | [ZEBRA_SRV6_MANAGER_GET_LOCATOR_CHUNK] = | |
1040 | sharp_zebra_process_srv6_locator_chunk, | |
1041 | }; | |
1042 | ||
8a71d93d DS |
1043 | void sharp_zebra_init(void) |
1044 | { | |
996c9314 | 1045 | struct zclient_options opt = {.receive_notify = true}; |
8a71d93d | 1046 | |
04bc334e SY |
1047 | if_zapi_callbacks(sharp_ifp_create, sharp_ifp_up, sharp_ifp_down, |
1048 | sharp_ifp_destroy); | |
138c5a74 | 1049 | |
a243d1db DL |
1050 | zclient = zclient_new(master, &opt, sharp_handlers, |
1051 | array_size(sharp_handlers)); | |
8a71d93d DS |
1052 | |
1053 | zclient_init(zclient, ZEBRA_ROUTE_SHARP, 0, &sharp_privs); | |
1054 | zclient->zebra_connected = zebra_connected; | |
07414912 | 1055 | zclient->zebra_buffer_write_ready = sharp_zclient_buffer_ready; |
8a71d93d | 1056 | } |