mirror of
https://github.com/kernelkit/infix.git
synced 2026-07-22 01:13:00 +02:00
confd: initial support for NTP server
Fixes #904 Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -40,6 +40,7 @@ confd_plugin_la_SOURCES = \
|
||||
if-wifi.c \
|
||||
keystore.c \
|
||||
system.c \
|
||||
ntp.c \
|
||||
syslog.c \
|
||||
factory-default.c \
|
||||
routing.c \
|
||||
|
||||
@@ -328,6 +328,10 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod
|
||||
if ((rc = keystore_change(session, config, diff, event, confd)))
|
||||
goto free_diff;
|
||||
|
||||
/* ietf-ntp */
|
||||
if ((rc = ntp_change(session, config, diff, event, confd)))
|
||||
goto free_diff;
|
||||
|
||||
/* infix-services */
|
||||
if ((rc = services_change(session, config, diff, event, confd)))
|
||||
goto free_diff;
|
||||
@@ -366,6 +370,20 @@ static int change_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *mod
|
||||
if ((rc = meta_change_cb(session, config, diff, event, confd)))
|
||||
goto free_diff;
|
||||
|
||||
/*
|
||||
* Manage chronyd service enable/disable state. Must be done
|
||||
* after both ietf-system:ntp and ietf-ntp have are done.
|
||||
*/
|
||||
if (event == SR_EV_DONE && config) {
|
||||
bool client = false;
|
||||
bool server = false;
|
||||
|
||||
client = srx_enabled(session, "/ietf-system:system/ntp/enabled");
|
||||
server = lydx_get_xpathf(config, "/ietf-ntp:ntp") != NULL;
|
||||
|
||||
systemf("initctl -nbq %s chronyd", client || server ? "enable" : "disable");
|
||||
}
|
||||
|
||||
if (cfg)
|
||||
sr_release_data(cfg);
|
||||
|
||||
@@ -454,6 +472,11 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
|
||||
ERROR("Failed to subscribe to ietf-netconf-acm");
|
||||
goto err;
|
||||
}
|
||||
rc = subscribe_model("ietf-ntp", &confd, 0);
|
||||
if (rc) {
|
||||
ERROR("Failed to subscribe to ietf-ntp");
|
||||
goto err;
|
||||
}
|
||||
rc = subscribe_model("infix-dhcp-client", &confd, 0);
|
||||
if (rc) {
|
||||
ERROR("Failed to subscribe to infix-dhcp-client");
|
||||
@@ -554,6 +577,10 @@ int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
|
||||
goto err;
|
||||
|
||||
rc = dhcp_server_candidate_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
rc = ntp_candidate_init(&confd);
|
||||
if (rc)
|
||||
goto err;
|
||||
/* YOUR_INIT GOES HERE */
|
||||
|
||||
@@ -247,4 +247,10 @@ int firewall_rpc_init(struct confd *confd);
|
||||
int firewall_candidate_init(struct confd *confd);
|
||||
int firewall_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd);
|
||||
|
||||
/* ntp.c */
|
||||
int ntp_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd);
|
||||
int ntp_cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *path, sr_event_t event, unsigned request_id, void *priv);
|
||||
int ntp_candidate_init(struct confd *confd);
|
||||
|
||||
#endif /* CONFD_CORE_H_ */
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/* SPDX-License-Identifier: BSD-3-Clause */
|
||||
|
||||
#include <assert.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#include <srx/common.h>
|
||||
#include <srx/lyx.h>
|
||||
#include <srx/srx_val.h>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
#define XPATH_NTP_ "/ietf-ntp:ntp"
|
||||
#define NTP_CONF "/etc/chrony/conf.d/ntp-server.conf"
|
||||
#define NTP_PREV NTP_CONF "-"
|
||||
#define NTP_NEXT NTP_CONF "+"
|
||||
|
||||
static int change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff,
|
||||
sr_event_t event, struct confd *confd)
|
||||
{
|
||||
struct lyd_node *ntp, *entry, *makestep, *refclock;
|
||||
const char *port;
|
||||
FILE *fp;
|
||||
|
||||
if (diff && !lydx_get_xpathf(diff, XPATH_NTP_))
|
||||
return SR_ERR_OK;
|
||||
|
||||
switch (event) {
|
||||
case SR_EV_ENABLED: /* first time, on register. */
|
||||
case SR_EV_CHANGE: /* regular change (copy cand running) */
|
||||
/* Generate next config */
|
||||
break;
|
||||
|
||||
case SR_EV_ABORT: /* User abort, or other plugin failed */
|
||||
(void)remove(NTP_NEXT);
|
||||
return SR_ERR_OK;
|
||||
|
||||
case SR_EV_DONE:
|
||||
/* Check if NTP container exists (presence container) */
|
||||
if (!lydx_get_xpathf(config, XPATH_NTP_)) {
|
||||
DEBUG("NTP server disabled, removing config");
|
||||
systemf("rm -f %s", NTP_CONF);
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
/* Check if passed validation in previous event */
|
||||
if (!fexist(NTP_NEXT))
|
||||
return SR_ERR_OK;
|
||||
|
||||
(void)remove(NTP_PREV);
|
||||
(void)rename(NTP_CONF, NTP_PREV);
|
||||
(void)rename(NTP_NEXT, NTP_CONF);
|
||||
|
||||
/* Reload chronyd to pick up new config */
|
||||
if (systemf("chronyc reload sources >/dev/null 2>&1"))
|
||||
ERRNO("Failed reloading chronyd sources");
|
||||
|
||||
systemf("initctl -nbq touch chronyd");
|
||||
return SR_ERR_OK;
|
||||
|
||||
default:
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
ntp = lydx_get_xpathf(config, XPATH_NTP_);
|
||||
if (!ntp)
|
||||
return SR_ERR_OK;
|
||||
|
||||
fp = fopen(NTP_NEXT, "w");
|
||||
if (!fp) {
|
||||
ERROR("Failed creating %s: %s", NTP_NEXT, strerror(errno));
|
||||
return SR_ERR_SYS;
|
||||
}
|
||||
|
||||
fprintf(fp, "# Generated by confd\n");
|
||||
fprintf(fp, "# This file configures chronyd as an NTP server\n\n");
|
||||
|
||||
/* Port configuration (optional) */
|
||||
port = lydx_get_cattr(ntp, "port");
|
||||
if (port) {
|
||||
fprintf(fp, "# Custom NTP port\n");
|
||||
fprintf(fp, "port %d\n\n", atoi(port));
|
||||
}
|
||||
|
||||
/* makestep configuration - allow clock stepping for fast initial sync */
|
||||
makestep = lydx_get_child(ntp, "makestep");
|
||||
if (makestep) {
|
||||
const char *threshold = lydx_get_cattr(makestep, "threshold");
|
||||
const char *limit = lydx_get_cattr(makestep, "limit");
|
||||
|
||||
fprintf(fp, "# Allow clock stepping for fast initial sync\n");
|
||||
fprintf(fp, "makestep %.1f %d\n\n", atof(threshold), atoi(limit));
|
||||
}
|
||||
|
||||
fprintf(fp, "# Upstream NTP servers and peers\n");
|
||||
LYX_LIST_FOR_EACH(lyd_child(ntp), entry, "unicast-configuration") {
|
||||
const char *address, *type, *minpoll, *maxpoll, *version, *srcport;
|
||||
const char *directive = NULL;
|
||||
bool prefer, burst, iburst;
|
||||
|
||||
address = lydx_get_cattr(entry, "address");
|
||||
type = lydx_get_cattr(entry, "type");
|
||||
minpoll = lydx_get_cattr(entry, "minpoll");
|
||||
maxpoll = lydx_get_cattr(entry, "maxpoll");
|
||||
version = lydx_get_cattr(entry, "version");
|
||||
srcport = lydx_get_cattr(entry, "port");
|
||||
prefer = lydx_get_bool(entry, "prefer");
|
||||
burst = lydx_get_bool(entry, "burst");
|
||||
iburst = lydx_get_bool(entry, "iburst");
|
||||
|
||||
if (type && strstr(type, "uc-server"))
|
||||
directive = "server";
|
||||
else if (type && strstr(type, "uc-peer"))
|
||||
directive = "peer";
|
||||
|
||||
if (directive && address) {
|
||||
fprintf(fp, "%s %s", directive, address);
|
||||
if (srcport)
|
||||
fprintf(fp, " port %s", srcport);
|
||||
if (iburst)
|
||||
fprintf(fp, " iburst");
|
||||
if (burst)
|
||||
fprintf(fp, " burst");
|
||||
if (prefer)
|
||||
fprintf(fp, " prefer");
|
||||
if (minpoll)
|
||||
fprintf(fp, " minpoll %s", minpoll);
|
||||
if (maxpoll)
|
||||
fprintf(fp, " maxpoll %s", maxpoll);
|
||||
if (version)
|
||||
fprintf(fp, " version %s", version);
|
||||
fprintf(fp, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(fp, "\n");
|
||||
|
||||
/* Reference clock (local stratum) - fallback time source */
|
||||
refclock = lydx_get_child(ntp, "refclock-master");
|
||||
if (refclock) {
|
||||
int stratum = atoi(lydx_get_cattr(refclock, "master-stratum"));
|
||||
|
||||
/* Only configure local clock if stratum is valid (1-15) */
|
||||
if (stratum >= 1 && stratum <= 15) {
|
||||
fprintf(fp, "# Local reference clock - fallback stratum %d source\n", stratum);
|
||||
fprintf(fp, "local stratum %d orphan\n\n", stratum);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enable NTP server mode - allow clients to query us
|
||||
*
|
||||
* Using 'allow' without arguments permits all clients.
|
||||
* In a future version with access-rules support, we could
|
||||
* restrict to specific subnets.
|
||||
*/
|
||||
fprintf(fp, "# Allow NTP clients to query this server\n");
|
||||
fprintf(fp, "allow\n\n");
|
||||
|
||||
/*
|
||||
* Enable RTC synchronization
|
||||
*
|
||||
* On Linux, the kernel will copy system time to the hardware RTC
|
||||
* every 11 minutes when the clock is synchronized. This keeps the
|
||||
* RTC accurate for the next boot, which is important for embedded
|
||||
* systems without continuous network connectivity.
|
||||
*/
|
||||
fprintf(fp, "# Synchronize system time to hardware RTC\n");
|
||||
fprintf(fp, "rtcsync\n");
|
||||
|
||||
fclose(fp);
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Inference callback for NTP server configuration
|
||||
*
|
||||
* When a user creates the /ietf-ntp:ntp presence container without
|
||||
* any configuration, we infer sensible defaults:
|
||||
*
|
||||
* - refclock-master container (stratum defaults to 16 per YANG model)
|
||||
* - makestep with threshold 1.0 and limit 3 (fast initial sync for embedded)
|
||||
*
|
||||
* This provides a usable NTP server configuration with just 'edit ntp' + 'leave'.
|
||||
*/
|
||||
static int cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *path, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
sr_val_t inferred_container = { .type = SR_CONTAINER_PRESENCE_T };
|
||||
size_t cnt = 0;
|
||||
|
||||
if (event != SR_EV_UPDATE && event != SR_EV_CHANGE)
|
||||
return SR_ERR_OK;
|
||||
|
||||
/* Check if NTP container exists */
|
||||
if (srx_nitems(session, &cnt, XPATH_NTP_) || !cnt)
|
||||
return SR_ERR_OK;
|
||||
|
||||
/* Check if refclock-master already configured */
|
||||
if (!srx_nitems(session, &cnt, XPATH_NTP_"/refclock-master") && cnt > 0)
|
||||
return SR_ERR_OK;
|
||||
|
||||
/* Check if unicast-configuration already configured */
|
||||
if (!srx_nitems(session, &cnt, XPATH_NTP_"/unicast-configuration") && cnt > 0)
|
||||
return SR_ERR_OK;
|
||||
|
||||
/* Infer refclock-master container (let YANG provide default stratum 16) */
|
||||
DEBUG("Inferring NTP refclock-master container");
|
||||
srx_set_item(session, &inferred_container, 0, XPATH_NTP_"/refclock-master");
|
||||
|
||||
/* Infer makestep for fast initial sync (critical for embedded systems) */
|
||||
if (!srx_nitems(session, &cnt, XPATH_NTP_"/infix-ntp:makestep") || cnt == 0) {
|
||||
DEBUG("Inferring NTP makestep container");
|
||||
/* Create presence container (let YANG provide defaults: threshold 1.0, limit 3) */
|
||||
srx_set_item(session, &inferred_container, 0, XPATH_NTP_"/infix-ntp:makestep");
|
||||
}
|
||||
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
int ntp_change(sr_session_ctx_t *session, struct lyd_node *config,
|
||||
struct lyd_node *diff, sr_event_t event, struct confd *confd)
|
||||
{
|
||||
return change(session, config, diff, event, confd);
|
||||
}
|
||||
|
||||
int ntp_cand(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
|
||||
const char *path, sr_event_t event, unsigned request_id, void *priv)
|
||||
{
|
||||
return cand(session, sub_id, module, path, event, request_id, priv);
|
||||
}
|
||||
|
||||
int ntp_candidate_init(struct confd *confd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
REGISTER_CHANGE(confd->cand, "ietf-ntp", XPATH_NTP_ "//.", SR_SUBSCR_UPDATE,
|
||||
ntp_cand, confd, &confd->sub);
|
||||
|
||||
return SR_ERR_OK;
|
||||
fail:
|
||||
ERROR("init failed: %s", sr_strerror(rc));
|
||||
return rc;
|
||||
}
|
||||
@@ -283,7 +283,7 @@ err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int change_ntp(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd)
|
||||
static int change_ntp_client(sr_session_ctx_t *session, struct lyd_node *config, struct lyd_node *diff, sr_event_t event, struct confd *confd)
|
||||
{
|
||||
sr_change_iter_t *iter = NULL;
|
||||
int rc, err = SR_ERR_OK;
|
||||
@@ -305,7 +305,8 @@ static int change_ntp(sr_session_ctx_t *session, struct lyd_node *config, struct
|
||||
case SR_EV_DONE:
|
||||
if (!srx_enabled(session, XPATH_NTP_"/enabled")) {
|
||||
systemf("rm -rf /etc/chrony/conf.d/* /etc/chrony/sources.d/*");
|
||||
systemf("initctl -nbq disable chronyd");
|
||||
/* Note: chronyd enable/disable is managed centrally in core.c */
|
||||
systemf("initctl -nbq touch chronyd");
|
||||
return SR_ERR_OK;
|
||||
}
|
||||
|
||||
@@ -314,7 +315,8 @@ static int change_ntp(sr_session_ctx_t *session, struct lyd_node *config, struct
|
||||
erase("/run/chrony/.changes");
|
||||
}
|
||||
|
||||
systemf("initctl -nbq enable chronyd");
|
||||
/* Note: chronyd enable/disable is managed centrally in core.c */
|
||||
systemf("initctl -nbq touch chronyd");
|
||||
return SR_ERR_OK;
|
||||
|
||||
default:
|
||||
@@ -1608,7 +1610,7 @@ int system_change(sr_session_ctx_t *session, struct lyd_node *config, struct lyd
|
||||
|
||||
if ((rc = change_auth(session, config, diff, event, confd)))
|
||||
return rc;
|
||||
if ((rc = change_ntp(session, config, diff, event, confd)))
|
||||
if ((rc = change_ntp_client(session, config, diff, event, confd)))
|
||||
return rc;
|
||||
if ((rc = change_dns(session, config, diff, event, confd)))
|
||||
return rc;
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
MODULES=(
|
||||
"ietf-system@2014-08-06.yang -e authentication -e local-users -e ntp -e ntp-udp-port -e timezone-name"
|
||||
"iana-timezones@2013-11-19.yang"
|
||||
"ietf-ntp@2022-07-05.yang -e ntp-port -e unicast-configuration"
|
||||
"ietf-access-control-list@2019-03-04.yang"
|
||||
"ietf-packet-fields@2019-03-04.yang"
|
||||
"ietf-ethertypes@2019-03-04.yang"
|
||||
"ietf-interfaces@2018-02-20.yang -e if-mib"
|
||||
"ietf-ip@2018-02-22.yang -e ipv6-privacy-autoconf"
|
||||
"ietf-network-instance@2019-01-21.yang"
|
||||
@@ -48,4 +52,5 @@ MODULES=(
|
||||
"infix-crypto-types@2025-06-17.yang"
|
||||
"ietf-keystore -e symmetric-keys"
|
||||
"infix-keystore@2025-12-10.yang"
|
||||
"infix-ntp@2025-12-03.yang"
|
||||
)
|
||||
|
||||
@@ -0,0 +1,668 @@
|
||||
module ietf-access-control-list {
|
||||
yang-version 1.1;
|
||||
namespace "urn:ietf:params:xml:ns:yang:ietf-access-control-list";
|
||||
prefix acl;
|
||||
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
reference
|
||||
"RFC 6991 - Common YANG Data Types.";
|
||||
}
|
||||
|
||||
import ietf-packet-fields {
|
||||
prefix pf;
|
||||
reference
|
||||
"RFC 8519 - YANG Data Model for Network Access Control
|
||||
Lists (ACLs).";
|
||||
}
|
||||
|
||||
import ietf-interfaces {
|
||||
prefix if;
|
||||
reference
|
||||
"RFC 8343 - A YANG Data Model for Interface Management.";
|
||||
}
|
||||
|
||||
organization
|
||||
"IETF NETMOD (Network Modeling) Working Group.";
|
||||
|
||||
contact
|
||||
"WG Web: <https://datatracker.ietf.org/wg/netmod/>
|
||||
WG List: netmod@ietf.org
|
||||
|
||||
Editor: Mahesh Jethanandani
|
||||
mjethanandani@gmail.com
|
||||
Editor: Lisa Huang
|
||||
huangyi_99@yahoo.com
|
||||
Editor: Sonal Agarwal
|
||||
sagarwal12@gmail.com
|
||||
Editor: Dana Blair
|
||||
dana@blairhome.com";
|
||||
|
||||
description
|
||||
"This YANG module defines a component that describes the
|
||||
configuration and monitoring of Access Control Lists (ACLs).
|
||||
|
||||
The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL',
|
||||
'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED',
|
||||
'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document
|
||||
are to be interpreted as described in BCP 14 (RFC 2119)
|
||||
(RFC 8174) when, and only when, they appear in all
|
||||
capitals, as shown here.
|
||||
|
||||
Copyright (c) 2019 IETF Trust and the persons identified as
|
||||
the document authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, is permitted pursuant to, and subject
|
||||
to the license terms contained in, the Simplified BSD
|
||||
License set forth in Section 4.c of the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info).
|
||||
|
||||
This version of this YANG module is part of RFC 8519; see
|
||||
the RFC itself for full legal notices.";
|
||||
|
||||
revision 2019-03-04 {
|
||||
description
|
||||
"Initial version.";
|
||||
reference
|
||||
"RFC 8519: YANG Data Model for Network Access Control
|
||||
Lists (ACLs).";
|
||||
}
|
||||
|
||||
/*
|
||||
* Identities
|
||||
*/
|
||||
/*
|
||||
* Forwarding actions for a packet
|
||||
*/
|
||||
|
||||
identity forwarding-action {
|
||||
description
|
||||
"Base identity for actions in the forwarding category.";
|
||||
}
|
||||
|
||||
identity accept {
|
||||
base forwarding-action;
|
||||
description
|
||||
"Accept the packet.";
|
||||
}
|
||||
|
||||
identity drop {
|
||||
base forwarding-action;
|
||||
description
|
||||
"Drop packet without sending any ICMP error message.";
|
||||
}
|
||||
|
||||
identity reject {
|
||||
base forwarding-action;
|
||||
description
|
||||
"Drop the packet and send an ICMP error message to the source.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Logging actions for a packet
|
||||
*/
|
||||
|
||||
identity log-action {
|
||||
description
|
||||
"Base identity for defining the destination for logging
|
||||
actions.";
|
||||
}
|
||||
|
||||
identity log-syslog {
|
||||
base log-action;
|
||||
description
|
||||
"System log (syslog) the information for the packet.";
|
||||
}
|
||||
identity log-none {
|
||||
base log-action;
|
||||
description
|
||||
"No logging for the packet.";
|
||||
}
|
||||
|
||||
/*
|
||||
* ACL type identities
|
||||
*/
|
||||
|
||||
identity acl-base {
|
||||
description
|
||||
"Base Access Control List type for all Access Control List type
|
||||
identifiers.";
|
||||
}
|
||||
|
||||
identity ipv4-acl-type {
|
||||
base acl:acl-base;
|
||||
if-feature "ipv4";
|
||||
description
|
||||
"An ACL that matches on fields from the IPv4 header
|
||||
(e.g., IPv4 destination address) and Layer 4 headers (e.g., TCP
|
||||
destination port). An ACL of type ipv4 does not contain
|
||||
matches on fields in the Ethernet header or the IPv6 header.";
|
||||
}
|
||||
|
||||
identity ipv6-acl-type {
|
||||
base acl:acl-base;
|
||||
if-feature "ipv6";
|
||||
description
|
||||
"An ACL that matches on fields from the IPv6 header
|
||||
(e.g., IPv6 destination address) and Layer 4 headers (e.g., TCP
|
||||
destination port). An ACL of type ipv6 does not contain
|
||||
matches on fields in the Ethernet header or the IPv4 header.";
|
||||
}
|
||||
|
||||
identity eth-acl-type {
|
||||
base acl:acl-base;
|
||||
if-feature "eth";
|
||||
description
|
||||
"An ACL that matches on fields in the Ethernet header,
|
||||
like 10/100/1000baseT or a Wi-Fi Access Control List. An ACL
|
||||
of type ethernet does not contain matches on fields in the
|
||||
IPv4 header, the IPv6 header, or Layer 4 headers.";
|
||||
}
|
||||
|
||||
identity mixed-eth-ipv4-acl-type {
|
||||
base acl:eth-acl-type;
|
||||
base acl:ipv4-acl-type;
|
||||
if-feature "mixed-eth-ipv4";
|
||||
description
|
||||
"An ACL that contains a mix of entries that match
|
||||
on fields in Ethernet headers and in IPv4 headers.
|
||||
Matching on Layer 4 header fields may also exist in the
|
||||
list.";
|
||||
}
|
||||
|
||||
identity mixed-eth-ipv6-acl-type {
|
||||
base acl:eth-acl-type;
|
||||
base acl:ipv6-acl-type;
|
||||
if-feature "mixed-eth-ipv6";
|
||||
description
|
||||
"An ACL that contains a mix of entries that match on fields
|
||||
in Ethernet headers and in IPv6 headers. Matching
|
||||
on Layer 4 header fields may also exist in the list.";
|
||||
}
|
||||
|
||||
identity mixed-eth-ipv4-ipv6-acl-type {
|
||||
base acl:eth-acl-type;
|
||||
base acl:ipv4-acl-type;
|
||||
base acl:ipv6-acl-type;
|
||||
if-feature "mixed-eth-ipv4-ipv6";
|
||||
description
|
||||
"An ACL that contains a mix of entries that
|
||||
match on fields in Ethernet headers, IPv4 headers, and IPv6
|
||||
headers. Matching on Layer 4 header fields may also exist
|
||||
in the list.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Features
|
||||
*/
|
||||
|
||||
/*
|
||||
* Features supported by device
|
||||
*/
|
||||
feature match-on-eth {
|
||||
description
|
||||
"The device can support matching on Ethernet headers.";
|
||||
}
|
||||
|
||||
feature match-on-ipv4 {
|
||||
description
|
||||
"The device can support matching on IPv4 headers.";
|
||||
}
|
||||
|
||||
feature match-on-ipv6 {
|
||||
description
|
||||
"The device can support matching on IPv6 headers.";
|
||||
}
|
||||
|
||||
feature match-on-tcp {
|
||||
description
|
||||
"The device can support matching on TCP headers.";
|
||||
}
|
||||
|
||||
feature match-on-udp {
|
||||
description
|
||||
"The device can support matching on UDP headers.";
|
||||
}
|
||||
|
||||
feature match-on-icmp {
|
||||
description
|
||||
"The device can support matching on ICMP (v4 and v6) headers.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Header classifications combinations supported by
|
||||
* device
|
||||
*/
|
||||
|
||||
feature eth {
|
||||
if-feature "match-on-eth";
|
||||
description
|
||||
"Plain Ethernet ACL supported.";
|
||||
}
|
||||
|
||||
feature ipv4 {
|
||||
if-feature "match-on-ipv4";
|
||||
description
|
||||
"Plain IPv4 ACL supported.";
|
||||
}
|
||||
|
||||
feature ipv6 {
|
||||
if-feature "match-on-ipv6";
|
||||
description
|
||||
"Plain IPv6 ACL supported.";
|
||||
}
|
||||
|
||||
feature mixed-eth-ipv4 {
|
||||
if-feature "match-on-eth and match-on-ipv4";
|
||||
description
|
||||
"Ethernet and IPv4 ACL combinations supported.";
|
||||
}
|
||||
feature mixed-eth-ipv6 {
|
||||
if-feature "match-on-eth and match-on-ipv6";
|
||||
description
|
||||
"Ethernet and IPv6 ACL combinations supported.";
|
||||
}
|
||||
|
||||
feature mixed-eth-ipv4-ipv6 {
|
||||
if-feature
|
||||
"match-on-eth and match-on-ipv4
|
||||
and match-on-ipv6";
|
||||
description
|
||||
"Ethernet, IPv4, and IPv6 ACL combinations supported.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Stats Features
|
||||
*/
|
||||
feature interface-stats {
|
||||
description
|
||||
"ACL counters are available and reported only per interface.";
|
||||
}
|
||||
|
||||
feature acl-aggregate-stats {
|
||||
description
|
||||
"ACL counters are aggregated over all interfaces and reported
|
||||
only per ACL entry.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Attachment point features
|
||||
*/
|
||||
feature interface-attachment {
|
||||
description
|
||||
"ACLs are set on interfaces.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
typedef acl-type {
|
||||
type identityref {
|
||||
base acl-base;
|
||||
}
|
||||
description
|
||||
"This type is used to refer to an ACL type.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Groupings
|
||||
*/
|
||||
grouping acl-counters {
|
||||
description
|
||||
"Common grouping for ACL counters.";
|
||||
leaf matched-packets {
|
||||
type yang:counter64;
|
||||
config false;
|
||||
description
|
||||
"Count of the number of packets matching the current ACL
|
||||
entry.
|
||||
|
||||
An implementation should provide this counter on a
|
||||
per-interface, per-ACL-entry basis if possible.
|
||||
|
||||
If an implementation only supports ACL counters on a per-
|
||||
entry basis (i.e., not broken out per interface), then the
|
||||
value should be equal to the aggregate count across all
|
||||
interfaces.
|
||||
|
||||
An implementation that provides counters on a per-entry, per-
|
||||
interface basis is not required to also provide an aggregate
|
||||
count, e.g., per entry -- the user is expected to be able to
|
||||
implement the required aggregation if such a count is
|
||||
needed.";
|
||||
}
|
||||
|
||||
leaf matched-octets {
|
||||
type yang:counter64;
|
||||
config false;
|
||||
description
|
||||
"Count of the number of octets (bytes) matching the current
|
||||
ACL entry.
|
||||
|
||||
An implementation should provide this counter on a
|
||||
per-interface, per-ACL-entry basis if possible.
|
||||
|
||||
If an implementation only supports ACL counters per entry
|
||||
(i.e., not broken out per interface), then the value
|
||||
should be equal to the aggregate count across all interfaces.
|
||||
|
||||
An implementation that provides counters per entry per
|
||||
interface is not required to also provide an aggregate count,
|
||||
e.g., per entry -- the user is expected to be able to
|
||||
implement the required aggregation if such a count is needed.";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Configuration and monitoring data nodes
|
||||
*/
|
||||
|
||||
container acls {
|
||||
description
|
||||
"This is a top-level container for Access Control Lists.
|
||||
It can have one or more acl nodes.";
|
||||
list acl {
|
||||
key "name";
|
||||
description
|
||||
"An ACL is an ordered list of ACEs. Each ACE has a
|
||||
list of match criteria and a list of actions.
|
||||
Since there are several kinds of ACLs implemented
|
||||
with different attributes for different vendors,
|
||||
this model accommodates customizing ACLs for
|
||||
each kind and for each vendor.";
|
||||
leaf name {
|
||||
type string {
|
||||
length "1..64";
|
||||
}
|
||||
description
|
||||
"The name of the access list. A device MAY further
|
||||
restrict the length of this name; space and special
|
||||
characters are not allowed.";
|
||||
}
|
||||
leaf type {
|
||||
type acl-type;
|
||||
description
|
||||
"Type of ACL. Indicates the primary intended
|
||||
type of match criteria (e.g., Ethernet, IPv4, IPv6, mixed,
|
||||
etc.) used in the list instance.";
|
||||
}
|
||||
container aces {
|
||||
description
|
||||
"The aces container contains one or more ACE nodes.";
|
||||
list ace {
|
||||
key "name";
|
||||
ordered-by user;
|
||||
description
|
||||
"List of ACEs.";
|
||||
leaf name {
|
||||
type string {
|
||||
length "1..64";
|
||||
}
|
||||
description
|
||||
"A unique name identifying this ACE.";
|
||||
}
|
||||
container matches {
|
||||
description
|
||||
"The rules in this set determine what fields will be
|
||||
matched upon before any action is taken on them.
|
||||
The rules are selected based on the feature set
|
||||
defined by the server and the acl-type defined.
|
||||
If no matches are defined in a particular container,
|
||||
then any packet will match that container. If no
|
||||
matches are specified at all in an ACE, then any
|
||||
packet will match the ACE.";
|
||||
|
||||
choice l2 {
|
||||
container eth {
|
||||
when "derived-from-or-self(/acls/acl/type, "
|
||||
+ "'acl:eth-acl-type')";
|
||||
if-feature "match-on-eth";
|
||||
uses pf:acl-eth-header-fields;
|
||||
description
|
||||
"Rule set that matches Ethernet headers.";
|
||||
}
|
||||
description
|
||||
"Match Layer 2 headers, for example, Ethernet
|
||||
header fields.";
|
||||
}
|
||||
|
||||
choice l3 {
|
||||
container ipv4 {
|
||||
when "derived-from-or-self(/acls/acl/type, "
|
||||
+ "'acl:ipv4-acl-type')";
|
||||
if-feature "match-on-ipv4";
|
||||
uses pf:acl-ip-header-fields;
|
||||
uses pf:acl-ipv4-header-fields;
|
||||
description
|
||||
"Rule set that matches IPv4 headers.";
|
||||
}
|
||||
|
||||
container ipv6 {
|
||||
when "derived-from-or-self(/acls/acl/type, "
|
||||
+ "'acl:ipv6-acl-type')";
|
||||
if-feature "match-on-ipv6";
|
||||
uses pf:acl-ip-header-fields;
|
||||
uses pf:acl-ipv6-header-fields;
|
||||
description
|
||||
"Rule set that matches IPv6 headers.";
|
||||
}
|
||||
description
|
||||
"Choice of either IPv4 or IPv6 headers";
|
||||
}
|
||||
choice l4 {
|
||||
container tcp {
|
||||
if-feature "match-on-tcp";
|
||||
uses pf:acl-tcp-header-fields;
|
||||
container source-port {
|
||||
choice source-port {
|
||||
case range-or-operator {
|
||||
uses pf:port-range-or-operator;
|
||||
description
|
||||
"Source port definition from range or
|
||||
operator.";
|
||||
}
|
||||
description
|
||||
"Choice of source port definition using
|
||||
range/operator or a choice to support future
|
||||
'case' statements, such as one enabling a
|
||||
group of source ports to be referenced.";
|
||||
}
|
||||
description
|
||||
"Source port definition.";
|
||||
}
|
||||
container destination-port {
|
||||
choice destination-port {
|
||||
case range-or-operator {
|
||||
uses pf:port-range-or-operator;
|
||||
description
|
||||
"Destination port definition from range or
|
||||
operator.";
|
||||
}
|
||||
description
|
||||
"Choice of destination port definition using
|
||||
range/operator or a choice to support future
|
||||
'case' statements, such as one enabling a
|
||||
group of destination ports to be referenced.";
|
||||
}
|
||||
description
|
||||
"Destination port definition.";
|
||||
}
|
||||
description
|
||||
"Rule set that matches TCP headers.";
|
||||
}
|
||||
|
||||
container udp {
|
||||
if-feature "match-on-udp";
|
||||
uses pf:acl-udp-header-fields;
|
||||
container source-port {
|
||||
choice source-port {
|
||||
case range-or-operator {
|
||||
uses pf:port-range-or-operator;
|
||||
description
|
||||
"Source port definition from range or
|
||||
operator.";
|
||||
}
|
||||
description
|
||||
"Choice of source port definition using
|
||||
range/operator or a choice to support future
|
||||
'case' statements, such as one enabling a
|
||||
group of source ports to be referenced.";
|
||||
}
|
||||
description
|
||||
"Source port definition.";
|
||||
}
|
||||
container destination-port {
|
||||
choice destination-port {
|
||||
case range-or-operator {
|
||||
uses pf:port-range-or-operator;
|
||||
description
|
||||
"Destination port definition from range or
|
||||
operator.";
|
||||
}
|
||||
description
|
||||
"Choice of destination port definition using
|
||||
range/operator or a choice to support future
|
||||
'case' statements, such as one enabling a
|
||||
group of destination ports to be referenced.";
|
||||
}
|
||||
description
|
||||
"Destination port definition.";
|
||||
}
|
||||
description
|
||||
"Rule set that matches UDP headers.";
|
||||
}
|
||||
|
||||
container icmp {
|
||||
if-feature "match-on-icmp";
|
||||
uses pf:acl-icmp-header-fields;
|
||||
description
|
||||
"Rule set that matches ICMP headers.";
|
||||
}
|
||||
description
|
||||
"Choice of TCP, UDP, or ICMP headers.";
|
||||
}
|
||||
|
||||
leaf egress-interface {
|
||||
type if:interface-ref;
|
||||
description
|
||||
"Egress interface. This should not be used if this ACL
|
||||
is attached as an egress ACL (or the value should
|
||||
equal the interface to which the ACL is attached).";
|
||||
}
|
||||
|
||||
leaf ingress-interface {
|
||||
type if:interface-ref;
|
||||
description
|
||||
"Ingress interface. This should not be used if this ACL
|
||||
is attached as an ingress ACL (or the value should
|
||||
equal the interface to which the ACL is attached).";
|
||||
}
|
||||
}
|
||||
|
||||
container actions {
|
||||
description
|
||||
"Definition of actions for this ace entry.";
|
||||
leaf forwarding {
|
||||
type identityref {
|
||||
base forwarding-action;
|
||||
}
|
||||
mandatory true;
|
||||
description
|
||||
"Specifies the forwarding action per ace entry.";
|
||||
}
|
||||
|
||||
leaf logging {
|
||||
type identityref {
|
||||
base log-action;
|
||||
}
|
||||
default "log-none";
|
||||
description
|
||||
"Specifies the log action and destination for
|
||||
matched packets. Default value is not to log the
|
||||
packet.";
|
||||
}
|
||||
}
|
||||
container statistics {
|
||||
if-feature "acl-aggregate-stats";
|
||||
config false;
|
||||
description
|
||||
"Statistics gathered across all attachment points for the
|
||||
given ACL.";
|
||||
uses acl-counters;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
container attachment-points {
|
||||
description
|
||||
"Enclosing container for the list of
|
||||
attachment points on which ACLs are set.";
|
||||
/*
|
||||
* Groupings
|
||||
*/
|
||||
grouping interface-acl {
|
||||
description
|
||||
"Grouping for per-interface ingress ACL data.";
|
||||
container acl-sets {
|
||||
description
|
||||
"Enclosing container for the list of ingress ACLs on the
|
||||
interface.";
|
||||
list acl-set {
|
||||
key "name";
|
||||
ordered-by user;
|
||||
description
|
||||
"List of ingress ACLs on the interface.";
|
||||
leaf name {
|
||||
type leafref {
|
||||
path "/acls/acl/name";
|
||||
}
|
||||
description
|
||||
"Reference to the ACL name applied on the ingress.";
|
||||
}
|
||||
list ace-statistics {
|
||||
if-feature "interface-stats";
|
||||
key "name";
|
||||
config false;
|
||||
description
|
||||
"List of ACEs.";
|
||||
leaf name {
|
||||
type leafref {
|
||||
path "/acls/acl/aces/ace/name";
|
||||
}
|
||||
description
|
||||
"Name of the ace entry.";
|
||||
}
|
||||
uses acl-counters;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list interface {
|
||||
if-feature "interface-attachment";
|
||||
key "interface-id";
|
||||
description
|
||||
"List of interfaces on which ACLs are set.";
|
||||
|
||||
leaf interface-id {
|
||||
type if:interface-ref;
|
||||
description
|
||||
"Reference to the interface id list key.";
|
||||
}
|
||||
|
||||
container ingress {
|
||||
uses interface-acl;
|
||||
description
|
||||
"The ACLs applied to the ingress interface.";
|
||||
}
|
||||
container egress {
|
||||
uses interface-acl;
|
||||
description
|
||||
"The ACLs applied to the egress interface.";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,381 @@
|
||||
module ietf-ethertypes {
|
||||
namespace "urn:ietf:params:xml:ns:yang:ietf-ethertypes";
|
||||
prefix ethertypes;
|
||||
|
||||
organization
|
||||
"IETF NETMOD (Network Modeling) Working Group.";
|
||||
|
||||
contact
|
||||
"WG Web: <https://datatracker.ietf.org/wg/netmod/>
|
||||
WG List: <mailto:netmod@ietf.org>
|
||||
|
||||
Editor: Mahesh Jethanandani
|
||||
<mjethanandani@gmail.com>";
|
||||
|
||||
description
|
||||
"This module contains common definitions for the
|
||||
Ethertype used by different modules. It is a
|
||||
placeholder module, till such time that IEEE
|
||||
starts a project to define these Ethertypes
|
||||
and publishes a standard.
|
||||
|
||||
At that time, this module can be deprecated.
|
||||
|
||||
Copyright (c) 2019 IETF Trust and the persons identified as
|
||||
the document authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, is permitted pursuant to, and subject
|
||||
to the license terms contained in, the Simplified BSD
|
||||
License set forth in Section 4.c of the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info).
|
||||
|
||||
This version of this YANG module is part of RFC 8519; see
|
||||
the RFC itself for full legal notices.";
|
||||
|
||||
revision 2019-03-04 {
|
||||
description
|
||||
"Initial revision.";
|
||||
reference
|
||||
"RFC 8519: YANG Data Model for Network Access Control
|
||||
Lists (ACLs).";
|
||||
}
|
||||
|
||||
typedef ethertype {
|
||||
type union {
|
||||
type uint16;
|
||||
type enumeration {
|
||||
enum ipv4 {
|
||||
value 2048;
|
||||
description
|
||||
"Internet Protocol version 4 (IPv4) with a
|
||||
hex value of 0x0800.";
|
||||
reference
|
||||
"RFC 791: Internet Protocol.";
|
||||
}
|
||||
enum arp {
|
||||
value 2054;
|
||||
description
|
||||
"Address Resolution Protocol (ARP) with a
|
||||
hex value of 0x0806.";
|
||||
reference
|
||||
"RFC 826: An Ethernet Address Resolution Protocol: Or
|
||||
Converting Network Protocol Addresses to 48.bit
|
||||
Ethernet Address for Transmission on Ethernet
|
||||
Hardware.";
|
||||
}
|
||||
enum wlan {
|
||||
value 2114;
|
||||
description
|
||||
"Wake-on-LAN. Hex value of 0x0842.";
|
||||
}
|
||||
enum trill {
|
||||
value 8947;
|
||||
description
|
||||
"Transparent Interconnection of Lots of Links.
|
||||
Hex value of 0x22F3.";
|
||||
reference
|
||||
"RFC 6325: Routing Bridges (RBridges): Base Protocol
|
||||
Specification.";
|
||||
}
|
||||
enum srp {
|
||||
value 8938;
|
||||
description
|
||||
"Stream Reservation Protocol. Hex value of
|
||||
0x22EA.";
|
||||
reference
|
||||
"IEEE 801.1Q-2011.";
|
||||
}
|
||||
enum decnet {
|
||||
value 24579;
|
||||
description
|
||||
"DECnet Phase IV. Hex value of 0x6003.";
|
||||
}
|
||||
enum rarp {
|
||||
value 32821;
|
||||
description
|
||||
"Reverse Address Resolution Protocol.
|
||||
Hex value 0x8035.";
|
||||
reference
|
||||
"RFC 903: A Reverse Address Resolution Protocol.";
|
||||
}
|
||||
enum appletalk {
|
||||
value 32923;
|
||||
description
|
||||
"Appletalk (Ethertalk). Hex value of 0x809B.";
|
||||
}
|
||||
enum aarp {
|
||||
value 33011;
|
||||
description
|
||||
"Appletalk Address Resolution Protocol. Hex value
|
||||
of 0x80F3.";
|
||||
}
|
||||
enum vlan {
|
||||
value 33024;
|
||||
description
|
||||
"VLAN-tagged frame (IEEE 802.1Q) and Shortest Path
|
||||
Bridging IEEE 802.1aq with Network-Network
|
||||
Interface (NNI) compatibility. Hex value of
|
||||
0x8100.";
|
||||
reference
|
||||
"IEEE 802.1Q.";
|
||||
}
|
||||
enum ipx {
|
||||
value 33079;
|
||||
description
|
||||
"Internetwork Packet Exchange (IPX). Hex value
|
||||
of 0x8137.";
|
||||
}
|
||||
enum qnx {
|
||||
value 33284;
|
||||
description
|
||||
"QNX Qnet. Hex value of 0x8204.";
|
||||
}
|
||||
enum ipv6 {
|
||||
value 34525;
|
||||
description
|
||||
"Internet Protocol Version 6 (IPv6). Hex value
|
||||
of 0x86DD.";
|
||||
reference
|
||||
"RFC 8200: Internet Protocol, Version 6 (IPv6)
|
||||
Specification
|
||||
RFC 8201: Path MTU Discovery for IP version 6.";
|
||||
}
|
||||
enum efc {
|
||||
value 34824;
|
||||
description
|
||||
"Ethernet flow control using pause frames.
|
||||
Hex value of 0x8808.";
|
||||
reference
|
||||
"IEEE 802.1Qbb.";
|
||||
}
|
||||
enum esp {
|
||||
value 34825;
|
||||
description
|
||||
"Ethernet Slow Protocol. Hex value of 0x8809.";
|
||||
reference
|
||||
"IEEE 802.3-2015.";
|
||||
}
|
||||
enum cobranet {
|
||||
value 34841;
|
||||
description
|
||||
"CobraNet. Hex value of 0x8819.";
|
||||
}
|
||||
enum mpls-unicast {
|
||||
value 34887;
|
||||
description
|
||||
"Multiprotocol Label Switching (MPLS) unicast traffic.
|
||||
Hex value of 0x8847.";
|
||||
reference
|
||||
"RFC 3031: Multiprotocol Label Switching Architecture.";
|
||||
}
|
||||
enum mpls-multicast {
|
||||
value 34888;
|
||||
description
|
||||
"MPLS multicast traffic. Hex value of 0x8848.";
|
||||
reference
|
||||
"RFC 3031: Multiprotocol Label Switching Architecture.";
|
||||
}
|
||||
enum pppoe-discovery {
|
||||
value 34915;
|
||||
description
|
||||
"Point-to-Point Protocol over Ethernet. Used during
|
||||
the discovery process. Hex value of 0x8863.";
|
||||
reference
|
||||
"RFC 2516: A Method for Transmitting PPP Over Ethernet
|
||||
(PPPoE).";
|
||||
}
|
||||
enum pppoe-session {
|
||||
value 34916;
|
||||
description
|
||||
"Point-to-Point Protocol over Ethernet. Used during
|
||||
session stage. Hex value of 0x8864.";
|
||||
reference
|
||||
"RFC 2516: A Method for Transmitting PPP Over Ethernet
|
||||
(PPPoE).";
|
||||
}
|
||||
enum intel-ans {
|
||||
value 34925;
|
||||
description
|
||||
"Intel Advanced Networking Services. Hex value of
|
||||
0x886D.";
|
||||
}
|
||||
enum jumbo-frames {
|
||||
value 34928;
|
||||
description
|
||||
"Jumbo frames or Ethernet frames with more than
|
||||
1500 bytes of payload, up to 9000 bytes.";
|
||||
}
|
||||
enum homeplug {
|
||||
value 34939;
|
||||
description
|
||||
"Family name for the various power line
|
||||
communications. Hex value of 0x887B.";
|
||||
}
|
||||
enum eap {
|
||||
value 34958;
|
||||
description
|
||||
"Ethernet Access Protocol (EAP) over LAN. Hex value
|
||||
of 0x888E.";
|
||||
reference
|
||||
"IEEE 802.1X.";
|
||||
}
|
||||
enum profinet {
|
||||
value 34962;
|
||||
description
|
||||
"PROcess FIeld Net (PROFINET). Hex value of 0x8892.";
|
||||
}
|
||||
enum hyperscsi {
|
||||
value 34970;
|
||||
description
|
||||
"Small Computer System Interface (SCSI) over Ethernet.
|
||||
Hex value of 0x889A.";
|
||||
}
|
||||
enum aoe {
|
||||
value 34978;
|
||||
description
|
||||
"Advanced Technology Advancement (ATA) over Ethernet.
|
||||
Hex value of 0x88A2.";
|
||||
}
|
||||
enum ethercat {
|
||||
value 34980;
|
||||
description
|
||||
"Ethernet for Control Automation Technology (EtherCAT).
|
||||
Hex value of 0x88A4.";
|
||||
}
|
||||
enum provider-bridging {
|
||||
value 34984;
|
||||
description
|
||||
"Provider Bridging (802.1ad) and Shortest Path Bridging
|
||||
(801.1aq). Hex value of 0x88A8.";
|
||||
reference
|
||||
"IEEE 802.1ad and IEEE 802.1aq).";
|
||||
}
|
||||
enum ethernet-powerlink {
|
||||
value 34987;
|
||||
description
|
||||
"Ethernet Powerlink. Hex value of 0x88AB.";
|
||||
}
|
||||
enum goose {
|
||||
value 35000;
|
||||
description
|
||||
"Generic Object Oriented Substation Event (GOOSE).
|
||||
Hex value of 0x88B8.";
|
||||
reference
|
||||
"IEC/ISO 8802-2 and 8802-3.";
|
||||
}
|
||||
enum gse {
|
||||
value 35001;
|
||||
description
|
||||
"Generic Substation Events. Hex value of 88B9.";
|
||||
reference
|
||||
"IEC 61850.";
|
||||
}
|
||||
enum sv {
|
||||
value 35002;
|
||||
description
|
||||
"Sampled Value Transmission. Hex value of 0x88BA.";
|
||||
reference
|
||||
"IEC 61850.";
|
||||
}
|
||||
enum lldp {
|
||||
value 35020;
|
||||
description
|
||||
"Link Layer Discovery Protocol (LLDP). Hex value of
|
||||
0x88CC.";
|
||||
reference
|
||||
"IEEE 802.1AB.";
|
||||
}
|
||||
enum sercos {
|
||||
value 35021;
|
||||
description
|
||||
"Sercos Interface. Hex value of 0x88CD.";
|
||||
}
|
||||
enum wsmp {
|
||||
value 35036;
|
||||
description
|
||||
"WAVE Short Message Protocol (WSMP). Hex value of
|
||||
0x88DC.";
|
||||
}
|
||||
enum homeplug-av-mme {
|
||||
value 35041;
|
||||
description
|
||||
"HomePlug AV Mobile Management Entity (MME). Hex value
|
||||
of 88E1.";
|
||||
}
|
||||
enum mrp {
|
||||
value 35043;
|
||||
description
|
||||
"Media Redundancy Protocol (MRP). Hex value of
|
||||
0x88E3.";
|
||||
reference
|
||||
"IEC 62439-2.";
|
||||
}
|
||||
enum macsec {
|
||||
value 35045;
|
||||
description
|
||||
"MAC Security. Hex value of 0x88E5.";
|
||||
reference
|
||||
"IEEE 802.1AE.";
|
||||
}
|
||||
enum pbb {
|
||||
value 35047;
|
||||
description
|
||||
"Provider Backbone Bridges (PBB). Hex value of
|
||||
0x88E7.";
|
||||
reference
|
||||
"IEEE 802.1ah.";
|
||||
}
|
||||
enum cfm {
|
||||
value 35074;
|
||||
description
|
||||
"Connectivity Fault Management (CFM). Hex value of
|
||||
0x8902.";
|
||||
reference
|
||||
"IEEE 802.1ag.";
|
||||
}
|
||||
enum fcoe {
|
||||
value 35078;
|
||||
description
|
||||
"Fiber Channel over Ethernet (FCoE). Hex value of
|
||||
0x8906.";
|
||||
reference
|
||||
"T11 FC-BB-5.";
|
||||
}
|
||||
enum fcoe-ip {
|
||||
value 35092;
|
||||
description
|
||||
"FCoE Initialization Protocol. Hex value of 0x8914.";
|
||||
}
|
||||
enum roce {
|
||||
value 35093;
|
||||
description
|
||||
"RDMA over Converged Ethernet (RoCE). Hex value of
|
||||
0x8915.";
|
||||
}
|
||||
enum tte {
|
||||
value 35101;
|
||||
description
|
||||
"TTEthernet Protocol Control Frame (TTE). Hex value
|
||||
of 0x891D.";
|
||||
reference
|
||||
"SAE AS6802.";
|
||||
}
|
||||
enum hsr {
|
||||
value 35119;
|
||||
description
|
||||
"High-availability Seamless Redundancy (HSR). Hex
|
||||
value of 0x892F.";
|
||||
reference
|
||||
"IEC 62439-3:2016.";
|
||||
}
|
||||
}
|
||||
}
|
||||
description
|
||||
"The uint16 type placeholder is defined to enable
|
||||
users to manage their own ethertypes not
|
||||
covered by the module. Otherwise, the module contains
|
||||
enum definitions for the more commonly used ethertypes.";
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,576 @@
|
||||
module ietf-packet-fields {
|
||||
yang-version 1.1;
|
||||
namespace "urn:ietf:params:xml:ns:yang:ietf-packet-fields";
|
||||
prefix packet-fields;
|
||||
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
reference
|
||||
"RFC 6991 - Common YANG Data Types.";
|
||||
}
|
||||
|
||||
import ietf-yang-types {
|
||||
prefix yang;
|
||||
reference
|
||||
"RFC 6991 - Common YANG Data Types.";
|
||||
}
|
||||
|
||||
import ietf-ethertypes {
|
||||
prefix eth;
|
||||
reference
|
||||
"RFC 8519 - YANG Data Model for Network Access Control
|
||||
Lists (ACLs).";
|
||||
}
|
||||
|
||||
organization
|
||||
"IETF NETMOD (Network Modeling) Working Group.";
|
||||
|
||||
contact
|
||||
"WG Web: <https://datatracker.ietf.org/wg/netmod/>
|
||||
WG List: netmod@ietf.org
|
||||
|
||||
Editor: Mahesh Jethanandani
|
||||
mjethanandani@gmail.com
|
||||
Editor: Lisa Huang
|
||||
huangyi_99@yahoo.com
|
||||
Editor: Sonal Agarwal
|
||||
sagarwal12@gmail.com
|
||||
Editor: Dana Blair
|
||||
dana@blairhome.com";
|
||||
|
||||
description
|
||||
"This YANG module defines groupings that are used by
|
||||
the ietf-access-control-list YANG module. Their usage
|
||||
is not limited to ietf-access-control-list and can be
|
||||
used anywhere as applicable.
|
||||
|
||||
Copyright (c) 2019 IETF Trust and the persons identified as
|
||||
the document authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or
|
||||
without modification, is permitted pursuant to, and subject
|
||||
to the license terms contained in, the Simplified BSD
|
||||
License set forth in Section 4.c of the IETF Trust's Legal
|
||||
Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info).
|
||||
|
||||
This version of this YANG module is part of RFC 8519; see
|
||||
the RFC itself for full legal notices.";
|
||||
|
||||
revision 2019-03-04 {
|
||||
description
|
||||
"Initial version.";
|
||||
reference
|
||||
"RFC 8519: YANG Data Model for Network Access Control
|
||||
Lists (ACLs).";
|
||||
}
|
||||
|
||||
/*
|
||||
* Typedefs
|
||||
*/
|
||||
typedef operator {
|
||||
type enumeration {
|
||||
enum lte {
|
||||
description
|
||||
"Less than or equal to.";
|
||||
}
|
||||
enum gte {
|
||||
description
|
||||
"Greater than or equal to.";
|
||||
}
|
||||
enum eq {
|
||||
description
|
||||
"Equal to.";
|
||||
}
|
||||
enum neq {
|
||||
description
|
||||
"Not equal to.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"The source and destination port range definitions
|
||||
can be further qualified using an operator. An
|
||||
operator is needed only if the lower-port is specified
|
||||
and the upper-port is not specified. The operator
|
||||
therefore further qualifies the lower-port only.";
|
||||
}
|
||||
|
||||
/*
|
||||
* Groupings
|
||||
*/
|
||||
grouping port-range-or-operator {
|
||||
choice port-range-or-operator {
|
||||
case range {
|
||||
leaf lower-port {
|
||||
type inet:port-number;
|
||||
must '. <= ../upper-port' {
|
||||
error-message
|
||||
"The lower-port must be less than or equal to
|
||||
the upper-port.";
|
||||
}
|
||||
mandatory true;
|
||||
description
|
||||
"Lower boundary for a port.";
|
||||
}
|
||||
leaf upper-port {
|
||||
type inet:port-number;
|
||||
mandatory true;
|
||||
description
|
||||
"Upper boundary for a port.";
|
||||
}
|
||||
}
|
||||
case operator {
|
||||
leaf operator {
|
||||
type operator;
|
||||
default "eq";
|
||||
description
|
||||
"Operator to be applied on the port below.";
|
||||
}
|
||||
leaf port {
|
||||
type inet:port-number;
|
||||
mandatory true;
|
||||
description
|
||||
"Port number along with the operator on which to
|
||||
match.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Choice of specifying a port range or a single
|
||||
port along with an operator.";
|
||||
}
|
||||
description
|
||||
"Grouping for port definitions in the form of a
|
||||
choice statement.";
|
||||
}
|
||||
|
||||
grouping acl-ip-header-fields {
|
||||
description
|
||||
"IP header fields common to IPv4 and IPv6";
|
||||
reference
|
||||
"RFC 791: Internet Protocol.";
|
||||
|
||||
leaf dscp {
|
||||
type inet:dscp;
|
||||
description
|
||||
"Differentiated Services Code Point.";
|
||||
reference
|
||||
"RFC 2474: Definition of the Differentiated Services
|
||||
Field (DS Field) in the IPv4 and IPv6
|
||||
Headers.";
|
||||
}
|
||||
|
||||
leaf ecn {
|
||||
type uint8 {
|
||||
range "0..3";
|
||||
}
|
||||
description
|
||||
"Explicit Congestion Notification.";
|
||||
reference
|
||||
"RFC 3168: The Addition of Explicit Congestion
|
||||
Notification (ECN) to IP.";
|
||||
}
|
||||
|
||||
leaf length {
|
||||
type uint16;
|
||||
description
|
||||
"In the IPv4 header field, this field is known as the Total
|
||||
Length. Total Length is the length of the datagram, measured
|
||||
in octets, including internet header and data.
|
||||
|
||||
In the IPv6 header field, this field is known as the Payload
|
||||
Length, which is the length of the IPv6 payload, i.e., the rest
|
||||
of the packet following the IPv6 header, in octets.";
|
||||
reference
|
||||
"RFC 791: Internet Protocol
|
||||
RFC 8200: Internet Protocol, Version 6 (IPv6) Specification.";
|
||||
}
|
||||
leaf ttl {
|
||||
type uint8;
|
||||
description
|
||||
"This field indicates the maximum time the datagram is allowed
|
||||
to remain in the internet system. If this field contains the
|
||||
value zero, then the datagram must be dropped.
|
||||
|
||||
In IPv6, this field is known as the Hop Limit.";
|
||||
reference
|
||||
"RFC 791: Internet Protocol
|
||||
RFC 8200: Internet Protocol, Version 6 (IPv6) Specification.";
|
||||
}
|
||||
leaf protocol {
|
||||
type uint8;
|
||||
description
|
||||
"Internet Protocol number. Refers to the protocol of the
|
||||
payload. In IPv6, this field is known as 'next-header',
|
||||
and if extension headers are present, the protocol is
|
||||
present in the 'upper-layer' header.";
|
||||
reference
|
||||
"RFC 791: Internet Protocol
|
||||
RFC 8200: Internet Protocol, Version 6 (IPv6) Specification.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping acl-ipv4-header-fields {
|
||||
description
|
||||
"Fields in the IPv4 header.";
|
||||
leaf ihl {
|
||||
type uint8 {
|
||||
range "5..60";
|
||||
}
|
||||
description
|
||||
"In an IPv4 header field, the Internet Header Length (IHL) is
|
||||
the length of the internet header in 32-bit words and
|
||||
thus points to the beginning of the data. Note that the
|
||||
minimum value for a correct header is 5.";
|
||||
}
|
||||
leaf flags {
|
||||
type bits {
|
||||
bit reserved {
|
||||
position 0;
|
||||
description
|
||||
"Reserved. Must be zero.";
|
||||
}
|
||||
bit fragment {
|
||||
position 1;
|
||||
description
|
||||
"Setting the value to 0 indicates may fragment, while
|
||||
setting the value to 1 indicates do not fragment.";
|
||||
}
|
||||
bit more {
|
||||
position 2;
|
||||
description
|
||||
"Setting the value to 0 indicates this is the last fragment,
|
||||
and setting the value to 1 indicates more fragments are
|
||||
coming.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Bit definitions for the Flags field in the IPv4 header.";
|
||||
}
|
||||
leaf offset {
|
||||
type uint16 {
|
||||
range "20..65535";
|
||||
}
|
||||
description
|
||||
"The fragment offset is measured in units of 8 octets (64 bits).
|
||||
The first fragment has offset zero. The length is 13 bits";
|
||||
}
|
||||
leaf identification {
|
||||
type uint16;
|
||||
description
|
||||
"An identifying value assigned by the sender to aid in
|
||||
assembling the fragments of a datagram.";
|
||||
}
|
||||
|
||||
choice destination-network {
|
||||
case destination-ipv4-network {
|
||||
leaf destination-ipv4-network {
|
||||
type inet:ipv4-prefix;
|
||||
description
|
||||
"Destination IPv4 address prefix.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Choice of specifying a destination IPv4 address or
|
||||
referring to a group of IPv4 destination addresses.";
|
||||
}
|
||||
|
||||
choice source-network {
|
||||
case source-ipv4-network {
|
||||
leaf source-ipv4-network {
|
||||
type inet:ipv4-prefix;
|
||||
description
|
||||
"Source IPv4 address prefix.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Choice of specifying a source IPv4 address or
|
||||
referring to a group of IPv4 source addresses.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping acl-ipv6-header-fields {
|
||||
description
|
||||
"Fields in the IPv6 header.";
|
||||
|
||||
choice destination-network {
|
||||
case destination-ipv6-network {
|
||||
leaf destination-ipv6-network {
|
||||
type inet:ipv6-prefix;
|
||||
description
|
||||
"Destination IPv6 address prefix.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Choice of specifying a destination IPv6 address
|
||||
or referring to a group of IPv6 destination
|
||||
addresses.";
|
||||
}
|
||||
|
||||
choice source-network {
|
||||
case source-ipv6-network {
|
||||
leaf source-ipv6-network {
|
||||
type inet:ipv6-prefix;
|
||||
description
|
||||
"Source IPv6 address prefix.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Choice of specifying a source IPv6 address or
|
||||
referring to a group of IPv6 source addresses.";
|
||||
}
|
||||
|
||||
leaf flow-label {
|
||||
type inet:ipv6-flow-label;
|
||||
description
|
||||
"IPv6 Flow label.";
|
||||
}
|
||||
reference
|
||||
"RFC 4291: IP Version 6 Addressing Architecture
|
||||
RFC 4007: IPv6 Scoped Address Architecture
|
||||
RFC 5952: A Recommendation for IPv6 Address Text
|
||||
Representation.";
|
||||
}
|
||||
|
||||
grouping acl-eth-header-fields {
|
||||
description
|
||||
"Fields in the Ethernet header.";
|
||||
leaf destination-mac-address {
|
||||
type yang:mac-address;
|
||||
description
|
||||
"Destination IEEE 802 Media Access Control (MAC)
|
||||
address.";
|
||||
}
|
||||
leaf destination-mac-address-mask {
|
||||
type yang:mac-address;
|
||||
description
|
||||
"Destination IEEE 802 MAC address mask.";
|
||||
}
|
||||
leaf source-mac-address {
|
||||
type yang:mac-address;
|
||||
description
|
||||
"Source IEEE 802 MAC address.";
|
||||
}
|
||||
leaf source-mac-address-mask {
|
||||
type yang:mac-address;
|
||||
description
|
||||
"Source IEEE 802 MAC address mask.";
|
||||
}
|
||||
leaf ethertype {
|
||||
type eth:ethertype;
|
||||
description
|
||||
"The Ethernet Type (or Length) value represented
|
||||
in the canonical order defined by IEEE 802.
|
||||
The canonical representation uses lowercase
|
||||
characters.";
|
||||
reference
|
||||
"IEEE 802-2014, Clause 9.2.";
|
||||
}
|
||||
reference
|
||||
"IEEE 802: IEEE Standard for Local and Metropolitan
|
||||
Area Networks: Overview and Architecture.";
|
||||
}
|
||||
|
||||
grouping acl-tcp-header-fields {
|
||||
description
|
||||
"Collection of TCP header fields that can be used to
|
||||
set up a match filter.";
|
||||
leaf sequence-number {
|
||||
type uint32;
|
||||
description
|
||||
"Sequence number that appears in the packet.";
|
||||
}
|
||||
leaf acknowledgement-number {
|
||||
type uint32;
|
||||
description
|
||||
"The acknowledgement number that appears in the
|
||||
packet.";
|
||||
}
|
||||
leaf data-offset {
|
||||
type uint8 {
|
||||
range "5..15";
|
||||
}
|
||||
description
|
||||
"Specifies the size of the TCP header in 32-bit
|
||||
words. The minimum size header is 5 words and
|
||||
the maximum is 15 words; thus, this gives a
|
||||
minimum size of 20 bytes and a maximum of 60
|
||||
bytes, allowing for up to 40 bytes of options
|
||||
in the header.";
|
||||
}
|
||||
leaf reserved {
|
||||
type uint8;
|
||||
description
|
||||
"Reserved for future use.";
|
||||
}
|
||||
leaf flags {
|
||||
type bits {
|
||||
bit cwr {
|
||||
position 1;
|
||||
description
|
||||
"The Congestion Window Reduced (CWR) flag is set
|
||||
by the sending host to indicate that it received
|
||||
a TCP segment with the ECN-Echo (ECE) flag set
|
||||
and had responded in the congestion control
|
||||
mechanism.";
|
||||
reference
|
||||
"RFC 3168: The Addition of Explicit Congestion
|
||||
Notification (ECN) to IP.";
|
||||
}
|
||||
bit ece {
|
||||
position 2;
|
||||
description
|
||||
"ECN-Echo has a dual role, depending on the value
|
||||
of the SYN flag. It indicates the following: if
|
||||
the SYN flag is set (1), the TCP peer is ECN
|
||||
capable, and if the SYN flag is clear (0), a packet
|
||||
with the Congestion Experienced flag set (ECN=11)
|
||||
in the IP header was received during normal
|
||||
transmission (added to the header by RFC 3168).
|
||||
This serves as an indication of network congestion
|
||||
(or impending congestion) to the TCP sender.";
|
||||
reference
|
||||
"RFC 3168: The Addition of Explicit Congestion
|
||||
Notification (ECN) to IP.";
|
||||
}
|
||||
bit urg {
|
||||
position 3;
|
||||
description
|
||||
"Indicates that the Urgent Pointer field is significant.";
|
||||
}
|
||||
bit ack {
|
||||
position 4;
|
||||
description
|
||||
"Indicates that the Acknowledgement field is significant.
|
||||
All packets after the initial SYN packet sent by the
|
||||
client should have this flag set.";
|
||||
}
|
||||
bit psh {
|
||||
position 5;
|
||||
description
|
||||
"Push function. Asks to push the buffered data to the
|
||||
receiving application.";
|
||||
}
|
||||
bit rst {
|
||||
position 6;
|
||||
description
|
||||
"Reset the connection.";
|
||||
}
|
||||
bit syn {
|
||||
position 7;
|
||||
description
|
||||
"Synchronize sequence numbers. Only the first packet
|
||||
sent from each end should have this flag set. Some
|
||||
other flags and fields change meaning based on this
|
||||
flag, and some are only valid for when it is set,
|
||||
and others when it is clear.";
|
||||
}
|
||||
bit fin {
|
||||
position 8;
|
||||
description
|
||||
"Last package from the sender.";
|
||||
}
|
||||
}
|
||||
description
|
||||
"Also known as Control Bits. Contains nine 1-bit flags.";
|
||||
reference
|
||||
"RFC 793: Transmission Control Protocol.";
|
||||
}
|
||||
leaf window-size {
|
||||
type uint16;
|
||||
units "bytes";
|
||||
description
|
||||
"The size of the receive window, which specifies
|
||||
the number of window size units beyond the segment
|
||||
identified by the sequence number in the Acknowledgement
|
||||
field that the sender of this segment is currently
|
||||
willing to receive.";
|
||||
}
|
||||
leaf urgent-pointer {
|
||||
type uint16;
|
||||
description
|
||||
"This field is an offset from the sequence number
|
||||
indicating the last urgent data byte.";
|
||||
}
|
||||
leaf options {
|
||||
type binary {
|
||||
length "1..40";
|
||||
}
|
||||
description
|
||||
"The length of this field is determined by the
|
||||
Data Offset field. Options have up to three
|
||||
fields: Option-Kind (1 byte), Option-Length
|
||||
(1 byte), and Option-Data (variable). The Option-Kind
|
||||
field indicates the type of option and is the
|
||||
only field that is not optional. Depending on
|
||||
what kind of option we are dealing with,
|
||||
the next two fields may be set: the Option-Length
|
||||
field indicates the total length of the option,
|
||||
and the Option-Data field contains the value of
|
||||
the option, if applicable.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping acl-udp-header-fields {
|
||||
description
|
||||
"Collection of UDP header fields that can be used
|
||||
to set up a match filter.";
|
||||
leaf length {
|
||||
type uint16;
|
||||
description
|
||||
"A field that specifies the length in bytes of
|
||||
the UDP header and UDP data. The minimum
|
||||
length is 8 bytes because that is the length of
|
||||
the header. The field size sets a theoretical
|
||||
limit of 65,535 bytes (8-byte header plus 65,527
|
||||
bytes of data) for a UDP datagram. However, the
|
||||
actual limit for the data length, which is
|
||||
imposed by the underlying IPv4 protocol, is
|
||||
65,507 bytes (65,535 minus 8-byte UDP header
|
||||
minus 20-byte IP header).
|
||||
|
||||
In IPv6 jumbograms, it is possible to have
|
||||
UDP packets of a size greater than 65,535 bytes.
|
||||
RFC 2675 specifies that the Length field is set
|
||||
to zero if the length of the UDP header plus
|
||||
UDP data is greater than 65,535.";
|
||||
}
|
||||
}
|
||||
|
||||
grouping acl-icmp-header-fields {
|
||||
description
|
||||
"Collection of ICMP header fields that can be
|
||||
used to set up a match filter.";
|
||||
leaf type {
|
||||
type uint8;
|
||||
description
|
||||
"Also known as control messages.";
|
||||
reference
|
||||
"RFC 792: Internet Control Message Protocol
|
||||
RFC 4443: Internet Control Message Protocol (ICMPv6)
|
||||
for Internet Protocol Version 6 (IPv6)
|
||||
Specification.";
|
||||
}
|
||||
leaf code {
|
||||
type uint8;
|
||||
description
|
||||
"ICMP subtype. Also known as control messages.";
|
||||
reference
|
||||
"RFC 792: Internet Control Message Protocol
|
||||
RFC 4443: Internet Control Message Protocol (ICMPv6)
|
||||
for Internet Protocol Version 6 (IPv6)
|
||||
Specification.";
|
||||
}
|
||||
leaf rest-of-header {
|
||||
type binary;
|
||||
description
|
||||
"Unbounded in length, the contents vary based on the
|
||||
ICMP type and code. Also referred to as 'Message Body'
|
||||
in ICMPv6.";
|
||||
reference
|
||||
"RFC 792: Internet Control Message Protocol
|
||||
RFC 4443: Internet Control Message Protocol (ICMPv6)
|
||||
for Internet Protocol Version 6 (IPv6)
|
||||
Specification.";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
module infix-ntp {
|
||||
yang-version 1.1;
|
||||
namespace "urn:infix:ntp:ns:yang:1.0";
|
||||
prefix infix-ntp;
|
||||
|
||||
import ietf-ntp {
|
||||
prefix ntp;
|
||||
reference
|
||||
"RFC 9249: A YANG Data Model for NTP";
|
||||
}
|
||||
|
||||
import ietf-inet-types {
|
||||
prefix inet;
|
||||
reference
|
||||
"RFC 6991: Common YANG Data Types";
|
||||
}
|
||||
|
||||
organization "KernelKit";
|
||||
contact "kernelkit@googlegroups.com";
|
||||
description "Infix deviations and augments to ietf-ntp.";
|
||||
|
||||
revision 2025-12-03 {
|
||||
description
|
||||
"Initial release - NTP server support.
|
||||
|
||||
Implements NTP server functionality using chronyd as the
|
||||
underlying daemon. Supports standalone, server, and peer modes
|
||||
with configurable poll intervals, makestep for fast initial sync,
|
||||
and rtcsync for hardware RTC synchronization. Mutual exclusion
|
||||
with ietf-system:ntp enforced via YANG when statement.";
|
||||
reference "internal";
|
||||
}
|
||||
|
||||
/*
|
||||
* Augments for Infix-specific features
|
||||
*/
|
||||
|
||||
augment "/ntp:ntp" {
|
||||
container makestep {
|
||||
presence "Enable clock stepping for large offsets";
|
||||
description
|
||||
"Allow system clock to step (jump) immediately when offset is large.
|
||||
This is useful for fast initial synchronization on systems that boot
|
||||
with incorrect time (e.g., epoch time when no RTC present).";
|
||||
|
||||
leaf threshold {
|
||||
type decimal64 {
|
||||
fraction-digits 1;
|
||||
range "0.1..max";
|
||||
}
|
||||
default "1.0";
|
||||
units "seconds";
|
||||
description
|
||||
"Threshold in seconds. If clock offset exceeds this value,
|
||||
step the clock instead of slewing it slowly.";
|
||||
}
|
||||
|
||||
leaf limit {
|
||||
type int32 {
|
||||
range "-1 | 0..max";
|
||||
}
|
||||
default "3";
|
||||
description
|
||||
"Number of clock updates during which stepping is allowed.
|
||||
After this many updates, only slewing is used. Special values:
|
||||
-1 = always allow stepping (not recommended)
|
||||
0 = never step (defeats purpose of this directive)
|
||||
1-N = allow stepping for first N updates (recommended: 3)";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Additional operational state fields from chronyd
|
||||
* that are not part of the standard ietf-ntp model
|
||||
*/
|
||||
augment "/ntp:ntp/ntp:clock-state/ntp:system-status" {
|
||||
leaf last-offset {
|
||||
type decimal64 {
|
||||
fraction-digits 9;
|
||||
}
|
||||
config false;
|
||||
units "seconds";
|
||||
description
|
||||
"Estimated offset of the last clock update.";
|
||||
}
|
||||
|
||||
leaf rms-offset {
|
||||
type decimal64 {
|
||||
fraction-digits 9;
|
||||
}
|
||||
config false;
|
||||
units "seconds";
|
||||
description
|
||||
"Long-term average of the offset value.";
|
||||
}
|
||||
|
||||
leaf residual-freq {
|
||||
type decimal64 {
|
||||
fraction-digits 3;
|
||||
}
|
||||
config false;
|
||||
units "ppm";
|
||||
description
|
||||
"Residual frequency indicating how much the frequency has
|
||||
changed relative to the previous value.";
|
||||
}
|
||||
|
||||
leaf skew {
|
||||
type decimal64 {
|
||||
fraction-digits 3;
|
||||
}
|
||||
config false;
|
||||
units "ppm";
|
||||
description
|
||||
"Estimated error bound on the frequency.";
|
||||
}
|
||||
|
||||
leaf update-interval {
|
||||
type decimal64 {
|
||||
fraction-digits 1;
|
||||
}
|
||||
config false;
|
||||
units "seconds";
|
||||
description
|
||||
"Interval between the last two clock updates.";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Deviations for unsupported features
|
||||
*/
|
||||
|
||||
deviation "/ntp:ntp/ntp:access-rules" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ntp:statistics-reset" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ntp:ntp/ntp:interfaces" {
|
||||
deviate not-supported;
|
||||
}
|
||||
|
||||
deviation "/ntp:ntp/ntp:unicast-configuration/ntp:address" {
|
||||
deviate replace {
|
||||
type inet:host;
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/ntp:ntp/ntp:unicast-configuration/ntp:iburst" {
|
||||
deviate add {
|
||||
must "not(ntp:iburst and ../type = 'ntp:uc-peer')" {
|
||||
error-message "iburst option is not supported in peer mode";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/ntp:ntp/ntp:unicast-configuration/ntp:burst" {
|
||||
deviate add {
|
||||
must "not(ntp:burst and ../type = 'ntp:uc-peer')" {
|
||||
error-message "burst option is not supported in peer mode";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deviation "/ntp:ntp/ntp:unicast-configuration/ntp:source" {
|
||||
deviate not-supported;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
infix-ntp.yang
|
||||
@@ -324,15 +324,19 @@
|
||||
</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="ntp" help="Show NTP (client) status">
|
||||
<ACTION sym="script">
|
||||
show ntp
|
||||
</ACTION>
|
||||
<SWITCH name="optional" min="0">
|
||||
<COMMAND name="ntp" help="Show NTP status">
|
||||
<SWITCH name="subcommands" min="0">
|
||||
<COMMAND name="source" help="Show NTP source(s)">
|
||||
<SWITCH name="optional" min="0" max="1">
|
||||
<PARAM name="address" ptype="/STRING" help="Show details for specific source address"/>
|
||||
</SWITCH>
|
||||
<ACTION sym="script">show ntp source $KLISH_PARAM_address</ACTION>
|
||||
</COMMAND>
|
||||
<COMMAND name="tracking" help="Show NTP tracking">
|
||||
<ACTION sym="script">doas ntp tracking</ACTION>
|
||||
<ACTION sym="script">show ntp tracking</ACTION>
|
||||
</COMMAND>
|
||||
</SWITCH>
|
||||
<ACTION sym="script">show ntp</ACTION>
|
||||
</COMMAND>
|
||||
|
||||
<COMMAND name="dhcp-server" help="Show DHCP server status">
|
||||
|
||||
+72
-3
@@ -69,15 +69,84 @@ def hardware(args: List[str]) -> None:
|
||||
cli_pretty(data, "show-hardware")
|
||||
|
||||
def ntp(args: List[str]) -> None:
|
||||
data = run_sysrepocfg("/system-state/ntp")
|
||||
# Create argument parser for ntp subcommands
|
||||
parser = argparse.ArgumentParser(prog='show ntp', add_help=False)
|
||||
|
||||
# Add subparsers for source and tracking
|
||||
subparsers = parser.add_subparsers(dest='subcommand', help='NTP subcommands')
|
||||
|
||||
# source subcommand
|
||||
source_parser = subparsers.add_parser('source', help='Show NTP source(s)')
|
||||
source_parser.add_argument('address', nargs='?', help='Show details for specific source address')
|
||||
|
||||
# tracking subcommand
|
||||
tracking_parser = subparsers.add_parser('tracking', help='Show NTP tracking status')
|
||||
|
||||
# Parse the arguments
|
||||
try:
|
||||
parsed_args = parser.parse_args(args)
|
||||
except SystemExit:
|
||||
# argparse calls sys.exit on error, catch it
|
||||
return
|
||||
|
||||
# Dispatch to appropriate handler
|
||||
if parsed_args.subcommand == 'source':
|
||||
return ntp_source([parsed_args.address] if parsed_args.address else [])
|
||||
elif parsed_args.subcommand == 'tracking':
|
||||
return ntp_tracking([])
|
||||
|
||||
# Default: show ntp (no subcommand or address)
|
||||
# Fetch both client and server operational data
|
||||
client_data = run_sysrepocfg("/system-state/ntp")
|
||||
server_data = run_sysrepocfg("/ietf-ntp:ntp")
|
||||
|
||||
# Merge into single data structure
|
||||
data = {}
|
||||
if client_data:
|
||||
data.update(client_data)
|
||||
if server_data:
|
||||
data.update(server_data)
|
||||
|
||||
if RAW_OUTPUT:
|
||||
if not data:
|
||||
print("No ntp data retrieved.")
|
||||
return
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
|
||||
# Always call cli_pretty, even with empty data, to show proper message
|
||||
if not data:
|
||||
print("No ntp data retrieved.")
|
||||
data = {}
|
||||
|
||||
# Default show ntp - summary view (no address support at top level)
|
||||
cli_pretty(data, "show-ntp")
|
||||
|
||||
def ntp_tracking(args: List[str]) -> None:
|
||||
data = run_sysrepocfg("/ietf-ntp:ntp")
|
||||
if not data:
|
||||
print("No ntp server data retrieved.")
|
||||
return
|
||||
|
||||
if RAW_OUTPUT:
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
cli_pretty(data, "show-ntp")
|
||||
cli_pretty(data, "show-ntp-tracking")
|
||||
|
||||
def ntp_source(args: List[str]) -> None:
|
||||
data = run_sysrepocfg("/ietf-ntp:ntp")
|
||||
if not data:
|
||||
print("No ntp server data retrieved.")
|
||||
return
|
||||
|
||||
if RAW_OUTPUT:
|
||||
print(json.dumps(data, indent=2))
|
||||
return
|
||||
|
||||
# Pass address argument if provided
|
||||
if len(args) > 0 and args[0]:
|
||||
cli_pretty(data, "show-ntp-source", "-a", args[0])
|
||||
else:
|
||||
cli_pretty(data, "show-ntp-source")
|
||||
|
||||
def is_valid_interface_name(interface_name: str) -> bool:
|
||||
"""
|
||||
|
||||
@@ -2387,24 +2387,461 @@ def show_container_detail(json, name):
|
||||
print(f"CPU Usage : {cpu_usage}%")
|
||||
|
||||
|
||||
def show_ntp(json):
|
||||
if not json.get("ietf-system:system-state"):
|
||||
print("NTP client not enabled.")
|
||||
def show_ntp_source_detail_single(source, is_association=False):
|
||||
"""Display detailed information for a single NTP source (auto-detected single source)"""
|
||||
print(f"{'Server address':<20}: {source.get('address', 'N/A')}")
|
||||
|
||||
# State
|
||||
if is_association:
|
||||
prefer = source.get("prefer", False)
|
||||
state_desc = "Selected sync source" if prefer else "Candidate"
|
||||
else:
|
||||
state = source.get('state', 'unknown')
|
||||
state_desc = {
|
||||
'selected': 'Selected sync source',
|
||||
'candidate': 'Candidate',
|
||||
'unreach': 'Unreachable',
|
||||
'not-combined': 'Not combined'
|
||||
}.get(state, state)
|
||||
print(f"{'State':<20}: {state_desc}")
|
||||
|
||||
# Stratum
|
||||
stratum = source.get('stratum')
|
||||
if stratum is not None:
|
||||
print(f"{'Stratum':<20}: {stratum}")
|
||||
|
||||
# Poll interval
|
||||
poll = source.get('poll')
|
||||
if poll is not None:
|
||||
poll_seconds = 2 ** poll
|
||||
print(f"{'Poll interval':<20}: {poll} (2^{poll} seconds = {poll_seconds}s)")
|
||||
|
||||
def show_ntp(json, address=None):
|
||||
"""Unified NTP status display for both client and server modes"""
|
||||
ntp_data = json.get("ietf-ntp:ntp", {})
|
||||
port = ntp_data.get("port")
|
||||
is_server = port is not None
|
||||
|
||||
sources = []
|
||||
if is_server:
|
||||
associations = ntp_data.get("associations", {}).get("association", [])
|
||||
sources = associations
|
||||
else:
|
||||
system_state = json.get("ietf-system:system-state", {})
|
||||
if system_state:
|
||||
sources = get_json_data({}, json, 'ietf-system:system-state', 'infix-system:ntp', 'sources', 'source')
|
||||
|
||||
if address:
|
||||
matching = [s for s in sources if s.get('address') == address]
|
||||
if not matching:
|
||||
print(f"No NTP source found with address: {address}")
|
||||
return
|
||||
if is_server:
|
||||
show_ntp_association_detail(matching[0])
|
||||
else:
|
||||
show_ntp_source_detail_single(matching[0], False)
|
||||
return
|
||||
hdr = (f"{'ADDRESS':<{PadNtpSource.address}}"
|
||||
f"{'MODE':<{PadNtpSource.mode}}"
|
||||
f"{'STATE':<{PadNtpSource.state}}"
|
||||
f"{'STRATUM':>{PadNtpSource.stratum}}"
|
||||
f"{'POLL-INTERVAL':>{PadNtpSource.poll}}"
|
||||
)
|
||||
|
||||
if is_server:
|
||||
if sources:
|
||||
print(f"{'Mode':<20}: Relay (no local reference clock)")
|
||||
else:
|
||||
print(f"{'Mode':<20}: Server (local reference clock)")
|
||||
print(f"{'Port':<20}: {port}")
|
||||
|
||||
# Show operational stratum
|
||||
refclock = ntp_data.get("refclock-master")
|
||||
if refclock:
|
||||
stratum = refclock.get("master-stratum")
|
||||
if stratum is not None:
|
||||
print(f"{'Stratum':<20}: {stratum}")
|
||||
|
||||
# Show reference time
|
||||
clock_state = ntp_data.get("clock-state", {}).get("system-status", {})
|
||||
ref_time = clock_state.get("reference-time")
|
||||
if ref_time:
|
||||
from datetime import datetime
|
||||
try:
|
||||
dt = datetime.fromisoformat(ref_time.replace("Z", "+00:00"))
|
||||
ref_time_str = dt.strftime("%a %b %d %H:%M:%S %Y")
|
||||
print(f"{'Ref time (UTC)':<20}: {ref_time_str}")
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
|
||||
interfaces = ntp_data.get("interfaces", {}).get("interface", [])
|
||||
if interfaces:
|
||||
print(f"{'Interfaces':<20}: {', '.join([iface.get('name', '?') for iface in interfaces])}")
|
||||
else:
|
||||
print(f"{'Interfaces':<20}: All")
|
||||
|
||||
stats = ntp_data.get("ntp-statistics")
|
||||
if stats:
|
||||
print(f"{'Packets Received':<20}: {stats.get('packet-received', 0):,}")
|
||||
print(f"{'Packets Sent':<20}: {stats.get('packet-sent', 0):,}")
|
||||
print(f"{'Packets Dropped':<20}: {stats.get('packet-dropped', 0):,}")
|
||||
print(f"{'Send Failures':<20}: {stats.get('packet-sent-fail', 0):,}")
|
||||
else:
|
||||
print(f"{'Mode':<20}: Client")
|
||||
|
||||
# Show local clock state in client mode
|
||||
clock_state = ntp_data.get("clock-state", {}).get("system-status", {})
|
||||
|
||||
# Show local operational stratum
|
||||
stratum = clock_state.get("clock-stratum")
|
||||
if stratum is not None:
|
||||
print(f"{'Stratum':<20}: {stratum}")
|
||||
|
||||
# Show reference time
|
||||
ref_time = clock_state.get("reference-time")
|
||||
if ref_time:
|
||||
from datetime import datetime
|
||||
try:
|
||||
dt = datetime.fromisoformat(ref_time.replace("Z", "+00:00"))
|
||||
ref_time_str = dt.strftime("%a %b %d %H:%M:%S %Y")
|
||||
print(f"{'Ref time (UTC)':<20}: {ref_time_str}")
|
||||
except (ValueError, AttributeError):
|
||||
pass
|
||||
|
||||
if len(sources) == 0:
|
||||
return
|
||||
if len(sources) == 1:
|
||||
show_ntp_source_detail_single(sources[0], is_server)
|
||||
return
|
||||
print()
|
||||
hdr = (f"{'ADDRESS':<{PadNtpSource.address}}"
|
||||
f"{'MODE':<{PadNtpSource.mode}}"
|
||||
f"{'STATE':<{PadNtpSource.state}}"
|
||||
f"{'STRATUM':>{PadNtpSource.stratum}}"
|
||||
f"{'POLL':>{PadNtpSource.poll}}")
|
||||
print(Decore.invert(hdr))
|
||||
sources = get_json_data({}, json, 'ietf-system:system-state', 'infix-system:ntp', 'sources', 'source')
|
||||
for source in sources:
|
||||
row = f"{source['address']:<{PadNtpSource.address}}"
|
||||
row += f"{source['mode']:<{PadNtpSource.mode}}"
|
||||
row += f"{source['state'] if source['state'] != 'not-combined' else 'not combined':<{PadNtpSource.state}}"
|
||||
row += f"{source['stratum']:>{PadNtpSource.stratum}}"
|
||||
row += f"{source['poll']:>{PadNtpSource.poll}}"
|
||||
# Extract fields - handle both association and ietf-system format
|
||||
address = source.get('address', 'N/A')
|
||||
|
||||
if is_server:
|
||||
# Association format
|
||||
local_mode = source.get("local-mode", "")
|
||||
if ":" in local_mode:
|
||||
local_mode = local_mode.split(":")[-1]
|
||||
mode_str = local_mode
|
||||
prefer = source.get("prefer", False)
|
||||
state_str = "selected" if prefer else "candidate"
|
||||
else:
|
||||
# ietf-system format
|
||||
mode_str = source.get('mode', 'unknown')
|
||||
state_str = source.get('state', 'unknown')
|
||||
if state_str == 'not-combined':
|
||||
state_str = 'not combined'
|
||||
|
||||
stratum = source.get('stratum', 0)
|
||||
poll = source.get('poll', 0)
|
||||
poll_str = f"{2**poll}s" if poll else "-"
|
||||
|
||||
row = f"{address:<{PadNtpSource.address}}"
|
||||
row += f"{mode_str:<{PadNtpSource.mode}}"
|
||||
row += f"{state_str:<{PadNtpSource.state}}"
|
||||
row += f"{stratum:>{PadNtpSource.stratum}}"
|
||||
row += f"{poll_str:>{PadNtpSource.poll}}"
|
||||
print(row)
|
||||
|
||||
|
||||
def show_ntp_tracking(json):
|
||||
"""Display NTP clock tracking state using YANG operational data"""
|
||||
ntp_data = json.get("ietf-ntp:ntp")
|
||||
if not ntp_data:
|
||||
print("NTP server not enabled.")
|
||||
return
|
||||
|
||||
clock_state = ntp_data.get("clock-state", {}).get("system-status", {})
|
||||
if not clock_state:
|
||||
print("No clock state data available.")
|
||||
return
|
||||
|
||||
# Reference ID
|
||||
refid = clock_state.get("clock-refid", "N/A")
|
||||
print(f"{'Reference ID':<20}: {refid}")
|
||||
|
||||
# Stratum
|
||||
stratum = clock_state.get("clock-stratum", 16)
|
||||
print(f"{'Stratum':<20}: {stratum}")
|
||||
|
||||
# Reference time (show epoch if not set)
|
||||
ref_time = clock_state.get("reference-time")
|
||||
if ref_time:
|
||||
from datetime import datetime
|
||||
try:
|
||||
dt = datetime.fromisoformat(ref_time.replace("Z", "+00:00"))
|
||||
ref_time_str = dt.strftime("%a %b %d %H:%M:%S %Y")
|
||||
except (ValueError, AttributeError):
|
||||
ref_time_str = ref_time
|
||||
else:
|
||||
ref_time_str = "Thu Jan 01 00:00:00 1970"
|
||||
print(f"{'Ref time (UTC)':<20}: {ref_time_str}")
|
||||
|
||||
# System time offset (3 fraction-digits in ms = microsecond precision)
|
||||
offset = clock_state.get("clock-offset")
|
||||
if offset is not None:
|
||||
offset_sec = float(offset) / 1000.0
|
||||
sign = "slow" if offset_sec >= 0 else "fast"
|
||||
print(f"{'System time':<20}: {abs(offset_sec):.6f} seconds {sign} of NTP time")
|
||||
|
||||
# Last offset (infix-ntp augment)
|
||||
last_offset = clock_state.get("infix-ntp:last-offset")
|
||||
if last_offset is not None:
|
||||
last_offset_sec = float(last_offset)
|
||||
sign = "+" if last_offset_sec >= 0 else ""
|
||||
print(f"{'Last offset':<20}: {sign}{last_offset_sec:.9f} seconds")
|
||||
else:
|
||||
print(f"{'Last offset':<20}: N/A")
|
||||
|
||||
# RMS offset (infix-ntp augment)
|
||||
rms_offset = clock_state.get("infix-ntp:rms-offset")
|
||||
if rms_offset is not None:
|
||||
print(f"{'RMS offset':<20}: {float(rms_offset):.9f} seconds")
|
||||
else:
|
||||
print(f"{'RMS offset':<20}: N/A")
|
||||
|
||||
# Frequency (convert from Hz difference to ppm)
|
||||
nominal_freq = clock_state.get("nominal-freq")
|
||||
actual_freq = clock_state.get("actual-freq")
|
||||
if nominal_freq and actual_freq:
|
||||
freq_diff_hz = float(actual_freq) - float(nominal_freq)
|
||||
freq_ppm = (freq_diff_hz / float(nominal_freq)) * 1000000.0
|
||||
if freq_ppm == 0.0:
|
||||
direction = "slow"
|
||||
else:
|
||||
direction = "slow" if freq_ppm < 0 else "fast"
|
||||
print(f"{'Frequency':<20}: {abs(freq_ppm):.3f} ppm {direction}")
|
||||
|
||||
# Residual freq (infix-ntp augment)
|
||||
residual_freq = clock_state.get("infix-ntp:residual-freq")
|
||||
if residual_freq is not None:
|
||||
residual_freq_val = float(residual_freq)
|
||||
sign = "+" if residual_freq_val >= 0 else ""
|
||||
print(f"{'Residual freq':<20}: {sign}{residual_freq_val:.3f} ppm")
|
||||
else:
|
||||
print(f"{'Residual freq':<20}: N/A")
|
||||
|
||||
# Skew (infix-ntp augment)
|
||||
skew = clock_state.get("infix-ntp:skew")
|
||||
if skew is not None:
|
||||
print(f"{'Skew':<20}: {float(skew):.3f} ppm")
|
||||
else:
|
||||
print(f"{'Skew':<20}: N/A")
|
||||
|
||||
# Root delay (3 fraction-digits in ms = microsecond precision)
|
||||
root_delay = clock_state.get("root-delay")
|
||||
if root_delay is not None:
|
||||
root_delay_sec = float(root_delay) / 1000.0
|
||||
print(f"{'Root delay':<20}: {root_delay_sec:.6f} seconds")
|
||||
|
||||
# Root dispersion (3 fraction-digits in ms = microsecond precision)
|
||||
root_disp = clock_state.get("root-dispersion")
|
||||
if root_disp is not None:
|
||||
root_disp_sec = float(root_disp) / 1000.0
|
||||
print(f"{'Root dispersion':<20}: {root_disp_sec:.6f} seconds")
|
||||
|
||||
# Update interval (infix-ntp augment)
|
||||
update_interval = clock_state.get("infix-ntp:update-interval")
|
||||
if update_interval is not None:
|
||||
print(f"{'Update interval':<20}: {float(update_interval):.1f} seconds")
|
||||
else:
|
||||
print(f"{'Update interval':<20}: N/A")
|
||||
|
||||
# Leap status
|
||||
sync_state = clock_state.get("sync-state", "")
|
||||
if "clock-synchronized" in sync_state:
|
||||
leap_status = "Normal"
|
||||
elif "clock-never-set" in sync_state:
|
||||
leap_status = "Not synchronised"
|
||||
else:
|
||||
leap_status = "Unknown"
|
||||
print(f"{'Leap status':<20}: {leap_status}")
|
||||
|
||||
|
||||
def show_ntp_association_detail(assoc):
|
||||
"""Display detailed information for a single NTP association"""
|
||||
print(f"{'Address':<20}: {assoc.get('address', 'N/A')}")
|
||||
|
||||
# Mode
|
||||
local_mode = assoc.get("local-mode", "")
|
||||
if ":" in local_mode:
|
||||
local_mode = local_mode.split(":")[-1]
|
||||
|
||||
mode_desc = {
|
||||
'client': 'Server (client mode) [^]',
|
||||
'active': 'Peer (symmetric active) [=]',
|
||||
'broadcast-client': 'Broadcast/Local refclock [#]'
|
||||
}.get(local_mode, local_mode)
|
||||
print(f"{'Mode':<20}: {mode_desc}")
|
||||
|
||||
# State/Prefer
|
||||
prefer = assoc.get("prefer", False)
|
||||
state_desc = "Selected sync source [*]" if prefer else "Candidate [+]"
|
||||
print(f"{'State':<20}: {state_desc}")
|
||||
|
||||
# Configured
|
||||
isconfigured = assoc.get("isconfigured", False)
|
||||
print(f"{'Configured':<20}: {'Yes' if isconfigured else 'No (dynamic)'}")
|
||||
|
||||
# Stratum
|
||||
stratum = assoc.get("stratum")
|
||||
if stratum is not None:
|
||||
print(f"{'Stratum':<20}: {stratum}")
|
||||
|
||||
# Poll interval
|
||||
poll = assoc.get("poll")
|
||||
if poll is not None:
|
||||
print(f"{'Poll interval':<20}: {poll} (2^{poll} seconds = {2**poll}s)")
|
||||
|
||||
# Reachability
|
||||
reach = assoc.get("reach")
|
||||
if reach is not None:
|
||||
print(f"{'Reachability':<20}: {reach:03o} (octal) = {reach:08b}b")
|
||||
|
||||
# Time since last packet
|
||||
now = assoc.get("now")
|
||||
if now is not None:
|
||||
print(f"{'Last RX':<20}: {now}s ago")
|
||||
|
||||
# Offset
|
||||
offset = assoc.get("offset")
|
||||
if offset is not None:
|
||||
offset_ms = float(offset)
|
||||
if abs(offset_ms) < 1.0:
|
||||
offset_str = f"{offset_ms * 1000.0:+.1f}us ({offset_ms:+.6f}ms)"
|
||||
else:
|
||||
offset_str = f"{offset_ms:+.3f}ms ({offset_ms / 1000.0:+.6f}s)"
|
||||
print(f"{'Offset':<20}: {offset_str}")
|
||||
|
||||
# Delay
|
||||
delay = assoc.get("delay")
|
||||
if delay is not None:
|
||||
delay_ms = float(delay)
|
||||
if abs(delay_ms) < 1.0:
|
||||
delay_str = f"{delay_ms * 1000.0:.1f}us ({delay_ms:.6f}ms)"
|
||||
else:
|
||||
delay_str = f"{delay_ms:.3f}ms ({delay_ms / 1000.0:.6f}s)"
|
||||
print(f"{'Delay':<20}: {delay_str}")
|
||||
|
||||
# Dispersion
|
||||
dispersion = assoc.get("dispersion")
|
||||
if dispersion is not None:
|
||||
disp_ms = float(dispersion)
|
||||
if abs(disp_ms) < 1.0:
|
||||
disp_str = f"{disp_ms * 1000.0:.1f}us ({disp_ms:.6f}ms)"
|
||||
else:
|
||||
disp_str = f"{disp_ms:.3f}ms ({disp_ms / 1000.0:.6f}s)"
|
||||
print(f"{'Dispersion':<20}: {disp_str}")
|
||||
|
||||
def show_ntp_source(json, address=None):
|
||||
"""Display NTP associations/sources"""
|
||||
ntp_data = json.get("ietf-ntp:ntp")
|
||||
if not ntp_data:
|
||||
print("NTP server not enabled.")
|
||||
return
|
||||
|
||||
associations = ntp_data.get("associations", {}).get("association", [])
|
||||
if not associations:
|
||||
print("No NTP associations found.")
|
||||
return
|
||||
|
||||
# If address specified, show detailed view for that association
|
||||
if address:
|
||||
matching = [a for a in associations if a.get('address') == address]
|
||||
if not matching:
|
||||
print(f"No NTP association found with address: {address}")
|
||||
return
|
||||
show_ntp_association_detail(matching[0])
|
||||
return
|
||||
|
||||
# If single association, show detailed view automatically
|
||||
if len(associations) == 1:
|
||||
show_ntp_association_detail(associations[0])
|
||||
return
|
||||
|
||||
# First pass: determine maximum address width needed
|
||||
max_addr_len = len("Name/IP address") # Minimum width to match chronyc header
|
||||
for assoc in associations:
|
||||
addr_len = len(assoc.get("address", ""))
|
||||
if addr_len > max_addr_len:
|
||||
max_addr_len = addr_len
|
||||
|
||||
# Cap at reasonable maximum (IPv6 can be up to 39 chars uncompressed)
|
||||
max_addr_len = min(max_addr_len, 39)
|
||||
|
||||
# Table header - similar to chronyc sources
|
||||
hdr = f"{'MS':<3}{f'Name/IP address':<{max_addr_len}} {'Stratum':>7} {'Poll':>4} {'Reach':>5} {'LastRx':>6} {'Last sample':>24}"
|
||||
print(Decore.invert(hdr))
|
||||
|
||||
# Display each association
|
||||
for assoc in associations:
|
||||
# State indicator: * = prefer (sync source), + = candidate
|
||||
prefer = assoc.get("prefer", False)
|
||||
state = "*" if prefer else "+"
|
||||
|
||||
# Mode indicator
|
||||
local_mode = assoc.get("local-mode", "")
|
||||
if ":" in local_mode:
|
||||
local_mode = local_mode.split(":")[-1]
|
||||
# Map to chronyc-style mode indicators
|
||||
mode_indicator = "^" # Default to server mode
|
||||
if local_mode == "active":
|
||||
mode_indicator = "="
|
||||
elif local_mode == "broadcast-client":
|
||||
mode_indicator = "#"
|
||||
|
||||
address = assoc.get("address", "N/A")
|
||||
stratum = assoc.get("stratum", 0)
|
||||
|
||||
# Poll interval (log2 seconds)
|
||||
poll = assoc.get("poll")
|
||||
poll_str = str(poll) if poll is not None else "-"
|
||||
|
||||
# Reachability register (display as octal)
|
||||
reach = assoc.get("reach")
|
||||
if reach is not None:
|
||||
reach_str = f"{reach:03o}"
|
||||
else:
|
||||
reach_str = "-"
|
||||
|
||||
# Time since last packet (LastRx)
|
||||
now = assoc.get("now")
|
||||
now_str = str(now) if now is not None else "-"
|
||||
|
||||
# Offset (in milliseconds, convert to microseconds for display)
|
||||
offset = assoc.get("offset")
|
||||
if offset is not None:
|
||||
offset_ms = float(offset)
|
||||
if abs(offset_ms) < 1.0:
|
||||
# Show in microseconds if less than 1ms
|
||||
offset_str = f"{offset_ms * 1000.0:+.0f}us"
|
||||
else:
|
||||
# Show in milliseconds
|
||||
offset_str = f"{offset_ms:+.3f}ms"
|
||||
else:
|
||||
offset_str = "-"
|
||||
|
||||
# Delay (in milliseconds) - show as +/- similar to chronyc
|
||||
delay = assoc.get("delay")
|
||||
if delay is not None:
|
||||
delay_ms = float(delay)
|
||||
if abs(delay_ms) < 1.0:
|
||||
delay_str = f"+/- {delay_ms * 1000.0:.0f}us"
|
||||
else:
|
||||
delay_str = f"+/- {delay_ms:.3f}ms"
|
||||
else:
|
||||
delay_str = ""
|
||||
|
||||
# Last sample column combines offset and delay like chronyc
|
||||
if offset_str != "-":
|
||||
last_sample = f"{offset_str:>12} {delay_str}"
|
||||
else:
|
||||
last_sample = "-"
|
||||
|
||||
# Format row
|
||||
ms_col = f"{mode_indicator}{state}"
|
||||
row = f"{ms_col:<3}{address:<{max_addr_len}} {stratum:>7} {poll_str:>4} {reach_str:>5} {now_str:>6} {last_sample:>24}"
|
||||
print(row)
|
||||
|
||||
|
||||
@@ -4392,7 +4829,11 @@ def main():
|
||||
subparsers.add_parser('show-firewall-log', help='Show firewall log') \
|
||||
.add_argument('limit', nargs='?', help='Last N lines, default: all')
|
||||
|
||||
subparsers.add_parser('show-ntp', help='Show NTP sources')
|
||||
subparsers.add_parser('show-ntp', help='Show NTP status') \
|
||||
.add_argument('-a', '--address', help='Show details for specific address')
|
||||
subparsers.add_parser('show-ntp-tracking', help='Show NTP tracking status')
|
||||
subparsers.add_parser('show-ntp-source', help='Show NTP associations/sources') \
|
||||
.add_argument('-a', '--address', help='Show details for specific source')
|
||||
|
||||
subparsers.add_parser('show-bfd', help='Show BFD sessions')
|
||||
subparsers.add_parser('show-bfd-status', help='Show BFD status')
|
||||
@@ -4452,7 +4893,11 @@ def main():
|
||||
elif args.command == "show-firewall-log":
|
||||
show_firewall_logs(args.limit)
|
||||
elif args.command == "show-ntp":
|
||||
show_ntp(json_data)
|
||||
show_ntp(json_data, args.address)
|
||||
elif args.command == "show-ntp-tracking":
|
||||
show_ntp_tracking(json_data)
|
||||
elif args.command == "show-ntp-source":
|
||||
show_ntp_source(json_data, args.address)
|
||||
elif args.command == "show-wifi-radio":
|
||||
show_wifi_radio(json_data)
|
||||
elif args.command == "show-bfd":
|
||||
|
||||
@@ -78,6 +78,9 @@ def main():
|
||||
elif args.model == 'ietf-system':
|
||||
from . import ietf_system
|
||||
yang_data = ietf_system.operational()
|
||||
elif args.model == 'ietf-ntp':
|
||||
from . import ietf_ntp
|
||||
yang_data = ietf_ntp.operational()
|
||||
elif args.model == 'ieee802-dot1ab-lldp':
|
||||
from . import infix_lldp
|
||||
yang_data = infix_lldp.operational()
|
||||
|
||||
@@ -0,0 +1,363 @@
|
||||
import subprocess
|
||||
|
||||
from .common import insert
|
||||
from .host import HOST
|
||||
|
||||
|
||||
def add_ntp_associations(out):
|
||||
"""Add NTP association information from chronyc sources and sourcestats"""
|
||||
try:
|
||||
# Get basic source information
|
||||
sources_data = HOST.run_multiline(["chronyc", "-c", "sources"], [])
|
||||
if not sources_data:
|
||||
return
|
||||
|
||||
# Get statistical information (offset, dispersion)
|
||||
stats_data = HOST.run_multiline(["chronyc", "-c", "sourcestats"], [])
|
||||
|
||||
# Build a map of address -> stats for quick lookup
|
||||
stats_map = {}
|
||||
if stats_data:
|
||||
for line in stats_data:
|
||||
parts = line.split(',')
|
||||
if len(parts) >= 8:
|
||||
address = parts[0]
|
||||
stats_map[address] = {
|
||||
"offset": parts[6], # Estimated offset in seconds
|
||||
"std_dev": parts[7] # Standard deviation in seconds
|
||||
}
|
||||
|
||||
associations = []
|
||||
# Map chronyd mode indicators to ietf-ntp association-mode identities
|
||||
mode_map = {
|
||||
"^": "ietf-ntp:client", # We're client to this server
|
||||
"=": "ietf-ntp:active", # Peer mode (symmetric active)
|
||||
"#": "ietf-ntp:broadcast-client" # Local refclock (closest match)
|
||||
}
|
||||
|
||||
# chronyc -c sources format:
|
||||
# [0]=Mode, [1]=State, [2]=Address, [3]=Stratum, [4]=Poll, [5]=Reach,
|
||||
# [6]=LastRx, [7]=LastOffset, [8]=OffsetAtLastUpdate, [9]=Error
|
||||
for line in sources_data:
|
||||
parts = line.split(',')
|
||||
if len(parts) < 10:
|
||||
continue
|
||||
|
||||
mode_indicator = parts[0]
|
||||
state_indicator = parts[1]
|
||||
address = parts[2]
|
||||
stratum = int(parts[3])
|
||||
|
||||
# Skip sources with invalid stratum (0 means unreachable/not yet synced)
|
||||
# YANG model requires stratum to be in range 1..16
|
||||
if stratum < 1 or stratum > 16:
|
||||
continue
|
||||
|
||||
assoc = {}
|
||||
assoc["address"] = address
|
||||
assoc["local-mode"] = mode_map.get(mode_indicator, "ietf-ntp:client")
|
||||
assoc["isconfigured"] = True
|
||||
assoc["stratum"] = stratum
|
||||
|
||||
# Prefer indicator: * means current sync source
|
||||
if state_indicator == "*":
|
||||
assoc["prefer"] = True
|
||||
|
||||
# Reachability register (octal string to decimal uint8)
|
||||
try:
|
||||
reach_octal = parts[5]
|
||||
assoc["reach"] = int(reach_octal, 8)
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Poll interval (already in log2 seconds)
|
||||
try:
|
||||
assoc["poll"] = int(parts[4])
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Time since last packet (now)
|
||||
try:
|
||||
assoc["now"] = int(parts[6])
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Offset: prefer sourcestats data if available, otherwise use sources
|
||||
# Convert from seconds to milliseconds with 3 fraction-digits
|
||||
try:
|
||||
if address in stats_map:
|
||||
offset_sec = float(stats_map[address]["offset"])
|
||||
else:
|
||||
# Use last offset from sources output (parts[7])
|
||||
offset_sec = float(parts[7])
|
||||
assoc["offset"] = f"{offset_sec * 1000.0:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Delay: use error estimate from sources (parts[9])
|
||||
# Convert from seconds to milliseconds with 3 fraction-digits
|
||||
try:
|
||||
delay_sec = float(parts[9])
|
||||
# chronyd reports this as error bound, use absolute value
|
||||
assoc["delay"] = f"{abs(delay_sec) * 1000.0:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Dispersion: use standard deviation from sourcestats
|
||||
# Convert from seconds to milliseconds with 3 fraction-digits
|
||||
try:
|
||||
if address in stats_map:
|
||||
disp_sec = float(stats_map[address]["std_dev"])
|
||||
assoc["dispersion"] = f"{disp_sec * 1000.0:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
associations.append(assoc)
|
||||
|
||||
if associations:
|
||||
insert(out, "ietf-ntp:ntp", "associations", "association", associations)
|
||||
except Exception:
|
||||
# NTP not running or no sources configured, silently skip
|
||||
pass
|
||||
|
||||
|
||||
def add_ntp_clock_state(out):
|
||||
"""Add NTP clock state from chronyc tracking"""
|
||||
try:
|
||||
data = HOST.run_multiline(["chronyc", "-c", "tracking"], [])
|
||||
if not data or len(data) == 0:
|
||||
return
|
||||
|
||||
# Parse tracking output (CSV format)
|
||||
# Format: Ref-ID(IP),Ref-ID(name),Stratum,Ref-time,System-time,Last-offset,
|
||||
# RMS-offset,Frequency,Residual-freq,Skew,Root-delay,Root-dispersion,
|
||||
# Update-interval,Leap-status
|
||||
parts = data[0].split(',')
|
||||
if len(parts) < 14:
|
||||
return
|
||||
|
||||
clock_state = {}
|
||||
system_status = {}
|
||||
|
||||
# chronyd uses stratum 0 for "not synchronized", YANG requires 1-16
|
||||
stratum_raw = int(parts[2])
|
||||
stratum = 16 if stratum_raw == 0 else stratum_raw
|
||||
|
||||
if stratum == 16:
|
||||
system_status["clock-state"] = "ietf-ntp:unsynchronized"
|
||||
else:
|
||||
system_status["clock-state"] = "ietf-ntp:synchronized"
|
||||
|
||||
system_status["clock-stratum"] = stratum
|
||||
|
||||
# Convert hex Ref-ID to IPv4 dotted notation
|
||||
# "00000000" -> "0.0.0.0", "7F7F0101" -> "127.127.1.1"
|
||||
refid_ip = parts[0]
|
||||
refid_name = parts[1]
|
||||
|
||||
if refid_name:
|
||||
system_status["clock-refid"] = refid_name
|
||||
elif refid_ip and len(refid_ip) == 8:
|
||||
try:
|
||||
a = int(refid_ip[0:2], 16)
|
||||
b = int(refid_ip[2:4], 16)
|
||||
c = int(refid_ip[4:6], 16)
|
||||
d = int(refid_ip[6:8], 16)
|
||||
system_status["clock-refid"] = f"{a}.{b}.{c}.{d}"
|
||||
except ValueError:
|
||||
system_status["clock-refid"] = refid_ip if refid_ip else "0.0.0.0"
|
||||
else:
|
||||
system_status["clock-refid"] = refid_ip if refid_ip else "0.0.0.0"
|
||||
|
||||
# Add clock frequencies (in Hz)
|
||||
# chronyd reports frequency offset in ppm, need to convert to Hz
|
||||
# Nominal frequency is typically 1000000000 Hz for system clock
|
||||
# Format with fraction-digits 4 to avoid scientific notation
|
||||
try:
|
||||
freq_ppm = float(parts[7])
|
||||
nominal = 1000000000.0
|
||||
actual = nominal * (1.0 + freq_ppm / 1000000.0)
|
||||
system_status["nominal-freq"] = f"{nominal:.4f}"
|
||||
system_status["actual-freq"] = f"{actual:.4f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Clock precision (use skew as approximation, converted to log2 seconds)
|
||||
# chronyd reports skew in ppm, we'll use a fixed precision value
|
||||
# Most systems have precision around -6 to -20 (2^-6 to 2^-20 seconds)
|
||||
system_status["clock-precision"] = -20 # ~1 microsecond precision
|
||||
|
||||
# Clock offset (System-time column, already in seconds)
|
||||
# Convert to milliseconds with fraction-digits 3 to match YANG
|
||||
try:
|
||||
offset_sec = float(parts[4])
|
||||
system_status["clock-offset"] = f"{offset_sec * 1000.0:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Root delay (in seconds, convert to milliseconds)
|
||||
# Format with fraction-digits 3 to match YANG
|
||||
try:
|
||||
root_delay_sec = float(parts[10])
|
||||
system_status["root-delay"] = f"{root_delay_sec * 1000.0:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Root dispersion (in seconds, convert to milliseconds)
|
||||
# Format with fraction-digits 3 to match YANG
|
||||
try:
|
||||
root_disp_sec = float(parts[11])
|
||||
system_status["root-dispersion"] = f"{root_disp_sec * 1000.0:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Reference time (Ref-time in seconds since epoch)
|
||||
# YANG expects ntp-date-and-time format, but we'll provide Unix timestamp
|
||||
try:
|
||||
ref_time = float(parts[3])
|
||||
if ref_time > 0:
|
||||
# Convert to ISO 8601 timestamp
|
||||
from datetime import datetime
|
||||
dt = datetime.utcfromtimestamp(ref_time)
|
||||
system_status["reference-time"] = dt.strftime("%Y-%m-%dT%H:%M:%S.%fZ")[:-4] + "Z"
|
||||
except (ValueError, IndexError, OSError):
|
||||
pass
|
||||
|
||||
# Sync state based on leap status
|
||||
# chronyd leap status: 0=Normal, 1=Insert second, 2=Delete second, 3=Not synchronized
|
||||
try:
|
||||
leap_status_str = parts[13].strip()
|
||||
if leap_status_str == "Not synchronised" or stratum == 16:
|
||||
system_status["sync-state"] = "ietf-ntp:clock-never-set"
|
||||
else:
|
||||
system_status["sync-state"] = "ietf-ntp:clock-synchronized"
|
||||
except (ValueError, IndexError):
|
||||
# Default based on stratum
|
||||
if stratum == 16:
|
||||
system_status["sync-state"] = "ietf-ntp:clock-never-set"
|
||||
else:
|
||||
system_status["sync-state"] = "ietf-ntp:clock-synchronized"
|
||||
|
||||
# Infix-specific augments: additional chronyd operational data
|
||||
# Last offset (parts[5], in seconds, 9 fraction-digits)
|
||||
try:
|
||||
last_offset = float(parts[5])
|
||||
system_status["infix-ntp:last-offset"] = f"{last_offset:.9f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# RMS offset (parts[6], in seconds, 9 fraction-digits)
|
||||
try:
|
||||
rms_offset = float(parts[6])
|
||||
system_status["infix-ntp:rms-offset"] = f"{rms_offset:.9f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Residual frequency (parts[8], in ppm, 3 fraction-digits)
|
||||
try:
|
||||
residual_freq = float(parts[8])
|
||||
system_status["infix-ntp:residual-freq"] = f"{residual_freq:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Skew (parts[9], in ppm, 3 fraction-digits)
|
||||
try:
|
||||
skew = float(parts[9])
|
||||
system_status["infix-ntp:skew"] = f"{skew:.3f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
# Update interval (parts[12], in seconds, 1 fraction-digit)
|
||||
try:
|
||||
update_interval = float(parts[12])
|
||||
system_status["infix-ntp:update-interval"] = f"{update_interval:.1f}"
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
|
||||
clock_state["system-status"] = system_status
|
||||
insert(out, "ietf-ntp:ntp", "clock-state", clock_state)
|
||||
except Exception:
|
||||
# NTP not running, silently skip
|
||||
pass
|
||||
|
||||
|
||||
def add_ntp_server_status(out):
|
||||
"""Add NTP server operational status (port and stratum)
|
||||
|
||||
Note: This must be called after add_ntp_clock_state() so we can
|
||||
reuse the stratum already extracted from chronyc tracking.
|
||||
"""
|
||||
try:
|
||||
ntp_data = out.get("ietf-ntp:ntp", {})
|
||||
clock_state = ntp_data.get("clock-state", {})
|
||||
system_status = clock_state.get("system-status", {})
|
||||
stratum = system_status.get("clock-stratum")
|
||||
|
||||
if stratum is not None:
|
||||
# Populate refclock-master with operational stratum
|
||||
# This shows what stratum we're actually operating at
|
||||
refclock = {
|
||||
"master-stratum": stratum
|
||||
}
|
||||
insert(out, "ietf-ntp:ntp", "refclock-master", refclock)
|
||||
|
||||
# Get actual listening port, excluding loopback (command port)
|
||||
# UNCONN 0 0 0.0.0.0:123 0.0.0.0:* users:(("chronyd",pid=5441))
|
||||
# UNCONN 0 0 *:123 *:* users:(("chronyd",pid=5441))
|
||||
ss_lines = HOST.run_multiline(["ss", "-ulnp"], [])
|
||||
for line in ss_lines:
|
||||
if "chronyd" not in line:
|
||||
continue
|
||||
if "127.0.0.1" in line or "[::1]" in line:
|
||||
continue
|
||||
|
||||
parts = line.split()
|
||||
if len(parts) >= 5:
|
||||
local_addr = parts[3]
|
||||
port_str = local_addr.split(':')[-1]
|
||||
if port_str.isdigit():
|
||||
insert(out, "ietf-ntp:ntp", "port", int(port_str))
|
||||
break
|
||||
|
||||
except Exception:
|
||||
# NTP server not running, silently skip
|
||||
pass
|
||||
|
||||
|
||||
def add_ntp_server_stats(out):
|
||||
"""Add NTP server statistics if ietf-ntp is active"""
|
||||
try:
|
||||
# Get server statistics from chronyd
|
||||
data = HOST.run_multiline(["chronyc", "-c", "serverstats"], [])
|
||||
if not data or len(data) == 0:
|
||||
return
|
||||
|
||||
# Parse serverstats output (CSV format)
|
||||
# Format: NTPpacketsreceived,NTPpacketsdropped,Cmdpacketsreceived,
|
||||
# Cmdpacketsdropped,Clientlogsizeactive,Clientlogmemory,
|
||||
# Ratelimitdrops,NTPpktsresp,NTPpktsresp-fail
|
||||
parts = data[0].split(',')
|
||||
if len(parts) < 9:
|
||||
return
|
||||
|
||||
stats = {}
|
||||
stats["packet-received"] = int(parts[0])
|
||||
stats["packet-dropped"] = int(parts[1])
|
||||
stats["packet-sent"] = int(parts[7])
|
||||
stats["packet-sent-fail"] = int(parts[8])
|
||||
|
||||
insert(out, "ietf-ntp:ntp", "ntp-statistics", stats)
|
||||
except Exception:
|
||||
# NTP server not running or not configured, silently skip
|
||||
pass
|
||||
|
||||
|
||||
def operational():
|
||||
"""Get operational state for ietf-ntp module"""
|
||||
out = {}
|
||||
add_ntp_associations(out)
|
||||
add_ntp_clock_state(out)
|
||||
add_ntp_server_status(out)
|
||||
add_ntp_server_stats(out)
|
||||
|
||||
return out
|
||||
@@ -63,6 +63,7 @@ def add_ntp(out):
|
||||
|
||||
insert(out, "infix-system:ntp", "sources", "source", source)
|
||||
|
||||
|
||||
def add_dns(out):
|
||||
options = {}
|
||||
servers = []
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
#define XPATH_DHCP_SERVER_BASE "/infix-dhcp-server:dhcp-server"
|
||||
#define XPATH_LLDP_BASE "/ieee802-dot1ab-lldp:lldp"
|
||||
#define XPATH_FIREWALL_BASE "/infix-firewall:firewall"
|
||||
#define XPATH_NTP_BASE "/ietf-ntp:ntp"
|
||||
|
||||
TAILQ_HEAD(sub_head, sub);
|
||||
|
||||
@@ -444,6 +445,8 @@ static int subscribe_to_all(struct statd *statd)
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "infix-firewall", XPATH_FIREWALL_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
if (subscribe(statd, "ietf-ntp", XPATH_NTP_BASE, sr_generic_cb))
|
||||
return SR_ERR_INTERNAL;
|
||||
|
||||
INFO("Successfully subscribed to all models");
|
||||
return SR_ERR_OK;
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
[7mADDRESS MODE STATE STRATUM POLL-INTERVAL[0m
|
||||
192.168.1.1 server candidate 1 6
|
||||
192.168.2.1 server candidate 1 6
|
||||
192.168.3.1 server selected 1 6
|
||||
Mode : Client
|
||||
|
||||
[7mADDRESS MODE STATE STRATUM POLL[0m
|
||||
192.168.1.1 server candidate 1 64s
|
||||
192.168.2.1 server candidate 1 64s
|
||||
192.168.3.1 server selected 1 64s
|
||||
|
||||
Reference in New Issue
Block a user