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