mirror of
https://github.com/kernelkit/infix.git
synced 2026-08-02 13:53:01 +02:00
statd: Include new yangerd backend for operational
This commit is contained in:
@@ -2,7 +2,7 @@ DISTCLEANFILES = *~ *.d
|
||||
ACLOCAL_AMFLAGS = -I m4
|
||||
|
||||
sbin_PROGRAMS = statd
|
||||
statd_SOURCES = statd.c shared.c shared.h journal.c journal_retention.c journal.h avahi.c avahi.h
|
||||
statd_SOURCES = statd.c shared.c shared.h journal.c journal_retention.c journal.h avahi.c avahi.h yangerd.c yangerd.h
|
||||
statd_CPPFLAGS = -D_DEFAULT_SOURCE -D_GNU_SOURCE
|
||||
statd_CFLAGS = -W -Wall -Wextra
|
||||
statd_CFLAGS += $(jansson_CFLAGS) $(libyang_CFLAGS) $(sysrepo_CFLAGS)
|
||||
|
||||
+41
-108
@@ -21,23 +21,16 @@
|
||||
#include <ctype.h>
|
||||
#include <linux/if.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/helpers.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/systemv.h>
|
||||
|
||||
#include "shared.h"
|
||||
#include "journal.h"
|
||||
#include "avahi.h"
|
||||
#include "yangerd.h"
|
||||
|
||||
/* New kernel feature, not in sys/mman.h yet */
|
||||
#ifndef MFD_NOEXEC_SEAL
|
||||
#define MFD_NOEXEC_SEAL 0x0008U
|
||||
#endif
|
||||
|
||||
#define YANGER_BINPATH YANGER_DIR"/yanger"
|
||||
#define XPATH_MAX PATH_MAX
|
||||
#define XPATH_IFACE_BASE "/ietf-interfaces:interfaces"
|
||||
#define XPATH_ROUTING_BASE "/ietf-routing:routing/control-plane-protocols/control-plane-protocol"
|
||||
@@ -74,98 +67,61 @@ struct statd {
|
||||
struct mdns_ctx mdns; /* mDNS neighbor monitor */
|
||||
};
|
||||
|
||||
static int ly_add_yanger_data(const struct ly_ctx *ctx, struct lyd_node **parent,
|
||||
char *yanger_args[])
|
||||
static int ly_add_yangerd_data(const struct ly_ctx *ctx, struct lyd_node **parent,
|
||||
const char *path)
|
||||
{
|
||||
FILE *stream;
|
||||
char *json = NULL;
|
||||
size_t len = 0;
|
||||
int err;
|
||||
int fd;
|
||||
|
||||
fd = memfd_create("yanger_tmpfile", MFD_CLOEXEC | MFD_NOEXEC_SEAL);
|
||||
if (fd == -1) {
|
||||
ERROR("Error, unable to create memfd");
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
/* Wrap the file descriptor in a FILE stream for fwrite */
|
||||
stream = fdopen(fd, "w+");
|
||||
if (stream == NULL) {
|
||||
ERROR("Error, unable to fdopen memfd");
|
||||
close(fd);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
err = fsystemv(yanger_args, NULL, stream, NULL);
|
||||
err = yangerd_query(path, &json, &len);
|
||||
if (err) {
|
||||
ERROR("Error, running yanger");
|
||||
fclose(stream);
|
||||
free(json);
|
||||
ERROR("yangerd: query failed for %s", path);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
fflush(stream);
|
||||
|
||||
if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
|
||||
ERROR("Error, unable reset stream (seek)");
|
||||
fclose(stream);
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
err = lyd_parse_data_fd(ctx, fd, LYD_JSON, LYD_PARSE_ONLY, 0, parent);
|
||||
err = lyd_parse_data_mem(ctx, json, LYD_JSON, LYD_PARSE_ONLY, 0, parent);
|
||||
if (err)
|
||||
ERROR("Error, parsing yanger data (%d): %s", err, ly_errmsg(ctx));
|
||||
|
||||
fclose(stream);
|
||||
/* Note: fclose() already closes the underlying fd from fdopen() */
|
||||
|
||||
free(json);
|
||||
return err;
|
||||
}
|
||||
|
||||
static char *xpath_extract(const char *xpath, const char *key)
|
||||
static const char *xpath_to_yangerd_path(const char *xpath, char *buf, size_t bufsz)
|
||||
{
|
||||
char *res = NULL;
|
||||
const char *ptr;
|
||||
const char *end;
|
||||
const char *start, *slash;
|
||||
size_t len;
|
||||
|
||||
/* (also checks if key exist) */
|
||||
ptr = strstr(xpath, key);
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
|
||||
ptr += strlen(key);
|
||||
|
||||
end = strchr(ptr, '\'');
|
||||
if (!end) {
|
||||
ERROR("Can't find end quote for %s (sanity check)", key);
|
||||
return NULL;
|
||||
if (!xpath || !*xpath || !strcmp(xpath, "*") || !strcmp(xpath, "/*")) {
|
||||
buf[0] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
if ((end - ptr) >= XPATH_MAX) {
|
||||
ERROR("Value for %s is to long (sanity check)", key);
|
||||
return NULL;
|
||||
}
|
||||
start = xpath;
|
||||
if (*start == '/')
|
||||
start++;
|
||||
|
||||
res = calloc((end - ptr) + 1, sizeof(char));
|
||||
if (!res)
|
||||
return NULL;
|
||||
slash = strchr(start, '/');
|
||||
len = slash ? (size_t)(slash - start) : strlen(start);
|
||||
|
||||
strncpy(res, ptr, end - ptr);
|
||||
res[end - ptr] = '\0';
|
||||
if (len >= bufsz)
|
||||
len = bufsz - 1;
|
||||
|
||||
return res;
|
||||
memcpy(buf, start, len);
|
||||
buf[len] = '\0';
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
static int sr_iface_cb(sr_session_ctx_t *session, uint32_t, const char *model,
|
||||
static int sr_iface_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
const char *, const char *xpath, uint32_t,
|
||||
struct lyd_node **parent, __attribute__((unused)) void *priv)
|
||||
{
|
||||
char *yanger_args[5] = {
|
||||
YANGER_BINPATH,
|
||||
(char *)model,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
char *ifname = NULL;
|
||||
const struct ly_ctx *ctx;
|
||||
sr_conn_ctx_t *con;
|
||||
int err;
|
||||
@@ -184,29 +140,20 @@ static int sr_iface_cb(sr_session_ctx_t *session, uint32_t, const char *model,
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
ifname = xpath_extract(xpath, "[name='");
|
||||
if (ifname) {
|
||||
yanger_args[2] = "-p";
|
||||
yanger_args[3] = ifname;
|
||||
}
|
||||
err = ly_add_yanger_data(ctx, parent, yanger_args);
|
||||
err = ly_add_yangerd_data(ctx, parent, "ietf-interfaces:interfaces");
|
||||
if (err)
|
||||
ERROR("Error adding interface yanger data");
|
||||
ERROR("Error adding interface data");
|
||||
|
||||
sr_release_context(con);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
static int sr_generic_cb(sr_session_ctx_t *session, uint32_t, const char *model,
|
||||
static int sr_generic_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
const char *, const char *xpath, uint32_t,
|
||||
struct lyd_node **parent, __attribute__((unused)) void *priv)
|
||||
{
|
||||
char *yanger_args[5] = {
|
||||
YANGER_BINPATH,
|
||||
(char *)model,
|
||||
NULL
|
||||
};
|
||||
char yangerd_path[XPATH_MAX];
|
||||
const struct ly_ctx *ctx;
|
||||
sr_conn_ctx_t *con;
|
||||
sr_error_t err;
|
||||
@@ -225,9 +172,10 @@ static int sr_generic_cb(sr_session_ctx_t *session, uint32_t, const char *model,
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
err = ly_add_yanger_data(ctx, parent, yanger_args);
|
||||
xpath_to_yangerd_path(xpath, yangerd_path, sizeof(yangerd_path));
|
||||
err = ly_add_yangerd_data(ctx, parent, yangerd_path);
|
||||
if (err)
|
||||
ERROR("Error adding yanger data");
|
||||
ERROR("Error adding data for %s", yangerd_path);
|
||||
|
||||
sr_release_context(con);
|
||||
|
||||
@@ -238,11 +186,6 @@ static int sr_ospf_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
const char *, const char *xpath, uint32_t,
|
||||
struct lyd_node **parent, __attribute__((unused)) void *priv)
|
||||
{
|
||||
char *yanger_args[5] = {
|
||||
YANGER_BINPATH,
|
||||
"ietf-ospf",
|
||||
NULL
|
||||
};
|
||||
const struct ly_ctx *ctx;
|
||||
sr_conn_ctx_t *con;
|
||||
sr_error_t err;
|
||||
@@ -261,9 +204,9 @@ static int sr_ospf_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
err = ly_add_yanger_data(ctx, parent, yanger_args);
|
||||
err = ly_add_yangerd_data(ctx, parent, "ietf-routing:routing");
|
||||
if (err)
|
||||
ERROR("Error adding yanger data");
|
||||
ERROR("Error adding OSPF data");
|
||||
|
||||
sr_release_context(con);
|
||||
|
||||
@@ -274,11 +217,6 @@ static int sr_rip_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
const char *, const char *xpath, uint32_t,
|
||||
struct lyd_node **parent, __attribute__((unused)) void *priv)
|
||||
{
|
||||
char *yanger_args[5] = {
|
||||
YANGER_BINPATH,
|
||||
"ietf-rip",
|
||||
NULL
|
||||
};
|
||||
const struct ly_ctx *ctx;
|
||||
sr_conn_ctx_t *con;
|
||||
sr_error_t err;
|
||||
@@ -297,9 +235,9 @@ static int sr_rip_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
err = ly_add_yanger_data(ctx, parent, yanger_args);
|
||||
err = ly_add_yangerd_data(ctx, parent, "ietf-routing:routing");
|
||||
if (err)
|
||||
ERROR("Error adding yanger data");
|
||||
ERROR("Error adding RIP data");
|
||||
|
||||
sr_release_context(con);
|
||||
|
||||
@@ -310,11 +248,6 @@ static int sr_bfd_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
const char *, const char *xpath, uint32_t,
|
||||
struct lyd_node **parent, __attribute__((unused)) void *priv)
|
||||
{
|
||||
char *yanger_args[5] = {
|
||||
YANGER_BINPATH,
|
||||
"ietf-bfd-ip-sh",
|
||||
NULL
|
||||
};
|
||||
const struct ly_ctx *ctx;
|
||||
sr_conn_ctx_t *con;
|
||||
sr_error_t err;
|
||||
@@ -333,9 +266,9 @@ static int sr_bfd_cb(sr_session_ctx_t *session, uint32_t, const char *,
|
||||
return SR_ERR_INTERNAL;
|
||||
}
|
||||
|
||||
err = ly_add_yanger_data(ctx, parent, yanger_args);
|
||||
err = ly_add_yangerd_data(ctx, parent, "ietf-routing:routing");
|
||||
if (err)
|
||||
ERROR("Error adding yanger data");
|
||||
ERROR("Error adding BFD data");
|
||||
|
||||
sr_release_context(con);
|
||||
|
||||
|
||||
@@ -0,0 +1,246 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
|
||||
#include <jansson.h>
|
||||
#include <srx/common.h>
|
||||
|
||||
#include "yangerd.h"
|
||||
|
||||
static const char *yangerd_socket_path(void)
|
||||
{
|
||||
const char *env;
|
||||
|
||||
env = getenv("YANGERD_SOCKET");
|
||||
if (env && *env)
|
||||
return env;
|
||||
|
||||
return YANGERD_SOCKET_DEFAULT;
|
||||
}
|
||||
|
||||
static int yangerd_connect(void)
|
||||
{
|
||||
struct sockaddr_un addr = { .sun_family = AF_UNIX };
|
||||
struct timeval tv = { .tv_sec = YANGERD_TIMEOUT_SEC };
|
||||
const char *path;
|
||||
int fd;
|
||||
|
||||
path = yangerd_socket_path();
|
||||
if (strlen(path) >= sizeof(addr.sun_path)) {
|
||||
ERROR("yangerd socket path too long: %s", path);
|
||||
return -1;
|
||||
}
|
||||
strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
|
||||
|
||||
fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0) {
|
||||
ERROR("yangerd: socket(): %s", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
|
||||
setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
DEBUG("yangerd: connect(%s): %s", path, strerror(errno));
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int yangerd_write_all(int fd, const void *buf, size_t len)
|
||||
{
|
||||
const unsigned char *p = buf;
|
||||
|
||||
while (len > 0) {
|
||||
ssize_t n = write(fd, p, len);
|
||||
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
p += n;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yangerd_read_all(int fd, void *buf, size_t len)
|
||||
{
|
||||
unsigned char *p = buf;
|
||||
|
||||
while (len > 0) {
|
||||
ssize_t n = read(fd, p, len);
|
||||
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
return -1;
|
||||
}
|
||||
if (n == 0) {
|
||||
errno = ECONNRESET;
|
||||
return -1;
|
||||
}
|
||||
p += n;
|
||||
len -= n;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int yangerd_send_request(int fd, const char *path)
|
||||
{
|
||||
json_t *req;
|
||||
char *json_str;
|
||||
size_t json_len;
|
||||
unsigned char hdr[5];
|
||||
int rc = -1;
|
||||
|
||||
req = json_pack("{s:s, s:s}", "method", "get", "path", path);
|
||||
if (!req)
|
||||
return -1;
|
||||
|
||||
json_str = json_dumps(req, JSON_COMPACT);
|
||||
json_decref(req);
|
||||
if (!json_str)
|
||||
return -1;
|
||||
|
||||
json_len = strlen(json_str);
|
||||
if (json_len > YANGERD_MAX_PAYLOAD) {
|
||||
free(json_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
hdr[0] = YANGERD_PROTO_VERSION;
|
||||
hdr[1] = (json_len >> 24) & 0xff;
|
||||
hdr[2] = (json_len >> 16) & 0xff;
|
||||
hdr[3] = (json_len >> 8) & 0xff;
|
||||
hdr[4] = (json_len >> 0) & 0xff;
|
||||
|
||||
if (yangerd_write_all(fd, hdr, sizeof(hdr)) < 0)
|
||||
goto out;
|
||||
if (yangerd_write_all(fd, json_str, json_len) < 0)
|
||||
goto out;
|
||||
|
||||
rc = 0;
|
||||
out:
|
||||
free(json_str);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int yangerd_recv_response(int fd, char **buf, size_t *len)
|
||||
{
|
||||
unsigned char hdr[5];
|
||||
uint32_t payload_len;
|
||||
json_error_t jerr;
|
||||
json_t *resp;
|
||||
json_t *status;
|
||||
json_t *data;
|
||||
char *body;
|
||||
char *data_str;
|
||||
|
||||
*buf = NULL;
|
||||
*len = 0;
|
||||
|
||||
if (yangerd_read_all(fd, hdr, sizeof(hdr)) < 0)
|
||||
return -1;
|
||||
|
||||
if (hdr[0] != YANGERD_PROTO_VERSION) {
|
||||
ERROR("yangerd: protocol version mismatch: got %u, want %u",
|
||||
hdr[0], YANGERD_PROTO_VERSION);
|
||||
return -1;
|
||||
}
|
||||
|
||||
payload_len = ((uint32_t)hdr[1] << 24) |
|
||||
((uint32_t)hdr[2] << 16) |
|
||||
((uint32_t)hdr[3] << 8) |
|
||||
((uint32_t)hdr[4]);
|
||||
|
||||
if (payload_len > YANGERD_MAX_PAYLOAD) {
|
||||
ERROR("yangerd: payload too large: %u", payload_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
body = malloc(payload_len + 1);
|
||||
if (!body)
|
||||
return -1;
|
||||
|
||||
if (yangerd_read_all(fd, body, payload_len) < 0) {
|
||||
free(body);
|
||||
return -1;
|
||||
}
|
||||
body[payload_len] = '\0';
|
||||
|
||||
resp = json_loads(body, 0, &jerr);
|
||||
free(body);
|
||||
if (!resp) {
|
||||
ERROR("yangerd: invalid response JSON: %s", jerr.text);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = json_object_get(resp, "status");
|
||||
if (!json_is_string(status) || strcmp(json_string_value(status), "ok")) {
|
||||
json_t *msg = json_object_get(resp, "message");
|
||||
|
||||
ERROR("yangerd: request failed: %s",
|
||||
json_is_string(msg) ? json_string_value(msg) : "unknown");
|
||||
json_decref(resp);
|
||||
return -1;
|
||||
}
|
||||
|
||||
data = json_object_get(resp, "data");
|
||||
if (!data || json_is_null(data)) {
|
||||
json_decref(resp);
|
||||
*buf = strdup("{}");
|
||||
*len = 2;
|
||||
return 0;
|
||||
}
|
||||
|
||||
data_str = json_dumps(data, JSON_COMPACT);
|
||||
json_decref(resp);
|
||||
if (!data_str)
|
||||
return -1;
|
||||
|
||||
*buf = data_str;
|
||||
*len = strlen(data_str);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int yangerd_query(const char *path, char **buf, size_t *len)
|
||||
{
|
||||
int fd;
|
||||
int rc;
|
||||
|
||||
*buf = NULL;
|
||||
*len = 0;
|
||||
|
||||
fd = yangerd_connect();
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
if (yangerd_send_request(fd, path) < 0) {
|
||||
ERROR("yangerd: failed sending request for %s", path);
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = yangerd_recv_response(fd, buf, len);
|
||||
if (rc < 0)
|
||||
ERROR("yangerd: failed reading response for %s", path);
|
||||
|
||||
close(fd);
|
||||
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#ifndef STATD_YANGERD_H_
|
||||
#define STATD_YANGERD_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#define YANGERD_SOCKET_DEFAULT "/run/yangerd.sock"
|
||||
#define YANGERD_TIMEOUT_SEC 5
|
||||
#define YANGERD_MAX_PAYLOAD (4 << 20) /* 4 MiB, matches Go side */
|
||||
#define YANGERD_PROTO_VERSION 0x01
|
||||
|
||||
/**
|
||||
* yangerd_query() - Query yangerd daemon for operational YANG data
|
||||
* @path: YANG model path, e.g. "ietf-interfaces:interfaces"
|
||||
* @buf: Output pointer to malloc'd JSON string (caller must free)
|
||||
* @len: Output length of JSON data
|
||||
*
|
||||
* Connects to the yangerd Unix socket, sends a "get" request for @path,
|
||||
* reads the framed response, and extracts the "data" field as a JSON
|
||||
* string. The socket path defaults to %YANGERD_SOCKET_DEFAULT but can
|
||||
* be overridden with the YANGERD_SOCKET environment variable.
|
||||
*
|
||||
* Return: 0 on success, -1 on error (buf is set to NULL).
|
||||
*/
|
||||
int yangerd_query(const char *path, char **buf, size_t *len);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user