]> git.proxmox.com Git - mirror_frr.git/blame - mgmtd/mgmt_be_adapter.c
Merge pull request #13649 from donaldsharp/unlock_the_node_or_else
[mirror_frr.git] / mgmtd / mgmt_be_adapter.c
CommitLineData
7d65b7b7
CH
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * MGMTD Backend Client Connection Adapter
4 *
5 * Copyright (C) 2021 Vmware, Inc.
6 * Pushpasis Sarkar <spushpasis@vmware.com>
99564edc 7 * Copyright (c) 2023, LabN Consulting, L.L.C.
7d65b7b7
CH
8 */
9
10#include <zebra.h>
24a58196 11#include "frrevent.h"
7d65b7b7
CH
12#include "sockopt.h"
13#include "network.h"
14#include "libfrr.h"
f82370b4 15#include "mgmt_msg.h"
7d65b7b7
CH
16#include "mgmt_pb.h"
17#include "mgmtd/mgmt.h"
18#include "mgmtd/mgmt_memory.h"
19#include "mgmt_be_client.h"
20#include "mgmtd/mgmt_be_adapter.h"
21
cfa0facb 22#define MGMTD_BE_ADAPTER_DBG(fmt, ...) \
08e8019c
CH
23 DEBUGD(&mgmt_debug_be, "BE-ADAPTER: %s:" fmt, __func__, ##__VA_ARGS__)
24#define MGMTD_BE_ADAPTER_ERR(fmt, ...) \
25 zlog_err("BE-ADAPTER: %s: ERROR: " fmt, __func__, ##__VA_ARGS__)
7d65b7b7
CH
26
27#define FOREACH_ADAPTER_IN_LIST(adapter) \
28 frr_each_safe (mgmt_be_adapters, &mgmt_be_adapters, (adapter))
29
30/*
31 * Static mapping of YANG XPath regular expressions and
32 * the corresponding interested backend clients.
33 * NOTE: Thiis is a static mapping defined by all MGMTD
34 * backend client modules (for now, till we develop a
35 * more dynamic way of creating and updating this map).
36 * A running map is created by MGMTD in run-time to
37 * handle real-time mapping of YANG xpaths to one or
38 * more interested backend client adapters.
39 *
40 * Please see xpath_map_reg[] in lib/mgmt_be_client.c
41 * for the actual map
42 */
0327be91
CH
43struct mgmt_be_xpath_map_init {
44 const char *xpath_regexp;
45 uint subscr_info[MGMTD_BE_CLIENT_ID_MAX];
7d65b7b7
CH
46};
47
0327be91
CH
48struct mgmt_be_xpath_map {
49 char *xpath_regexp;
50 uint subscr_info[MGMTD_BE_CLIENT_ID_MAX];
51};
52
53struct mgmt_be_client_xpath {
54 const char *xpath;
55 uint subscribed;
56};
57
58struct mgmt_be_client_xpath_map {
59 struct mgmt_be_client_xpath *xpaths;
60 uint nxpaths;
7d65b7b7
CH
61};
62
63struct mgmt_be_get_adapter_config_params {
64 struct mgmt_be_client_adapter *adapter;
65 struct nb_config_cbs *cfg_chgs;
66 uint32_t seq;
67};
68
69/*
70 * Static mapping of YANG XPath regular expressions and
71 * the corresponding interested backend clients.
72 * NOTE: Thiis is a static mapping defined by all MGMTD
73 * backend client modules (for now, till we develop a
74 * more dynamic way of creating and updating this map).
75 * A running map is created by MGMTD in run-time to
76 * handle real-time mapping of YANG xpaths to one or
77 * more interested backend client adapters.
78 */
0327be91
CH
79static const struct mgmt_be_xpath_map_init mgmt_xpath_map_init[] = {
80 {
81 .xpath_regexp = "/frr-vrf:lib/*",
82 .subscr_info =
83 {
84#if HAVE_STATICD
85 [MGMTD_BE_CLIENT_ID_STATICD] =
86 MGMT_SUBSCR_VALIDATE_CFG |
87 MGMT_SUBSCR_NOTIFY_CFG,
88#endif
89 },
90 },
91 {
92 .xpath_regexp = "/frr-interface:lib/*",
93 .subscr_info =
94 {
7d65b7b7 95#if HAVE_STATICD
0327be91
CH
96 [MGMTD_BE_CLIENT_ID_STATICD] =
97 MGMT_SUBSCR_VALIDATE_CFG |
98 MGMT_SUBSCR_NOTIFY_CFG,
7d65b7b7 99#endif
0327be91
CH
100 },
101 },
102
103 {
104 .xpath_regexp =
105 "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*",
106 .subscr_info =
107 {
7d65b7b7 108#if HAVE_STATICD
0327be91
CH
109 [MGMTD_BE_CLIENT_ID_STATICD] =
110 MGMT_SUBSCR_VALIDATE_CFG |
111 MGMT_SUBSCR_NOTIFY_CFG,
7d65b7b7 112#endif
0327be91
CH
113 },
114 },
115};
7d65b7b7 116
0327be91
CH
117
118/*
119 * Each client gets their own map, but also union all the strings into the
120 * above map as well.
121 */
7d65b7b7 122#if HAVE_STATICD
0327be91
CH
123static struct mgmt_be_client_xpath staticd_xpaths[] = {
124 {
125 .xpath = "/frr-vrf:lib/*",
126 .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG,
127 },
128 {
129 .xpath = "/frr-interface:lib/*",
130 .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG,
131 },
132 {
133 .xpath =
134 "/frr-routing:routing/control-plane-protocols/control-plane-protocol/frr-staticd:staticd/*",
135 .subscribed = MGMT_SUBSCR_VALIDATE_CFG | MGMT_SUBSCR_NOTIFY_CFG,
136 },
137};
138#endif
139
140static struct mgmt_be_client_xpath_map
141 mgmt_client_xpaths[MGMTD_BE_CLIENT_ID_MAX] = {
142#ifdef HAVE_STATICD
143 [MGMTD_BE_CLIENT_ID_STATICD] = {staticd_xpaths,
144 array_size(staticd_xpaths)},
7d65b7b7 145#endif
7d65b7b7
CH
146};
147
148#define MGMTD_BE_MAX_NUM_XPATH_MAP 256
99564edc 149
0327be91
CH
150/* We would like to have a better ADT than one with O(n)
151 comparisons */
152static struct mgmt_be_xpath_map *mgmt_xpath_map;
153static uint mgmt_num_xpath_maps;
7d65b7b7 154
99564edc
CH
155static struct event_loop *mgmt_loop;
156static struct msg_server mgmt_be_server = {.fd = -1};
7d65b7b7
CH
157
158static struct mgmt_be_adapters_head mgmt_be_adapters;
159
160static struct mgmt_be_client_adapter
161 *mgmt_be_adapters_by_id[MGMTD_BE_CLIENT_ID_MAX];
162
163/* Forward declarations */
164static void
99564edc 165mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter);
7d65b7b7 166
0327be91
CH
167static uint mgmt_be_get_subscr_for_xpath_and_client(
168 const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask);
169
7d65b7b7
CH
170static struct mgmt_be_client_adapter *
171mgmt_be_find_adapter_by_fd(int conn_fd)
172{
173 struct mgmt_be_client_adapter *adapter;
174
175 FOREACH_ADAPTER_IN_LIST (adapter) {
6dad9b53 176 if (adapter->conn->fd == conn_fd)
7d65b7b7
CH
177 return adapter;
178 }
179
180 return NULL;
181}
182
183static struct mgmt_be_client_adapter *
184mgmt_be_find_adapter_by_name(const char *name)
185{
186 struct mgmt_be_client_adapter *adapter;
187
188 FOREACH_ADAPTER_IN_LIST (adapter) {
189 if (!strncmp(adapter->name, name, sizeof(adapter->name)))
190 return adapter;
191 }
192
193 return NULL;
194}
195
7d65b7b7
CH
196static void mgmt_be_xpath_map_init(void)
197{
0327be91 198 uint i;
7d65b7b7
CH
199
200 MGMTD_BE_ADAPTER_DBG("Init XPath Maps");
201
0327be91
CH
202 mgmt_num_xpath_maps = array_size(mgmt_xpath_map_init);
203 mgmt_xpath_map =
204 calloc(1, sizeof(*mgmt_xpath_map) * mgmt_num_xpath_maps);
205 for (i = 0; i < mgmt_num_xpath_maps; i++) {
7d65b7b7 206 MGMTD_BE_ADAPTER_DBG(" - XPATH: '%s'",
0327be91
CH
207 mgmt_xpath_map_init[i].xpath_regexp);
208 mgmt_xpath_map[i].xpath_regexp = XSTRDUP(
209 MTYPE_MGMTD_XPATH, mgmt_xpath_map_init[i].xpath_regexp);
210 memcpy(mgmt_xpath_map[i].subscr_info,
211 mgmt_xpath_map_init[i].subscr_info,
212 sizeof(mgmt_xpath_map_init[i].subscr_info));
7d65b7b7 213 }
7d65b7b7
CH
214 MGMTD_BE_ADAPTER_DBG("Total XPath Maps: %u", mgmt_num_xpath_maps);
215}
216
0327be91
CH
217static void mgmt_be_xpath_map_cleanup(void)
218{
219 uint i;
220
221 for (i = 0; i < mgmt_num_xpath_maps; i++)
222 XFREE(MTYPE_MGMTD_XPATH, mgmt_xpath_map[i].xpath_regexp);
223 free(mgmt_xpath_map);
224}
225
7d65b7b7
CH
226static int mgmt_be_eval_regexp_match(const char *xpath_regexp,
227 const char *xpath)
228{
229 int match_len = 0, re_indx = 0, xp_indx = 0;
230 int rexp_len, xpath_len;
231 bool match = true, re_wild = false, xp_wild = false;
232 bool delim = false, enter_wild_match = false;
233 char wild_delim = 0;
234
235 rexp_len = strlen(xpath_regexp);
236 xpath_len = strlen(xpath);
237
238 /*
239 * Remove the trailing wildcard from the regexp and Xpath.
240 */
241 if (rexp_len && xpath_regexp[rexp_len-1] == '*')
242 rexp_len--;
243 if (xpath_len && xpath[xpath_len-1] == '*')
244 xpath_len--;
245
246 if (!rexp_len || !xpath_len)
247 return 0;
248
249 for (re_indx = 0, xp_indx = 0;
250 match && re_indx < rexp_len && xp_indx < xpath_len;) {
251 match = (xpath_regexp[re_indx] == xpath[xp_indx]);
252
253 /*
254 * Check if we need to enter wildcard matching.
255 */
256 if (!enter_wild_match && !match &&
257 (xpath_regexp[re_indx] == '*'
258 || xpath[xp_indx] == '*')) {
259 /*
260 * Found wildcard
261 */
262 enter_wild_match =
263 (xpath_regexp[re_indx-1] == '/'
264 || xpath_regexp[re_indx-1] == '\''
265 || xpath[xp_indx-1] == '/'
266 || xpath[xp_indx-1] == '\'');
267 if (enter_wild_match) {
268 if (xpath_regexp[re_indx] == '*') {
269 /*
270 * Begin RE wildcard match.
271 */
272 re_wild = true;
273 wild_delim = xpath_regexp[re_indx-1];
274 } else if (xpath[xp_indx] == '*') {
275 /*
276 * Begin XP wildcard match.
277 */
278 xp_wild = true;
279 wild_delim = xpath[xp_indx-1];
280 }
281 }
282 }
283
284 /*
285 * Check if we need to exit wildcard matching.
286 */
287 if (enter_wild_match) {
288 if (re_wild && xpath[xp_indx] == wild_delim) {
289 /*
290 * End RE wildcard matching.
291 */
292 re_wild = false;
293 if (re_indx < rexp_len-1)
294 re_indx++;
295 enter_wild_match = false;
296 } else if (xp_wild
297 && xpath_regexp[re_indx] == wild_delim) {
298 /*
299 * End XP wildcard matching.
300 */
301 xp_wild = false;
302 if (xp_indx < xpath_len-1)
303 xp_indx++;
304 enter_wild_match = false;
305 }
306 }
307
308 match = (xp_wild || re_wild
309 || xpath_regexp[re_indx] == xpath[xp_indx]);
310
311 /*
312 * Check if we found a delimiter in both the Xpaths
313 */
314 if ((xpath_regexp[re_indx] == '/'
315 && xpath[xp_indx] == '/')
316 || (xpath_regexp[re_indx] == ']'
317 && xpath[xp_indx] == ']')
318 || (xpath_regexp[re_indx] == '['
319 && xpath[xp_indx] == '[')) {
320 /*
321 * Increment the match count if we have a
322 * new delimiter.
323 */
324 if (match && re_indx && xp_indx && !delim)
325 match_len++;
326 delim = true;
327 } else {
328 delim = false;
329 }
330
331 /*
332 * Proceed to the next character in the RE/XP string as
333 * necessary.
334 */
335 if (!re_wild)
336 re_indx++;
337 if (!xp_wild)
338 xp_indx++;
339 }
340
341 /*
342 * If we finished matching and the last token was a full match
343 * increment the match count appropriately.
344 */
345 if (match && !delim &&
346 (xpath_regexp[re_indx] == '/'
347 || xpath_regexp[re_indx] == ']'))
348 match_len++;
349
350 return match_len;
351}
352
e3cacd96 353static void mgmt_be_adapter_delete(struct mgmt_be_client_adapter *adapter)
7d65b7b7 354{
e3cacd96 355 MGMTD_BE_ADAPTER_DBG("deleting client adapter '%s'", adapter->name);
7d65b7b7
CH
356
357 /*
99564edc 358 * Notify about disconnect for appropriate cleanup
7d65b7b7 359 */
74335ceb 360 mgmt_txn_notify_be_adapter_conn(adapter, false);
7d65b7b7
CH
361 if (adapter->id < MGMTD_BE_CLIENT_ID_MAX) {
362 mgmt_be_adapters_by_id[adapter->id] = NULL;
363 adapter->id = MGMTD_BE_CLIENT_ID_MAX;
364 }
365
e3cacd96 366 assert(adapter->refcount == 1);
7d65b7b7 367 mgmt_be_adapter_unlock(&adapter);
e3cacd96
CH
368}
369
370static int mgmt_be_adapter_notify_disconnect(struct msg_conn *conn)
371{
372 struct mgmt_be_client_adapter *adapter = conn->user;
373
374 MGMTD_BE_ADAPTER_DBG("notify disconnect for client adapter '%s'",
375 adapter->name);
376
377 mgmt_be_adapter_delete(adapter);
99564edc
CH
378
379 return 0;
7d65b7b7
CH
380}
381
382static void
383mgmt_be_adapter_cleanup_old_conn(struct mgmt_be_client_adapter *adapter)
384{
385 struct mgmt_be_client_adapter *old;
386
387 FOREACH_ADAPTER_IN_LIST (old) {
83b78f43 388 if (old != adapter &&
389 !strncmp(adapter->name, old->name, sizeof(adapter->name))) {
7d65b7b7
CH
390 /*
391 * We have a Zombie lingering around
392 */
393 MGMTD_BE_ADAPTER_DBG(
394 "Client '%s' (FD:%d) seems to have reconnected. Removing old connection (FD:%d)!",
6dad9b53
CH
395 adapter->name, adapter->conn->fd,
396 old->conn->fd);
397 /* this will/should delete old */
398 msg_conn_disconnect(old->conn, false);
7d65b7b7
CH
399 }
400 }
401}
402
9405278e
CH
403
404static int mgmt_be_adapter_send_msg(struct mgmt_be_client_adapter *adapter,
405 Mgmtd__BeMessage *be_msg)
406{
407 return msg_conn_send_msg(
408 adapter->conn, MGMT_MSG_VERSION_PROTOBUF, be_msg,
409 mgmtd__be_message__get_packed_size(be_msg),
410 (size_t(*)(void *, void *))mgmtd__be_message__pack, false);
411}
412
413static int mgmt_be_send_subscr_reply(struct mgmt_be_client_adapter *adapter,
414 bool success)
415{
416 Mgmtd__BeMessage be_msg;
417 Mgmtd__BeSubscribeReply reply;
418
419 mgmtd__be_subscribe_reply__init(&reply);
420 reply.success = success;
421
422 mgmtd__be_message__init(&be_msg);
423 be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY;
424 be_msg.subscr_reply = &reply;
425
426 MGMTD_FE_CLIENT_DBG("Sending SUBSCR_REPLY client: %s sucess: %u",
427 adapter->name, success);
428
429 return mgmt_be_adapter_send_msg(adapter, &be_msg);
430}
431
7d65b7b7
CH
432static int
433mgmt_be_adapter_handle_msg(struct mgmt_be_client_adapter *adapter,
434 Mgmtd__BeMessage *be_msg)
435{
0b645fd2
CH
436 /*
437 * protobuf-c adds a max size enum with an internal, and changing by
438 * version, name; cast to an int to avoid unhandled enum warnings
439 */
440 switch ((int)be_msg->message_case) {
7d65b7b7
CH
441 case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REQ:
442 MGMTD_BE_ADAPTER_DBG(
218625aa 443 "Got SUBSCR_REQ from '%s' to %sregister %zu xpaths",
7d65b7b7 444 be_msg->subscr_req->client_name,
218625aa
CH
445 !be_msg->subscr_req->subscribe_xpaths &&
446 be_msg->subscr_req->n_xpath_reg
7d65b7b7
CH
447 ? "de"
448 : "",
218625aa 449 be_msg->subscr_req->n_xpath_reg);
7d65b7b7
CH
450
451 if (strlen(be_msg->subscr_req->client_name)) {
452 strlcpy(adapter->name, be_msg->subscr_req->client_name,
453 sizeof(adapter->name));
454 adapter->id = mgmt_be_client_name2id(adapter->name);
455 if (adapter->id >= MGMTD_BE_CLIENT_ID_MAX) {
456 MGMTD_BE_ADAPTER_ERR(
457 "Unable to resolve adapter '%s' to a valid ID. Disconnecting!",
458 adapter->name);
6dad9b53
CH
459 /* this will/should delete old */
460 msg_conn_disconnect(adapter->conn, false);
99564edc
CH
461 zlog_err("XXX different from original code");
462 break;
7d65b7b7
CH
463 }
464 mgmt_be_adapters_by_id[adapter->id] = adapter;
465 mgmt_be_adapter_cleanup_old_conn(adapter);
42f4bb2b
CH
466
467 /* schedule INIT sequence now that it is registered */
468 mgmt_be_adapter_sched_init_event(adapter);
7d65b7b7 469 }
9405278e
CH
470
471 if (be_msg->subscr_req->n_xpath_reg)
472 /* we aren't handling dynamic xpaths yet */
473 mgmt_be_send_subscr_reply(adapter, false);
474 else
475 mgmt_be_send_subscr_reply(adapter, true);
7d65b7b7
CH
476 break;
477 case MGMTD__BE_MESSAGE__MESSAGE_TXN_REPLY:
478 MGMTD_BE_ADAPTER_DBG(
218625aa
CH
479 "Got %s TXN_REPLY from '%s' txn-id %" PRIx64
480 " with '%s'",
7d65b7b7 481 be_msg->txn_reply->create ? "Create" : "Delete",
218625aa 482 adapter->name, be_msg->txn_reply->txn_id,
7d65b7b7
CH
483 be_msg->txn_reply->success ? "success" : "failure");
484 /*
74335ceb 485 * Forward the TXN_REPLY to txn module.
7d65b7b7 486 */
74335ceb
YR
487 mgmt_txn_notify_be_txn_reply(
488 be_msg->txn_reply->txn_id,
489 be_msg->txn_reply->create,
490 be_msg->txn_reply->success, adapter);
7d65b7b7
CH
491 break;
492 case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REPLY:
493 MGMTD_BE_ADAPTER_DBG(
218625aa
CH
494 "Got CFGDATA_REPLY from '%s' txn-id %" PRIx64
495 " batch-id %" PRIu64 " err:'%s'",
496 adapter->name, be_msg->cfg_data_reply->txn_id,
497 be_msg->cfg_data_reply->batch_id,
7d65b7b7
CH
498 be_msg->cfg_data_reply->error_if_any
499 ? be_msg->cfg_data_reply->error_if_any
500 : "None");
501 /*
74335ceb 502 * Forward the CGFData-create reply to txn module.
7d65b7b7 503 */
74335ceb
YR
504 mgmt_txn_notify_be_cfgdata_reply(
505 be_msg->cfg_data_reply->txn_id,
506 be_msg->cfg_data_reply->batch_id,
507 be_msg->cfg_data_reply->success,
508 be_msg->cfg_data_reply->error_if_any, adapter);
7d65b7b7
CH
509 break;
510 case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REPLY:
511 MGMTD_BE_ADAPTER_DBG(
218625aa
CH
512 "Got %s CFG_APPLY_REPLY from '%s' txn-id %" PRIx64
513 " for %zu batches id %" PRIu64 "-%" PRIu64 " err:'%s'",
7d65b7b7 514 be_msg->cfg_apply_reply->success ? "successful"
218625aa
CH
515 : "failed",
516 adapter->name, be_msg->cfg_apply_reply->txn_id,
517 be_msg->cfg_apply_reply->n_batch_ids,
518 be_msg->cfg_apply_reply->batch_ids[0],
519 be_msg->cfg_apply_reply->batch_ids
520 [be_msg->cfg_apply_reply->n_batch_ids - 1],
7d65b7b7
CH
521 be_msg->cfg_apply_reply->error_if_any
522 ? be_msg->cfg_apply_reply->error_if_any
523 : "None");
74335ceb
YR
524 /*
525 * Forward the CGFData-apply reply to txn module.
7d65b7b7 526 */
74335ceb
YR
527 mgmt_txn_notify_be_cfg_apply_reply(
528 be_msg->cfg_apply_reply->txn_id,
529 be_msg->cfg_apply_reply->success,
530 (uint64_t *)be_msg->cfg_apply_reply->batch_ids,
531 be_msg->cfg_apply_reply->n_batch_ids,
532 be_msg->cfg_apply_reply->error_if_any, adapter);
7d65b7b7
CH
533 break;
534 case MGMTD__BE_MESSAGE__MESSAGE_GET_REPLY:
535 case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REPLY:
536 case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REPLY:
537 case MGMTD__BE_MESSAGE__MESSAGE_NOTIFY_DATA:
538 /*
539 * TODO: Add handling code in future.
540 */
541 break;
542 /*
543 * NOTE: The following messages are always sent from MGMTD to
544 * Backend clients only and/or need not be handled on MGMTd.
545 */
546 case MGMTD__BE_MESSAGE__MESSAGE_SUBSCR_REPLY:
547 case MGMTD__BE_MESSAGE__MESSAGE_GET_REQ:
548 case MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ:
549 case MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ:
550 case MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ:
551 case MGMTD__BE_MESSAGE__MESSAGE_CFG_CMD_REQ:
552 case MGMTD__BE_MESSAGE__MESSAGE_SHOW_CMD_REQ:
553 case MGMTD__BE_MESSAGE__MESSAGE__NOT_SET:
7d65b7b7
CH
554 default:
555 /*
556 * A 'default' case is being added contrary to the
557 * FRR code guidelines to take care of build
558 * failures on certain build systems (courtesy of
559 * the proto-c package).
560 */
561 break;
562 }
563
564 return 0;
565}
566
7d65b7b7
CH
567static int mgmt_be_send_txn_req(struct mgmt_be_client_adapter *adapter,
568 uint64_t txn_id, bool create)
569{
570 Mgmtd__BeMessage be_msg;
571 Mgmtd__BeTxnReq txn_req;
572
573 mgmtd__be_txn_req__init(&txn_req);
574 txn_req.create = create;
575 txn_req.txn_id = txn_id;
576
577 mgmtd__be_message__init(&be_msg);
578 be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_TXN_REQ;
579 be_msg.txn_req = &txn_req;
580
218625aa
CH
581 MGMTD_BE_ADAPTER_DBG("Sending TXN_REQ to '%s' txn-id: %" PRIu64,
582 adapter->name, txn_id);
7d65b7b7
CH
583
584 return mgmt_be_adapter_send_msg(adapter, &be_msg);
585}
586
587static int
588mgmt_be_send_cfgdata_create_req(struct mgmt_be_client_adapter *adapter,
589 uint64_t txn_id, uint64_t batch_id,
590 Mgmtd__YangCfgDataReq **cfgdata_reqs,
591 size_t num_reqs, bool end_of_data)
592{
593 Mgmtd__BeMessage be_msg;
594 Mgmtd__BeCfgDataCreateReq cfgdata_req;
595
596 mgmtd__be_cfg_data_create_req__init(&cfgdata_req);
597 cfgdata_req.batch_id = batch_id;
598 cfgdata_req.txn_id = txn_id;
599 cfgdata_req.data_req = cfgdata_reqs;
600 cfgdata_req.n_data_req = num_reqs;
601 cfgdata_req.end_of_data = end_of_data;
602
603 mgmtd__be_message__init(&be_msg);
604 be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_DATA_REQ;
605 be_msg.cfg_data_req = &cfgdata_req;
606
607 MGMTD_BE_ADAPTER_DBG(
218625aa
CH
608 "Sending CFGDATA_CREATE_REQ to '%s' txn-id: %" PRIu64
609 " batch-id: %" PRIu64,
610 adapter->name, txn_id, batch_id);
7d65b7b7
CH
611
612 return mgmt_be_adapter_send_msg(adapter, &be_msg);
613}
614
615static int mgmt_be_send_cfgapply_req(struct mgmt_be_client_adapter *adapter,
616 uint64_t txn_id)
617{
618 Mgmtd__BeMessage be_msg;
619 Mgmtd__BeCfgDataApplyReq apply_req;
620
621 mgmtd__be_cfg_data_apply_req__init(&apply_req);
622 apply_req.txn_id = txn_id;
623
624 mgmtd__be_message__init(&be_msg);
625 be_msg.message_case = MGMTD__BE_MESSAGE__MESSAGE_CFG_APPLY_REQ;
626 be_msg.cfg_apply_req = &apply_req;
627
218625aa
CH
628 MGMTD_BE_ADAPTER_DBG("Sending CFG_APPLY_REQ to '%s' txn-id: %" PRIu64,
629 adapter->name, txn_id);
7d65b7b7
CH
630
631 return mgmt_be_adapter_send_msg(adapter, &be_msg);
632}
633
070c5e7a 634static void mgmt_be_adapter_process_msg(uint8_t version, uint8_t *data,
99564edc 635 size_t len, struct msg_conn *conn)
7d65b7b7 636{
6dad9b53
CH
637 struct mgmt_be_client_adapter *adapter = conn->user;
638 Mgmtd__BeMessage *be_msg = mgmtd__be_message__unpack(NULL, len, data);
7d65b7b7 639
f82370b4
CH
640 if (!be_msg) {
641 MGMTD_BE_ADAPTER_DBG(
642 "Failed to decode %zu bytes for adapter: %s", len,
643 adapter->name);
644 return;
7d65b7b7 645 }
f82370b4
CH
646 MGMTD_BE_ADAPTER_DBG("Decoded %zu bytes of message: %u for adapter: %s",
647 len, be_msg->message_case, adapter->name);
648 (void)mgmt_be_adapter_handle_msg(adapter, be_msg);
649 mgmtd__be_message__free_unpacked(be_msg, NULL);
7d65b7b7
CH
650}
651
acd7aea0
CH
652static void mgmt_be_iter_and_get_cfg(struct mgmt_ds_ctx *ds_ctx,
653 const char *xpath, struct lyd_node *node,
218625aa 654 struct nb_node *nb_node, void *ctx)
7d65b7b7 655{
0327be91
CH
656 struct mgmt_be_get_adapter_config_params *parms = ctx;
657 struct mgmt_be_client_adapter *adapter = parms->adapter;
658 uint subscr;
7d65b7b7 659
0327be91
CH
660 subscr = mgmt_be_get_subscr_for_xpath_and_client(
661 xpath, adapter->id, MGMT_SUBSCR_NOTIFY_CFG);
662 if (subscr)
663 nb_config_diff_created(node, &parms->seq, parms->cfg_chgs);
7d65b7b7
CH
664}
665
99564edc
CH
666/*
667 * Initialize a BE client over a new connection
668 */
e6685141 669static void mgmt_be_adapter_conn_init(struct event *thread)
7d65b7b7
CH
670{
671 struct mgmt_be_client_adapter *adapter;
672
e16d030c 673 adapter = (struct mgmt_be_client_adapter *)EVENT_ARG(thread);
6dad9b53 674 assert(adapter && adapter->conn->fd >= 0);
7d65b7b7
CH
675
676 /*
74335ceb 677 * Check first if the current session can run a CONFIG
7d65b7b7
CH
678 * transaction or not. Reschedule if a CONFIG transaction
679 * from another session is already in progress.
74335ceb 680 */
7d65b7b7 681 if (mgmt_config_txn_in_progress() != MGMTD_SESSION_ID_NONE) {
99564edc
CH
682 zlog_err("XXX txn in progress, retry init");
683 mgmt_be_adapter_sched_init_event(adapter);
74335ceb 684 return;
7d65b7b7 685 }
7d65b7b7 686
74335ceb
YR
687 /*
688 * Notify TXN module to create a CONFIG transaction and
689 * download the CONFIGs identified for this new client.
690 * If the TXN module fails to initiate the CONFIG transaction
691 * disconnect from the client forcing a reconnect later.
692 * That should also take care of destroying the adapter.
693 */
7d65b7b7 694 if (mgmt_txn_notify_be_adapter_conn(adapter, true) != 0) {
99564edc 695 zlog_err("XXX notify be adapter conn fail");
6dad9b53 696 msg_conn_disconnect(adapter->conn, false);
7d65b7b7
CH
697 adapter = NULL;
698 }
7d65b7b7
CH
699}
700
99564edc
CH
701/*
702 * Schedule the initialization of the BE client connection.
703 */
7d65b7b7 704static void
99564edc 705mgmt_be_adapter_sched_init_event(struct mgmt_be_client_adapter *adapter)
7d65b7b7 706{
99564edc
CH
707 event_add_timer_msec(mgmt_loop, mgmt_be_adapter_conn_init, adapter,
708 MGMTD_BE_CONN_INIT_DELAY_MSEC,
709 &adapter->conn_init_ev);
7d65b7b7
CH
710}
711
712void mgmt_be_adapter_lock(struct mgmt_be_client_adapter *adapter)
713{
714 adapter->refcount++;
715}
716
717extern void mgmt_be_adapter_unlock(struct mgmt_be_client_adapter **adapter)
718{
99564edc
CH
719 struct mgmt_be_client_adapter *a = *adapter;
720 assert(a && a->refcount);
721
722 if (!--a->refcount) {
723 mgmt_be_adapters_del(&mgmt_be_adapters, a);
724 EVENT_OFF(a->conn_init_ev);
6dad9b53 725 msg_server_conn_delete(a->conn);
99564edc 726 XFREE(MTYPE_MGMTD_BE_ADPATER, a);
7d65b7b7
CH
727 }
728
729 *adapter = NULL;
730}
731
99564edc
CH
732/*
733 * Initialize the BE adapter module
734 */
735void mgmt_be_adapter_init(struct event_loop *tm)
7d65b7b7 736{
99564edc
CH
737 assert(!mgmt_loop);
738 mgmt_loop = tm;
7d65b7b7 739
99564edc
CH
740 mgmt_be_adapters_init(&mgmt_be_adapters);
741 mgmt_be_xpath_map_init();
742
743 if (msg_server_init(&mgmt_be_server, MGMTD_BE_SERVER_PATH, tm,
744 mgmt_be_create_adapter, "backend",
745 &mgmt_debug_be)) {
746 zlog_err("cannot initialize backend server");
747 exit(1);
748 }
7d65b7b7
CH
749}
750
99564edc
CH
751/*
752 * Destroy the BE adapter module
753 */
7d65b7b7
CH
754void mgmt_be_adapter_destroy(void)
755{
e3cacd96
CH
756 struct mgmt_be_client_adapter *adapter;
757
99564edc 758 msg_server_cleanup(&mgmt_be_server);
e3cacd96
CH
759 FOREACH_ADAPTER_IN_LIST (adapter) {
760 mgmt_be_adapter_delete(adapter);
761 }
0327be91 762 mgmt_be_xpath_map_cleanup();
7d65b7b7
CH
763}
764
99564edc
CH
765/*
766 * The server accepted a new connection
767 */
768struct msg_conn *mgmt_be_create_adapter(int conn_fd, union sockunion *from)
7d65b7b7
CH
769{
770 struct mgmt_be_client_adapter *adapter = NULL;
771
e3cacd96
CH
772 assert(!mgmt_be_find_adapter_by_fd(conn_fd));
773
774 adapter = XCALLOC(MTYPE_MGMTD_BE_ADPATER,
775 sizeof(struct mgmt_be_client_adapter));
776 adapter->id = MGMTD_BE_CLIENT_ID_MAX;
777 snprintf(adapter->name, sizeof(adapter->name), "Unknown-FD-%d",
778 conn_fd);
779
780 mgmt_be_adapter_lock(adapter);
781 mgmt_be_adapters_add_tail(&mgmt_be_adapters, adapter);
782 RB_INIT(nb_config_cbs, &adapter->cfg_chgs);
783
784 adapter->conn = msg_server_conn_create(
785 mgmt_loop, conn_fd, mgmt_be_adapter_notify_disconnect,
786 mgmt_be_adapter_process_msg, MGMTD_BE_MAX_NUM_MSG_PROC,
787 MGMTD_BE_MAX_NUM_MSG_WRITE, MGMTD_BE_MSG_MAX_LEN, adapter,
788 "BE-adapter");
789
790 MGMTD_BE_ADAPTER_DBG("Added new MGMTD Backend adapter '%s'",
791 adapter->name);
7d65b7b7 792
99564edc 793 return adapter->conn;
7d65b7b7
CH
794}
795
796struct mgmt_be_client_adapter *
797mgmt_be_get_adapter_by_id(enum mgmt_be_client_id id)
798{
799 return (id < MGMTD_BE_CLIENT_ID_MAX ? mgmt_be_adapters_by_id[id]
800 : NULL);
801}
802
803struct mgmt_be_client_adapter *
804mgmt_be_get_adapter_by_name(const char *name)
805{
806 return mgmt_be_find_adapter_by_name(name);
807}
808
809int mgmt_be_get_adapter_config(struct mgmt_be_client_adapter *adapter,
810 struct mgmt_ds_ctx *ds_ctx,
811 struct nb_config_cbs **cfg_chgs)
812{
7d65b7b7
CH
813 struct mgmt_be_get_adapter_config_params parms;
814
815 assert(cfg_chgs);
816
acd7aea0 817 /*
51941c19 818 * TODO: we should consider making this an assertable condition and
acd7aea0
CH
819 * guaranteeing it be true when this function is called. B/c what is
820 * going to happen if there are some changes being sent, and we don't
821 * gather a new snapshot, what new changes that came after the previous
822 * snapshot will then be lost?
823 */
7d65b7b7
CH
824 if (RB_EMPTY(nb_config_cbs, &adapter->cfg_chgs)) {
825 parms.adapter = adapter;
826 parms.cfg_chgs = &adapter->cfg_chgs;
827 parms.seq = 0;
828
acd7aea0
CH
829 mgmt_ds_iter_data(ds_ctx, "", mgmt_be_iter_and_get_cfg,
830 (void *)&parms);
7d65b7b7
CH
831 }
832
833 *cfg_chgs = &adapter->cfg_chgs;
834 return 0;
835}
836
837int mgmt_be_create_txn(struct mgmt_be_client_adapter *adapter,
838 uint64_t txn_id)
839{
840 return mgmt_be_send_txn_req(adapter, txn_id, true);
841}
842
843int mgmt_be_destroy_txn(struct mgmt_be_client_adapter *adapter,
844 uint64_t txn_id)
845{
846 return mgmt_be_send_txn_req(adapter, txn_id, false);
847}
848
849int mgmt_be_send_cfg_data_create_req(struct mgmt_be_client_adapter *adapter,
850 uint64_t txn_id, uint64_t batch_id,
851 struct mgmt_be_cfgreq *cfg_req,
852 bool end_of_data)
853{
854 return mgmt_be_send_cfgdata_create_req(
855 adapter, txn_id, batch_id, cfg_req->cfgdata_reqs,
856 cfg_req->num_reqs, end_of_data);
857}
858
859extern int
860mgmt_be_send_cfg_apply_req(struct mgmt_be_client_adapter *adapter,
861 uint64_t txn_id)
862{
863 return mgmt_be_send_cfgapply_req(adapter, txn_id);
864}
865
0327be91 866void mgmt_be_get_subscr_info_for_xpath(
7d65b7b7
CH
867 const char *xpath, struct mgmt_be_client_subscr_info *subscr_info)
868{
7d65b7b7 869 enum mgmt_be_client_id id;
0327be91 870 uint i;
7d65b7b7 871
7d65b7b7
CH
872 memset(subscr_info, 0, sizeof(*subscr_info));
873
218625aa 874 MGMTD_BE_ADAPTER_DBG("XPATH: '%s'", xpath);
0327be91
CH
875 for (i = 0; i < mgmt_num_xpath_maps; i++) {
876 if (!mgmt_be_eval_regexp_match(mgmt_xpath_map[i].xpath_regexp,
877 xpath))
878 continue;
879 FOREACH_MGMTD_BE_CLIENT_ID (id) {
880 subscr_info->xpath_subscr[id] |=
881 mgmt_xpath_map[i].subscr_info[id];
7d65b7b7 882 }
7d65b7b7
CH
883 }
884
0327be91 885 if (DEBUG_MODE_CHECK(&mgmt_debug_be, DEBUG_MODE_ALL)) {
7d65b7b7 886 FOREACH_MGMTD_BE_CLIENT_ID (id) {
0327be91
CH
887 if (!subscr_info->xpath_subscr[id])
888 continue;
889 MGMTD_BE_ADAPTER_DBG("Cient: %s: subscribed: 0x%x",
890 mgmt_be_client_id2name(id),
891 subscr_info->xpath_subscr[id]);
7d65b7b7
CH
892 }
893 }
0327be91 894}
7d65b7b7 895
0327be91
CH
896/**
897 * Return the subscription info bits for a given `xpath` for a given
898 * `client_id`.
899 *
900 * Args:
901 * xpath - the xpath to check for subscription information.
902 * client_id - the BE client being checked for.
903 * subscr_mask - The subscr bits the caller is interested in seeing
904 * if set.
905 *
906 * Returns:
907 * The subscription info bits.
908 */
909static uint mgmt_be_get_subscr_for_xpath_and_client(
910 const char *xpath, enum mgmt_be_client_id client_id, uint subscr_mask)
911{
912 struct mgmt_be_client_xpath_map *map;
913 uint subscr = 0;
914 uint i;
915
916 assert(client_id < MGMTD_BE_CLIENT_ID_MAX);
917
918 MGMTD_BE_ADAPTER_DBG("Checking client: %s for xpath: '%s'",
919 mgmt_be_client_id2name(client_id), xpath);
920
921 map = &mgmt_client_xpaths[client_id];
922 for (i = 0; i < map->nxpaths; i++) {
923 if (!mgmt_be_eval_regexp_match(map->xpaths[i].xpath, xpath))
924 continue;
925 MGMTD_BE_ADAPTER_DBG("xpath: %s: matched: %s",
926 map->xpaths[i].xpath, xpath);
927 subscr |= map->xpaths[i].subscribed;
928 if ((subscr & subscr_mask) == subscr_mask)
929 break;
930 }
931 MGMTD_BE_ADAPTER_DBG("client: %s: subscribed: 0x%x",
932 mgmt_be_client_id2name(client_id), subscr);
933 return subscr;
7d65b7b7
CH
934}
935
936void mgmt_be_adapter_status_write(struct vty *vty)
937{
938 struct mgmt_be_client_adapter *adapter;
939
940 vty_out(vty, "MGMTD Backend Adapters\n");
941
942 FOREACH_ADAPTER_IN_LIST (adapter) {
943 vty_out(vty, " Client: \t\t\t%s\n", adapter->name);
6dad9b53 944 vty_out(vty, " Conn-FD: \t\t\t%d\n", adapter->conn->fd);
7d65b7b7
CH
945 vty_out(vty, " Client-Id: \t\t\t%d\n", adapter->id);
946 vty_out(vty, " Ref-Count: \t\t\t%u\n", adapter->refcount);
f82370b4 947 vty_out(vty, " Msg-Recvd: \t\t\t%" PRIu64 "\n",
6dad9b53 948 adapter->conn->mstate.nrxm);
f82370b4 949 vty_out(vty, " Bytes-Recvd: \t\t%" PRIu64 "\n",
6dad9b53 950 adapter->conn->mstate.nrxb);
f82370b4 951 vty_out(vty, " Msg-Sent: \t\t\t%" PRIu64 "\n",
6dad9b53 952 adapter->conn->mstate.ntxm);
f82370b4 953 vty_out(vty, " Bytes-Sent: \t\t%" PRIu64 "\n",
6dad9b53 954 adapter->conn->mstate.ntxb);
7d65b7b7
CH
955 }
956 vty_out(vty, " Total: %d\n",
957 (int)mgmt_be_adapters_count(&mgmt_be_adapters));
958}
959
960void mgmt_be_xpath_register_write(struct vty *vty)
961{
0327be91 962 uint indx;
7d65b7b7
CH
963 enum mgmt_be_client_id id;
964 struct mgmt_be_client_adapter *adapter;
0327be91 965 uint info;
7d65b7b7
CH
966
967 vty_out(vty, "MGMTD Backend XPath Registry\n");
968
969 for (indx = 0; indx < mgmt_num_xpath_maps; indx++) {
970 vty_out(vty, " - XPATH: '%s'\n",
971 mgmt_xpath_map[indx].xpath_regexp);
972 FOREACH_MGMTD_BE_CLIENT_ID (id) {
0327be91
CH
973 info = mgmt_xpath_map[indx].subscr_info[id];
974 if (!info)
975 continue;
976 vty_out(vty,
977 " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n",
978 mgmt_be_client_id2name(id),
979 (info & MGMT_SUBSCR_VALIDATE_CFG) != 0,
980 (info & MGMT_SUBSCR_NOTIFY_CFG) != 0,
981 (info & MGMT_SUBSCR_OPER_OWN) != 0);
982 adapter = mgmt_be_get_adapter_by_id(id);
983 if (adapter)
984 vty_out(vty, " -- Adapter: %p\n", adapter);
7d65b7b7
CH
985 }
986 }
987
988 vty_out(vty, "Total XPath Registries: %u\n", mgmt_num_xpath_maps);
989}
990
991void mgmt_be_xpath_subscr_info_write(struct vty *vty, const char *xpath)
992{
993 struct mgmt_be_client_subscr_info subscr;
994 enum mgmt_be_client_id id;
995 struct mgmt_be_client_adapter *adapter;
0327be91 996 uint info;
7d65b7b7 997
0327be91 998 mgmt_be_get_subscr_info_for_xpath(xpath, &subscr);
7d65b7b7
CH
999
1000 vty_out(vty, "XPath: '%s'\n", xpath);
1001 FOREACH_MGMTD_BE_CLIENT_ID (id) {
0327be91
CH
1002 info = subscr.xpath_subscr[id];
1003 if (!info)
1004 continue;
1005 vty_out(vty,
1006 " -- Client: '%s'\tValidate:%d, Notify:%d, Own:%d\n",
1007 mgmt_be_client_id2name(id),
1008 (info & MGMT_SUBSCR_VALIDATE_CFG) != 0,
1009 (info & MGMT_SUBSCR_NOTIFY_CFG) != 0,
1010 (info & MGMT_SUBSCR_OPER_OWN) != 0);
1011 adapter = mgmt_be_get_adapter_by_id(id);
1012 if (adapter)
1013 vty_out(vty, " -- Adapter: %p\n", adapter);
7d65b7b7
CH
1014 }
1015}