1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* June 29 2023, Christian Hopps <chopps@labn.net>
*
* Copyright (c) 2023, LabN Consulting, L.L.C.
*
* Public APIs:
*
* The message type codes and corresponding message data definitions for
* front-end client messages represent a public API, as such any changes should
* only be made according to backward compatible principles (basically never,
* just use a new message type). Back-end clients being always compiled with FRR
* can be updated (although one should take care in modifying BE messages as it
* could impact private back-end client implementations which will then need to
* be updated by their owners).
*/
#ifndef _FRR_MGMT_MSG_NATIVE_H_
#define _FRR_MGMT_MSG_NATIVE_H_
#ifdef __cplusplus
extern "C" {
#elif 0
}
#endif
#include <zebra.h>
#include "compiler.h"
#include "darr.h"
#include "memory.h"
#include "mgmt_msg.h"
#include "mgmt_defines.h"
#include "northbound.h"
#include <stdalign.h>
/*
* ==================
* Native Message API
* ==================
*
* -----------------------
* Defining A New Message:
* -----------------------
*
* 1) Start with `struct mgmt_msg_header` as the first (unnamed) field.
*
* 2) Add fixed-width fields. Add on natural aligned boundaries (*)
*
* 3) [Optional] Add a zero-length variable field. Add aligned on a 64-bit
* boundary, this is done so that: `value = (HDR + 1)` works.
*
* 4) Define a new MTYPE for the new message type (see DECLARE_MTYPE below
* as well as the paired DEFINE_MTYPE in mgmt_msg_native.c)
*
* These rules are so the messages may be read from and written directly to
* "the wire", easily, using common programming languages (e.g., C, rust, go,
* python, ...)
*
* (*) Natrual aligned boundaries, i.e., uint16_t on 2-byte boundary, uint64_t
* on 8-byte boundaries, ...)
*
* ------------------------------
* Allocating New Native Messages
* ------------------------------
*
* For fixed-length and variable length messages one should allocate new
* messages with the mgmt_msg_native_alloc_msg() passing in the newly defined
* MTYPE. Likewise, to free the message one should use
* mgmt_msg_native_free_msg().
*
* Unknown Variable Length Messages:
* ---------------------------------
*
* If using a zero-length variable length field and the length is not known at
* message creation time, you can use the `native` API function
* mgmt_msg_native_append() to add data to the end of the message, or if a more
* full set of operations are required, the darr_xxxx() API is also available as
* in the Advanced section below.
*
* Notable API Functions:
* ---------------------------------
*
* mgmt_msg_native_alloc_msg() - Allocate a native msg.
* mgmt_msg_native_free_msg() - Free a native msg.
* mgmt_msg_native_append() - Append data to the end of the msg.
* mgmt_msg_native_get_msg_len() - Get the total length of the msg.
* mgmt_msg_native_send_msg() - Send the message.
*
* mgmt_msg_native_xpath_encode() - Encode xpath in xpath, data format message.
* mgmt_msg_native_xpath_data_decode() - Decode xpath, data format message.
* mgmt_msg_native_xpath_decode() - Get the xpath, from xpath, data format message.
* mgmt_msg_native_data_decode() - Get the secondary data from xpath, data message.
* mgmt_msg_native_data_len_decode() - Get length of secondary data.
*
* -------------------------------------
* [Advanced Use] Dynamic Array Messages
* -------------------------------------
*
* NOTE: Most users can simply use mgmt_msg_native_append() and skip this
* section.
*
* This section is only important to understand if you wish to utilize the fact
* that native messages allocated with mgmt_msg_native_alloc_msg are
* actually allocated as uint8_t dynamic arrays (`darr`).
*
* You can utilize all the darr_xxxx() API to manipulate the variable length
* message data in a native message. To do so you simply need to understand that
* the native message is actually a `uint8_t *` darr. So, for example, to append
* data to the end of a message one could do the following:
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* msg = (struct mggm_msg_my_msg *)
* darr_strcat((uint8_t *)msg, "/metric");
*
* // ...
* }
*
* NOTE: If reallocs happen the original passed in pointer will be updated;
* however, any other pointers into the message will become invalid, and so they
* should always be discarded or reinitialized after using any reallocating
* darr_xxx() API functions.
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* char *xpath = msg->xpath; // pointer into message
*
* darr_in_strcat((uint8_t *)msg, "/metric");
* // msg may have been updated to point at new memory
*
* xpath = NULL; // now invalid
* xpath = msg->xpath; // reinitialize
* // ...
* }
*
* Rather than worry about this, it's typical when using dynamic arrays to always
* work from the main pointer to the dynamic array, rather than caching multiple
* pointers into the data. Modern compilers will optimize the code so that it
* adds no extra execution cost.
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* darr_in_strcat((uint8_t *)msg, "/metric");
*
* // Use `msg->xpath` directly rather creating and using an
* // `xpath = msg->xpath` local variable.
*
* if (strcmp(msg->xpath, "foobar/metric")) {
* // ...
* }
* }
*
*/
DECLARE_MTYPE(MSG_NATIVE_MSG);
DECLARE_MTYPE(MSG_NATIVE_ERROR);
DECLARE_MTYPE(MSG_NATIVE_GET_TREE);
DECLARE_MTYPE(MSG_NATIVE_TREE_DATA);
DECLARE_MTYPE(MSG_NATIVE_GET_DATA);
DECLARE_MTYPE(MSG_NATIVE_NOTIFY);
DECLARE_MTYPE(MSG_NATIVE_NOTIFY_SELECT);
DECLARE_MTYPE(MSG_NATIVE_EDIT);
DECLARE_MTYPE(MSG_NATIVE_EDIT_REPLY);
DECLARE_MTYPE(MSG_NATIVE_RPC);
DECLARE_MTYPE(MSG_NATIVE_RPC_REPLY);
DECLARE_MTYPE(MSG_NATIVE_SESSION_REQ);
DECLARE_MTYPE(MSG_NATIVE_SESSION_REPLY);
DECLARE_MTYPE(MSG_NATIVE_SUBSCRIBE);
DECLARE_MTYPE(MSG_NATIVE_TXN_REQ);
DECLARE_MTYPE(MSG_NATIVE_TXN_REPLY);
DECLARE_MTYPE(MSG_NATIVE_CFG_REQ);
DECLARE_MTYPE(MSG_NATIVE_CFG_REPLY);
DECLARE_MTYPE(MSG_NATIVE_CFG_APPLY_REQ);
DECLARE_MTYPE(MSG_NATIVE_CFG_APPLY_REPLY);
DECLARE_MTYPE(MSG_NATIVE_LOCK);
DECLARE_MTYPE(MSG_NATIVE_LOCK_REPLY);
DECLARE_MTYPE(MSG_NATIVE_COMMIT);
DECLARE_MTYPE(MSG_NATIVE_COMMIT_REPLY);
/*
* Native message codes
*/
#define MGMT_MSG_CODE_ERROR 0 /* Public API */
#define MGMT_MSG_CODE_GET_TREE 1 /* BE only, non-public API */
#define MGMT_MSG_CODE_TREE_DATA 2 /* Public API */
#define MGMT_MSG_CODE_GET_DATA 3 /* Public API */
#define MGMT_MSG_CODE_NOTIFY 4 /* Public API */
#define MGMT_MSG_CODE_EDIT 5 /* Public API */
#define MGMT_MSG_CODE_EDIT_REPLY 6 /* Public API */
#define MGMT_MSG_CODE_RPC 7 /* Public API */
#define MGMT_MSG_CODE_RPC_REPLY 8 /* Public API */
#define MGMT_MSG_CODE_NOTIFY_SELECT 9 /* Public API */
#define MGMT_MSG_CODE_SESSION_REQ 10 /* Public API */
#define MGMT_MSG_CODE_SESSION_REPLY 11 /* Public API */
#define MGMT_MSG_CODE_LOCK 19 /* Public API */
#define MGMT_MSG_CODE_LOCK_REPLY 20 /* Public API */
#define MGMT_MSG_CODE_COMMIT 21 /* Public API */
#define MGMT_MSG_CODE_COMMIT_REPLY 22 /* Public API */
/* Derived from protobuf messages to remove protobuf dependency */
#define MGMT_MSG_CODE_SUBSCRIBE 12 /* BE only, non-public API */
#define MGMT_MSG_CODE_TXN_REQ 13 /* BE only, non-public API */
#define MGMT_MSG_CODE_TXN_REPLY 14 /* BE only, non-public API */
#define MGMT_MSG_CODE_CFG_REQ 15 /* BE only, non-public API */
#define MGMT_MSG_CODE_CFG_REPLY 16 /* BE only, non-public API */
#define MGMT_MSG_CODE_CFG_APPLY_REQ 17 /* BE only, non-public API */
#define MGMT_MSG_CODE_CFG_APPLY_REPLY 18 /* BE only, non-public API */
/*
* Datastores
*/
#define MGMT_MSG_DATASTORE_NONE 0
#define MGMT_MSG_DATASTORE_RUNNING 1
#define MGMT_MSG_DATASTORE_CANDIDATE 2
#define MGMT_MSG_DATASTORE_OPERATIONAL 3
/*
* Formats
*/
#define MGMT_MSG_FORMAT_XML 1
#define MGMT_MSG_FORMAT_JSON 2
#define MGMT_MSG_FORMAT_BINARY 3 /* non-standard libyang internal format */
#define MGMT_MSG_FORMAT_LAST 3
/*
* Now we're using LYD_FORMAT directly to avoid mapping code, but having our
* own definitions allows us to create such a mapping in the future if libyang
* makes a backwards incompatible change.
*/
_Static_assert(MGMT_MSG_FORMAT_XML == LYD_XML, "Format mismatch");
_Static_assert(MGMT_MSG_FORMAT_JSON == LYD_JSON, "Format mismatch");
_Static_assert(MGMT_MSG_FORMAT_BINARY == LYD_LYB, "Format mismatch");
/**
* struct mgmt_msg_header - Header common to all native messages.
*
* @code: the actual type of the message.
* @resv: Set to zero, ignore on receive.
* @vsplit: If a variable section is split in 2, the length of first part.
* @refer_id: the session, txn, conn, etc, this message is associated with.
* @req_id: the request this message is for.
*/
struct mgmt_msg_header {
uint16_t code;
uint16_t resv;
uint32_t vsplit;
uint64_t refer_id;
uint64_t req_id;
};
_Static_assert(sizeof(struct mgmt_msg_header) == 3 * 8, "Bad padding");
_Static_assert(sizeof(struct mgmt_msg_header) ==
offsetof(struct mgmt_msg_header, req_id) +
sizeof(((struct mgmt_msg_header *)0)->req_id),
"Size mismatch");
/**
* struct mgmt_msg_error - Common error message.
*
* @error: An error value.
* @errst: Description of error can be 0 length.
*
* This common error message can be used for replies for many msg requests
* (req_id).
*/
struct mgmt_msg_error {
struct mgmt_msg_header;
int16_t error;
uint8_t resv2[6];
alignas(8) char errstr[];
};
_Static_assert(sizeof(struct mgmt_msg_error) ==
offsetof(struct mgmt_msg_error, errstr),
"Size mismatch");
/**
* struct mgmt_msg_get_tree - backend oper data request.
*
* @result_type: ``LYD_FORMAT`` for the returned result.
* @xpath: the query for the data to return.
*/
struct mgmt_msg_get_tree {
struct mgmt_msg_header;
uint8_t result_type;
uint8_t resv2[7];
alignas(8) char xpath[];
};
_Static_assert(sizeof(struct mgmt_msg_get_tree) ==
offsetof(struct mgmt_msg_get_tree, xpath),
"Size mismatch");
/**
* struct mgmt_msg_tree_data - Message carrying tree data.
*
* @partial_error: If the full result could not be returned do to this error.
* @result_type: ``LYD_FORMAT`` for format of the @result value.
* @more: if this is a partial return and there will be more coming.
* @result: The tree data in @result_type format.
*
*/
struct mgmt_msg_tree_data {
struct mgmt_msg_header;
int8_t partial_error;
uint8_t result_type;
uint8_t more;
uint8_t resv2[5];
alignas(8) uint8_t result[];
};
_Static_assert(sizeof(struct mgmt_msg_tree_data) ==
offsetof(struct mgmt_msg_tree_data, result),
"Size mismatch");
/* Flags for get-data request */
#define GET_DATA_FLAG_STATE 0x01 /* include "config false" data */
#define GET_DATA_FLAG_CONFIG 0x02 /* include "config true" data */
#define GET_DATA_FLAG_EXACT 0x04 /* get exact data node instead of the full tree */
/*
* Modes of reporting default values. Non-default values are always reported.
* These options reflect "with-defaults" modes as defined in RFC 6243.
*/
#define GET_DATA_DEFAULTS_EXPLICIT 0 /* "explicit" */
#define GET_DATA_DEFAULTS_TRIM 1 /* "trim" */
#define GET_DATA_DEFAULTS_ALL 2 /* "report-all" */
#define GET_DATA_DEFAULTS_ALL_ADD_TAG 3 /* "report-all-tagged" */
/**
* struct mgmt_msg_get_data - frontend get-data request.
*
* @result_type: ``LYD_FORMAT`` for the returned result.
* @flags: combination of ``GET_DATA_FLAG_*`` flags.
* @defaults: one of ``GET_DATA_DEFAULTS_*`` values.
* @xpath: the query for the data to return.
*/
struct mgmt_msg_get_data {
struct mgmt_msg_header;
uint8_t result_type;
uint8_t flags;
uint8_t defaults;
uint8_t datastore;
uint8_t resv2[4];
alignas(8) char xpath[];
};
_Static_assert(sizeof(struct mgmt_msg_get_data) ==
offsetof(struct mgmt_msg_get_data, xpath),
"Size mismatch");
#define NOTIFY_OP_NOTIFICATION 0
#define NOTIFY_OP_DS_REPLACE 1
#define NOTIFY_OP_DS_DELETE 2
#define NOTIFY_OP_DS_PATCH 3
#define NOTIFY_OP_DS_GET_SYNC 4
/**
* struct mgmt_msg_notify_data - Message carrying notification data.
*
* @result_type: ``LYD_FORMAT`` for format of the @result value.
* @data: The xpath string of the notification followed by the tree data in
* @result_type format.
* @op: notify operation type.
*/
struct mgmt_msg_notify_data {
struct mgmt_msg_header;
uint8_t result_type;
uint8_t op;
uint8_t resv2[6];
alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_notify_data) == offsetof(struct mgmt_msg_notify_data, data),
"Size mismatch");
#define EDIT_FLAG_IMPLICIT_LOCK 0x01
#define EDIT_FLAG_IMPLICIT_COMMIT 0x02
#define EDIT_OP_CREATE 0
#define EDIT_OP_DELETE 4
#define EDIT_OP_MERGE 2
#define EDIT_OP_REPLACE 5
#define EDIT_OP_REMOVE 3
_Static_assert(EDIT_OP_CREATE == NB_OP_CREATE_EXCL, "Operation mismatch");
_Static_assert(EDIT_OP_DELETE == NB_OP_DELETE, "Operation mismatch");
_Static_assert(EDIT_OP_MERGE == NB_OP_MODIFY, "Operation mismatch");
_Static_assert(EDIT_OP_REPLACE == NB_OP_REPLACE, "Operation mismatch");
_Static_assert(EDIT_OP_REMOVE == NB_OP_DESTROY, "Operation mismatch");
/**
* struct mgmt_msg_edit - frontend edit request.
*
* @request_type: ``LYD_FORMAT`` for the @data.
* @flags: combination of ``EDIT_FLAG_*`` flags.
* @datastore: the datastore to edit.
* @operation: one of ``EDIT_OP_*`` operations.
* @data: the xpath followed by the tree data for the operation.
* for CREATE, xpath points to the parent node.
*/
struct mgmt_msg_edit {
struct mgmt_msg_header;
uint8_t request_type;
uint8_t flags;
uint8_t datastore;
uint8_t operation;
uint8_t resv2[4];
alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_edit) ==
offsetof(struct mgmt_msg_edit, data),
"Size mismatch");
/**
* struct mgmt_msg_edit_reply - frontend edit reply.
*
* @changed: If true then changes in datastore resulted.
* @created: If true then object was newly created (non-existing before)
* @data: @vsplit values, second value may be zero len.
* @data: [0] the xpath of the data node that was created.
* @data: [1] Possible structured data to pass back to client (e.g., non-"error"
* yang modeled error data).
*/
struct mgmt_msg_edit_reply {
struct mgmt_msg_header;
uint8_t changed;
uint8_t created;
uint8_t resv2[6];
alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_edit_reply) ==
offsetof(struct mgmt_msg_edit_reply, data),
"Size mismatch");
/**
* struct mgmt_msg_rpc - RPC/action request.
*
* @request_type: ``LYD_FORMAT`` for the @data.
* @data: the xpath followed by the tree data for the operation.
*/
struct mgmt_msg_rpc {
struct mgmt_msg_header;
uint8_t request_type;
uint8_t resv2[7];
alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_rpc) ==
offsetof(struct mgmt_msg_rpc, data),
"Size mismatch");
/**
* struct mgmt_msg_rpc_reply - RPC/action reply.
*
* @result_type: ``LYD_FORMAT`` for the @data.
* @data: the tree data for the reply.
*/
struct mgmt_msg_rpc_reply {
struct mgmt_msg_header;
uint8_t result_type;
uint8_t resv2[7];
alignas(8) char data[];
};
_Static_assert(sizeof(struct mgmt_msg_rpc_reply) ==
offsetof(struct mgmt_msg_rpc_reply, data),
"Size mismatch");
/**
* struct mgmt_msg_notify_select - Add notification selectors for FE client.
*
* Add xpath prefix notification selectors to limit the notifications sent
* to the front-end client.
*
* Also used by mgmtd to the backend clients. In this case behavior depends on
* the @get_only boolean. If unset the backend filter set is updated
* accordingly, otherwise @get_only is true and a "get" operation is done within
* the notification stream using the given selectors, and the filter set in the
* backend is not modified. This is used so front-end clients can get an initial
* dump of the state followed by notifications w/o missing any changes to the
* state.
*
* @selectors: the xpath prefixes to selectors notifications through.
* @replace: if true replace existing selectors with `selectors`.
* @get_only: [backend-only]: if true do get op, don't change selectors.
*/
struct mgmt_msg_notify_select {
struct mgmt_msg_header;
uint8_t replace;
uint8_t get_only;
uint8_t resv2[6];
alignas(8) char selectors[];
};
_Static_assert(sizeof(struct mgmt_msg_notify_select) ==
offsetof(struct mgmt_msg_notify_select, selectors),
"Size mismatch");
#define DEFAULT_NOTIFY_FORMAT MGMT_MSG_FORMAT_JSON
/**
* struct mgmt_msg_session_req - Create or delete a front-end session.
*
* @refer_id: Zero for create, otherwise the session-id to delete.
* @req_id: For create will use as client-id.
* @notify_format: Format for notification data or 0 for default.
* @client_name: For first session request the client name, otherwise empty.
*/
struct mgmt_msg_session_req {
struct mgmt_msg_header;
uint8_t notify_format;
uint8_t resv2[7];
alignas(8) char client_name[];
};
_Static_assert(sizeof(struct mgmt_msg_session_req) ==
offsetof(struct mgmt_msg_session_req, client_name),
"Size mismatch");
/**
* struct mgmt_msg_session_reply - Reply to session request message.
*
* @created: true if this is a reply to a create request, otherwise 0.
* @refer_id: The session-id for the action (create or delete) just taken.
*/
struct mgmt_msg_session_reply {
struct mgmt_msg_header;
uint8_t created;
uint8_t resv2[7];
};
/**
* struct mgmt_msg_lock - Set or clear a lock on a datastore.
*
* @refer_id: The session-id that will own (or release) the lock.
* @datastore: the datastore to lock or unlock.
* @lock: true to lock, false to unlock.
*/
struct mgmt_msg_lock {
struct mgmt_msg_header;
uint8_t datastore; /* MGMT_MSG_DATASTORE_* */
uint8_t lock; /* True to lock, false to unlock */
uint8_t resv2[6];
};
/**
* struct mgmt_msg_lock_reply - Reply to lock message.
*
* @refer_id: The session-id that will own (or release) the lock.
* @datastore: the datastore to lock or unlock.
* @lock: true if locked, false if unlocked.
*/
struct mgmt_msg_lock_reply {
struct mgmt_msg_header;
uint8_t datastore; /* MGMT_MSG_DATASTORE_* */
uint8_t lock; /* True if locked, false to unlocked */
uint8_t resv2[6];
};
#define MGMT_MSG_COMMIT_APPLY 0
#define MGMT_MSG_COMMIT_ABORT 1
#define MGMT_MSG_COMMIT_VALIDATE 2
/**
* struct mgmt_msg_commit - Set or clear a commit on a datastore.
*
* @refer_id: The session-id that will own (or release) the commit.
* @source: the datastore to commit from, abort changes or validate changes.
* @target: the datastore to commit into.
* @action: apply, abort, or validate the changes from source to target.
* @unlock: value passed to reply message.
*/
struct mgmt_msg_commit {
struct mgmt_msg_header;
uint8_t source; /* MGMT_MSG_DATASTORE_* */
uint8_t target; /* MGMT_MSG_DATASTORE_* */
uint8_t action; /* MGMT_MSG_COMMIT_* */
uint8_t unlock;
uint8_t resv2[4];
};
/**
* struct mgmt_msg_commit_reply - Reply to commit message.
*
* @refer_id: The session-id that will own (or release) the commit.
* @source: the datastore committed from, or the changes aborted or validated.
* @target: the datastore committed into.
* @action: action taken, i.e., apply, abort, or validate.
* @unlock: true indicates receiver should unlock the datastores.
*/
struct mgmt_msg_commit_reply {
struct mgmt_msg_header;
uint8_t source; /* MGMT_MSG_DATASTORE_* */
uint8_t target; /* MGMT_MSG_DATASTORE_* */
uint8_t action; /* MGMT_MSG_COMMIT_* */
uint8_t unlock;
uint8_t resv2[4];
};
/**
* struct mgmt_msg_subscribe - Subscribe YANG paths for backend client.
*
* @nconfig: Number of config paths that start the array.
* @noper: Number of oper-state paths that follow config.
* @nnotif: Number of notif paths that follow oper.
* @nrpc: Number of rpc paths that follow notif.
* @strings: A sequence of strings that starts with the name of the client
* followed by @nconfig xpath prefxies, then by @noper xpath
* prefixes, finally by @nrpc xpath prefixes.
*
*/
struct mgmt_msg_subscribe {
struct mgmt_msg_header;
uint16_t nconfig; /* config path count */
uint16_t noper; /* oper-state path count */
uint16_t nnotify; /* notif path count */
uint16_t nrpc; /* rpc path count */
alignas(8) char strings[];
};
/**
* struct mgmt_msg_txn_req - Create or delete a transaction.
*
* @refer_id: The transaction ID to create or delete.
* @create: true if this is a create request, otherwise delete.
*/
struct mgmt_msg_txn_req {
struct mgmt_msg_header;
uint8_t create;
uint8_t resv2[7];
};
/**
* struct mgmt_msg_txn_reply - Reply to transaction request message.
*
* @refer_id: The transaction ID for the action (create or delete) just taken.
* @created: true if this is a reply to a create request, otherwise 0.
*/
struct mgmt_msg_txn_reply {
struct mgmt_msg_header;
uint8_t created;
uint8_t resv2[7];
};
/**
* struct mgmt_msg_cfg_req - Send the configuration to backend.
*
* @refer_id: The transaction ID to add config changes to.
* @config: The configuration changes as a sequence of strings to validate and
* store for applying. These are followed by a final string indicating
* the action to take for the preceding strings. There are 2 actions
* possible: "m" for modify and "r" for remove.
*
* To process the config data, walk the action string, each byte indicates the
* action to take. For "d" (delete/remove) consume 1 string which is the xpath to
* remove. For "m" (modify) consume 2 strings, the first is the xpath to modify
* and the second is the new value. The value may be zero length for YANG value
* type "empty".
*/
struct mgmt_msg_cfg_req {
struct mgmt_msg_header;
uint8_t resv2[8]; /* bug in compiler */
alignas(8) char config[];
};
_Static_assert(sizeof(struct mgmt_msg_cfg_req) == offsetof(struct mgmt_msg_cfg_req, config),
"Size mismatch");
/**
* struct mgmt_msg_cfg_reply - Reply to configuration message.
*
* @refer_id: The transaction ID whose configuration was received and validated.
*/
struct mgmt_msg_cfg_reply {
struct mgmt_msg_header;
};
/**
* struct mgmt_msg_cfg_apply_req - Apply config in transaction.
*
* @refer_id: The transaction ID to apply.
*/
struct mgmt_msg_cfg_apply_req {
struct mgmt_msg_header;
};
/**
* struct mgmt_msg_cfg_apply_reply - Signal the config has been applied.
*
* @refer_id: The transaction ID which was applied.
*/
struct mgmt_msg_cfg_apply_reply {
struct mgmt_msg_header;
};
/*
* Validate that the message ends in a NUL terminating byte
*/
#define MGMT_MSG_VALIDATE_NUL_TERM(msgp, len) \
((len) >= sizeof(*msgp) + 1 && ((char *)msgp)[(len)-1] == 0)
/**
* mgmt_msg_get_min_size() - Get minimum message size given the type
* @code: The type of the message (MGMT_MSG_CODE_*)
*
* Return:
* The minimum size of a message of the given type or 0 if the message
* code is unknown.
*/
size_t mgmt_msg_get_min_size(uint code);
/**
* Send a native message error to the other end of the connection.
*
* This function is normally used by the server-side to indicate a failure to
* process a client request. For this server side handling of client messages
* which expect a reply, either that reply or this error should be returned, as
* closing the connection is not allowed during message handling.
*
* Args:
* conn: the connection.
* sess_or_txn_id: Session ID (to FE client) or Txn ID (from BE client)
* req_id: which req_id this error is associated with.
* short_circuit_ok: if short circuit sending is OK.
* error: the error value
* errfmt: vprintfrr style format string
* ap: the variable args for errfmt.
*
* Return:
* The return value of ``msg_conn_send_msg``.
*/
extern int vmgmt_msg_native_send_error(struct msg_conn *conn,
uint64_t sess_or_txn_id, uint64_t req_id,
bool short_circuit_ok, int16_t error,
const char *errfmt, va_list ap)
PRINTFRR(6, 0);
/**
* mgmt_msg_native_alloc_msg() - Create a native appendable msg.
* @msg_type: The message structure type.
* @var_len: The initial additional length to add to the message.
* @mem_type: The initial additional length to add to the message.
*
* This function takes a C type (e.g., `struct mgmt_msg_get_tree`) as an
* argument and returns a new native message. The newly allocated message
* can be used with the other `native` functions.
*
* Importantly the mgmt_msg_native_append() function can be used to add data
* to the end of the message, and mgmt_msg_get_native_msg_len() can be used
* to obtain the total length of the message (i.e., the fixed sized header plus
* the variable length data that has been appended).
*
* Additionally, a dynamic array (darr) pointer can be obtained using
* mgmt_msg_get_native_darr() which allows adding and manipulating the
* variable data that follows the fixed sized header.
*
* Return: A `msg_type` object created using a dynamic_array.
*/
#define mgmt_msg_native_alloc_msg(msg_type, var_len, mem_type) \
({ \
uint8_t *_nam_buf = NULL; \
darr_append_nz_mt(_nam_buf, sizeof(msg_type) + (var_len), mem_type); \
(msg_type *)_nam_buf; \
})
/**
* mgmt_msg_free_native_msg() - Free a native msg.
* @msg: pointer to message allocated by mgmt_msg_create_native_msg().
*/
#define mgmt_msg_native_free_msg(msg) darr_free(msg)
/**
* mgmt_msg_native_get_msg_len() - Get the total length of the msg.
* @msg: the native message.
*
* Return: the total length of the message, fixed + variable length.
*/
#define mgmt_msg_native_get_msg_len(msg) (darr_len((uint8_t *)(msg)))
/**
* mgmt_msg_native_append() - Append data to the end of the msg.
* @msg: (IN/OUT) Pointer to the native message, variable may be updated.
* @data: data to append.
* @len: length of data to append.
*
* Append @data of length @len to the native message @msg.
*
* NOTE: Be aware @msg pointer may change as a result of reallocating the
* message to fit the new data. Any other pointers into the old message should
* be discarded.
*
* Return: a pointer to the newly appended data.
*/
#define mgmt_msg_native_append(msg, data, len) \
({ \
uint8_t **_na_darrp = mgmt_msg_native_get_darrp(msg); \
uint8_t *_na_p = darr_append_n(*_na_darrp, len); \
memcpy(_na_p, data, len); \
_na_p; \
})
/**
* mgmt_msg_native_add_str() - Append [another] string to the msg.
* @msg: (IN/OUT) Pointer to the native message, variable may be updated.
* @s: string to append.
*
* Append string @s to the native message @msg. @msg is assumed to have a
* sequence of NUL-terminated strings at the end of it. This function appends
* the string @s and it's NUL terminating octet to the message.
*
* NOTE: Be aware @msg pointer may change as a result of reallocating the
* message to fit the new data. Any other pointers into the old message should
* be discarded.
*/
#define mgmt_msg_native_add_str(msg, s) \
do { \
int _nas_len = strlen(s) + 1; \
mgmt_msg_native_append(msg, s, _nas_len); \
} while (0)
/**
* mgmt_msg_native_send_msg(msg, short_circuit_ok) - Send a native msg.
* @conn: the mgmt_msg connection.
* @msg: the native message.
* @short_circuit_ok: True if short-circuit sending is required.
*
* Return: The error return value of msg_conn_send_msg().
*/
#define mgmt_msg_native_send_msg(conn, msg, short_circuit_ok) \
msg_conn_send_msg(conn, MGMT_MSG_VERSION_NATIVE, msg, \
mgmt_msg_native_get_msg_len(msg), NULL, \
short_circuit_ok)
/**
* mgmt_msg_native_get_darrp() - Return a ptr to the dynamic array ptr.
* @msg: Pointer to the native message.
*
* NOTE: Most users can simply use mgmt_msg_native_append() instead of this.
*
* This function obtains a pointer to the dynamic byte array for this message,
* this array actually includes the message header if one is going to look at
* the length value. With that in mind any of the `darr_*()` functions/API may
* be used to manipulate the variable data at the end of the message.
*
* NOTE: The pointer returned is actually a pointer to the message pointer
* passed in to this function. This pointer to pointer is required so that
* realloc can be done inside the darr API.
*
* NOTE: If reallocs happen the original passed in pointer will be updated;
* however, any other pointers into the message will become invalid and so they
* should always be discarded after using the returned value.
*
* Example:
*
* void append_metric_path(struct mgmt_msg_my_msg *msg)
* {
* char *xpath = msg->xpath; // pointer into message
* uint8_t **darp;
*
* darrp = mgmt_msg_native_get_darrp(msg);
* darr_in_strcat(*darrp, "/metric");
*
* xpath = NULL; // now invalid
* xpath = msg->xpath;
* }
*
*
* Return: A pointer to the first argument -- which is a pointer to a pointer to
* a dynamic array.
*/
#define mgmt_msg_native_get_darrp(msg) ((uint8_t **)&(msg))
/* ------------------------- */
/* Encode and Decode Helpers */
/* ------------------------- */
/**
* mgmt_msg_native_xpath_encode() - encode an xpath in a xpath, data message.
* @msg: Pointer to the native message.
* @xpath: The xpath string to encode.
*
* This function starts the encoding of a message that can be decoded with
* `mgmt_msg_native_xpath_data_decode()`. The variable length data is comprised
* of a NUL terminated string followed by some data of any format. This starts
* the first half of the encoding, after which one can simply append the
* secondary data to the message.
*/
#define mgmt_msg_native_xpath_encode(msg, xpath) \
do { \
size_t _m__slen = strlen(xpath) + 1; \
mgmt_msg_native_append(msg, xpath, _m__slen); \
(msg)->vsplit = _m__slen; \
} while (0)
/**
* mgmt_msg_native_xpath_data_decode() - decode an xpath, data format message.
* @msg: Pointer to the native message.
* @msglen: Length of the message.
* @data: [OUT] Pointer to the data section of the variable data
*
* This function decodes a message that was encoded with
* `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
* NUL terminated string followed by some data of any format.
*
* Return:
* The xpath string or NULL if there was an error decoding (i.e., the
* message is corrupt).
*/
#define mgmt_msg_native_xpath_data_decode(msg, msglen, _data) \
({ \
size_t _m__len = (msglen) - sizeof(*msg); \
const char *_m__s = NULL; \
(_data) = NULL; \
if (msg->vsplit && msg->vsplit <= _m__len && msg->data[msg->vsplit - 1] == 0) { \
if (msg->vsplit < _m__len) \
(_data) = msg->data + msg->vsplit; \
_m__s = msg->data; \
} \
_m__s; \
})
/**
* mgmt_msg_native_xpath_decode() - return the xpath from xpath, data message.
* @msg: Pointer to the native message.
* @msglen: Length of the message.
*
* This function decodes the xpath from a message that was encoded with
* `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
* NUL terminated string followed by some data of any format.
*
* Return:
* The xpath string or NULL if there was an error decoding (i.e., the
* message is corrupt).
*/
#define mgmt_msg_native_xpath_decode(msg, msglen) \
({ \
size_t _m__len = (msglen) - sizeof(*msg); \
const char *_m__s = msg->data; \
if (!msg->vsplit || msg->vsplit > _m__len || _m__s[msg->vsplit - 1] != 0) \
_m__s = NULL; \
_m__s; \
})
/**
* mgmt_msg_native_data_decode() - return the data from xpath, data message.
* @msg: Pointer to the native message.
* @msglen: Length of the message.
*
* This function decodes the secondary data from a message that was encoded with
* `mgmt_msg_native_xpath_encode()`. The variable length data is comprised of a
* NUL terminated string followed by some data of any format.
*
* Return:
* The secondary data or NULL if there was an error decoding (i.e., the
* message is corrupt).
*/
#define mgmt_msg_native_data_decode(msg, msglen) \
({ \
size_t _m__len = (msglen) - sizeof(*msg); \
const char *_m__data = msg->data + msg->vsplit; \
if (!msg->vsplit || msg->vsplit > _m__len || _m__data[-1] != 0) \
_m__data = NULL; \
_m__data; \
})
/**
* mgmt_msg_native_data_len_decode() - len of data in xpath, data format message.
* @msg: Pointer to the native message.
* @msglen: Length of the message.
*
* This function returns the length of the secondary variable data from a
* message that was encoded with `mgmt_msg_native_xpath_encode()`. The variable
* length data is comprised of a NUL terminated string followed by some data of
* any format.
*
* Return:
* The length of the secondary variable data. The message is assumed to be
* validated as not corrupt already.
*/
#define mgmt_msg_native_data_len_decode(msg, msglen) \
((msglen) - sizeof(*msg) - msg->vsplit)
/**
* mgmt_msg_native_strings_decode() - Get dynamic array of str ptrs from the msg.
* @msg: Pointer to the native message.
* @msglen: Length of the message.
* @sdata: pointer to the variable length string data at end of @msg.
*
* Given a pointer to a sequence of NUL-terminated strings allocate
* and return a dynamic array of dynamic array strings. This function
* can be used to decode a message that was built using
* mgmt_msg_native_add_str().
*
* Return: a dynamic array (darr) of string pointers, or NULL if the message
* is corrupt.
*/
#define mgmt_msg_native_strings_decode(msg, msg_len, sdata) \
_mgmt_msg_native_strings_decode(sdata, \
(msg_len) - ((sdata) - (char *)(msg)))
extern const char **_mgmt_msg_native_strings_decode(const void *sdata,
int sdlen);
#ifdef __cplusplus
}
#endif
#endif /* _FRR_MGMT_MSG_NATIVE_H_ */
|