Merge branch 'sysrepo'

This commit is contained in:
Tobias Waldekranz
2023-04-13 10:21:23 +02:00
74 changed files with 8743 additions and 20 deletions
+1 -2
View File
@@ -1,4 +1,3 @@
[submodule "buildroot"]
path = buildroot
url = https://github.com/buildroot/buildroot.git
branch = 2023.02.x
url = ../buildroot.git
-1
View File
@@ -141,7 +141,6 @@ CONFIG_MTD_SPI_NOR=y
CONFIG_OF_OVERLAY=y
CONFIG_BLK_DEV_NULL_BLK=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=y
CONFIG_BLK_DEV_RAM=y
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_NVME=y
-1
View File
@@ -95,7 +95,6 @@ CONFIG_FW_CFG_SYSFS=y
CONFIG_FW_CFG_SYSFS_CMDLINE=y
CONFIG_BLK_DEV_NULL_BLK=y
CONFIG_BLK_DEV_LOOP=y
CONFIG_BLK_DEV_NBD=y
CONFIG_BLK_DEV_RAM=y
CONFIG_VIRTIO_BLK=y
CONFIG_BLK_DEV_SD=y
@@ -16,4 +16,4 @@ alias ipaddr='ip addr'
alias iplink='ip link'
alias bridge='bridge --color=auto'
alias setup='pdmenu; clear'
alias cli='clixon_cli'
alias cli='klish'
+3 -7
View File
@@ -48,6 +48,7 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SUPPORT=y
BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_NETOPEER2_CLI=y
BR2_PACKAGE_NSS_MDNS=y
BR2_PACKAGE_ONIGURUMA=y
BR2_PACKAGE_AVAHI=y
@@ -74,10 +75,6 @@ BR2_PACKAGE_NETCALC=y
BR2_PACKAGE_NETCAT_OPENBSD=y
BR2_PACKAGE_NETSNMP=y
BR2_PACKAGE_NFTABLES=y
BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y
BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y
BR2_PACKAGE_NGINX_HTTP_GUNZIP_MODULE=y
BR2_PACKAGE_NGINX_HTTP_AUTH_REQUEST_MODULE=y
BR2_PACKAGE_NMAP=y
BR2_PACKAGE_NMAP_NCAT=y
BR2_PACKAGE_NMAP_NMAP=y
@@ -102,7 +99,6 @@ BR2_PACKAGE_PWGEN=y
BR2_PACKAGE_RAUC=y
BR2_PACKAGE_RAUC_GPT=y
BR2_PACKAGE_RAUC_NETWORK=y
BR2_PACKAGE_RAUC_STREAMING=y
BR2_PACKAGE_SYSKLOGD=y
BR2_PACKAGE_SYSKLOGD_LOGGER=y
BR2_PACKAGE_WATCHDOGD=y
@@ -119,7 +115,7 @@ BR2_PACKAGE_HOST_RAUC=y
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
BR2_PACKAGE_CLIXON_RESTCONF_FCGI=y
BR2_PACKAGE_CONFD=y
BR2_PACKAGE_FACTORY=y
BR2_PACKAGE_FINIT_SULOGIN=y
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
@@ -130,7 +126,7 @@ BR2_PACKAGE_FINIT_PLUGIN_RTC=y
BR2_PACKAGE_FINIT_PLUGIN_TTY=y
BR2_PACKAGE_FINIT_PLUGIN_URANDOM=y
BR2_PACKAGE_IFUPDOWN_NG=y
BR2_PACKAGE_MECH=y
BR2_PACKAGE_KLINFIX=y
BR2_PACKAGE_P_NET=y
BR2_PACKAGE_P_NET_MAX_SUBSLOTS=12
BR2_PACKAGE_P_NET_MAX_PHYSICAL_PORTS=10
+5 -7
View File
@@ -45,6 +45,7 @@ BR2_PACKAGE_UBOOT_TOOLS_FIT_SUPPORT=y
BR2_PACKAGE_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
BR2_PACKAGE_UBOOT_TOOLS_FIT_CHECK_SIGN=y
BR2_PACKAGE_LIBCURL_CURL=y
BR2_PACKAGE_NETOPEER2_CLI=y
BR2_PACKAGE_NSS_MDNS=y
BR2_PACKAGE_ONIGURUMA=y
BR2_PACKAGE_AVAHI=y
@@ -71,10 +72,6 @@ BR2_PACKAGE_NETCALC=y
BR2_PACKAGE_NETCAT_OPENBSD=y
BR2_PACKAGE_NETSNMP=y
BR2_PACKAGE_NFTABLES=y
BR2_PACKAGE_NGINX_HTTP_SSL_MODULE=y
BR2_PACKAGE_NGINX_HTTP_V2_MODULE=y
BR2_PACKAGE_NGINX_HTTP_GUNZIP_MODULE=y
BR2_PACKAGE_NGINX_HTTP_AUTH_REQUEST_MODULE=y
BR2_PACKAGE_NMAP=y
BR2_PACKAGE_NMAP_NCAT=y
BR2_PACKAGE_NMAP_NMAP=y
@@ -99,7 +96,6 @@ BR2_PACKAGE_PWGEN=y
BR2_PACKAGE_RAUC=y
BR2_PACKAGE_RAUC_GPT=y
BR2_PACKAGE_RAUC_NETWORK=y
BR2_PACKAGE_RAUC_STREAMING=y
BR2_PACKAGE_SYSKLOGD=y
BR2_PACKAGE_SYSKLOGD_LOGGER=y
BR2_PACKAGE_WATCHDOGD=y
@@ -114,15 +110,17 @@ BR2_TARGET_GRUB2_X86_64_EFI=y
BR2_TARGET_GRUB2_BUILTIN_MODULES_EFI="boot linux ext2 fat squash4 part_gpt normal efi_gop configfile loadenv test terminfo terminal echo"
BR2_TARGET_GRUB2_BUILTIN_CONFIG_EFI="${BR2_EXTERNAL_INFIX_PATH}/board/amd64/grub-embed.cfg"
BR2_TARGET_GRUB2_INSTALL_TOOLS=y
BR2_PACKAGE_HOST_DOSFSTOOLS=y
BR2_PACKAGE_HOST_E2FSPROGS=y
BR2_PACKAGE_HOST_ENVIRONMENT_SETUP=y
BR2_PACKAGE_HOST_GENEXT2FS=y
BR2_PACKAGE_HOST_GENIMAGE=y
BR2_PACKAGE_HOST_MTOOLS=y
BR2_PACKAGE_HOST_RAUC=y
BR2_PACKAGE_HOST_UBOOT_TOOLS=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SUPPORT=y
BR2_PACKAGE_HOST_UBOOT_TOOLS_FIT_SIGNATURE_SUPPORT=y
BR2_PACKAGE_CLIXON_RESTCONF_FCGI=y
BR2_PACKAGE_CONFD=y
BR2_PACKAGE_FACTORY=y
BR2_PACKAGE_FINIT_SULOGIN=y
BR2_PACKAGE_FINIT_PLUGIN_HOTPLUG=y
@@ -133,7 +131,7 @@ BR2_PACKAGE_FINIT_PLUGIN_RTC=y
BR2_PACKAGE_FINIT_PLUGIN_TTY=y
BR2_PACKAGE_FINIT_PLUGIN_URANDOM=y
BR2_PACKAGE_IFUPDOWN_NG=y
BR2_PACKAGE_MECH=y
BR2_PACKAGE_KLINFIX=y
BR2_PACKAGE_P_NET=y
BR2_PACKAGE_P_NET_MAX_SUBSLOTS=12
BR2_PACKAGE_P_NET_MAX_PHYSICAL_PORTS=10
+5
View File
@@ -1,9 +1,14 @@
source "$BR2_EXTERNAL_INFIX_PATH/package/cligen/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/clixon/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/confd/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/factory/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/faux/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/finit/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/ifupdown2/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/ifupdown-ng/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/klinfix/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/klish/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/klish-plugin-sysrepo/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/mdnsd/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/mech/Config.in"
source "$BR2_EXTERNAL_INFIX_PATH/package/osal/Config.in"
+10
View File
@@ -0,0 +1,10 @@
config BR2_PACKAGE_CONFD
bool "confd"
select BR2_PACKAGE_SYSREPO
select BR2_PACKAGE_NETOPEER2
select BR2_PACKAGE_AUGEAS
select BR2_PACKAGE_JANSSON
help
A plugin to sysrepo that provides the core YANG models used to
manage an Infix based system. Configuration can be done using
either the on-system CLI or using NETCONF.
+20
View File
@@ -0,0 +1,20 @@
#!/bin/sh
# This is an unmanaged node, leave /etc as-is
/lib/infix/use-etc && exit 0
# This is a managed node, ignore all default configuration ...
# ... except for some core services
reserved()
{
for svc in dnsmasq getty klish lldpd nginx sysklogd sysrepo; do
[ "$1" = "${svc}.conf" ] && return 0
done
return 1
}
for file in /etc/finit.d/enabled/*.conf; do
reserved "$(basename "$file")" && continue
rm "$file"
done
+26
View File
@@ -0,0 +1,26 @@
################################################################################
#
# confd
#
################################################################################
CONFD_VERSION = 1.0
CONFD_LICENSE = BSD-3-Clause
CONFD_SITE_METHOD = local
CONFD_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/confd
CONFD_DEPENDENCIES = augeas sysrepo
CONFD_AUTORECONF = YES
define CONFD_INSTALL_EXTRA
cp $(CONFD_PKGDIR)/sysrepo.conf $(FINIT_D)/available/
ln -sf ../available/sysrepo.conf $(FINIT_D)/enabled/sysrepo.conf
cp $(CONFD_PKGDIR)/tmpfiles.conf $(TARGET_DIR)/etc/tmpfiles.d/confd.conf
mkdir -p $(TARGET_DIR)/usr/share/factory/cfg
cp $(CONFD_PKGDIR)/factory.cfg $(TARGET_DIR)/usr/share/factory/cfg/startup-config.cfg
mkdir -p $(TARGET_DIR)/lib/infix
cp $(CONFD_PKGDIR)/prep-db $(TARGET_DIR)/lib/infix/
cp $(CONFD_PKGDIR)/clean-etc $(TARGET_DIR)/lib/infix/
endef
CONFD_TARGET_FINALIZE_HOOKS += CONFD_INSTALL_EXTRA
$(eval $(autotools-package))
+5
View File
@@ -0,0 +1,5 @@
{
"ietf-system:system": {
"hostname": "default"
}
}
+4
View File
@@ -0,0 +1,4 @@
#!/bin/sh
set -e
exit 0
+8
View File
@@ -0,0 +1,8 @@
run if:<boot/netconf> [S] /lib/infix/clean-etc --
run if:<boot/netconf> [S] /lib/infix/prep-db --
service if:<boot/netconf> name:sysrepo [S12345789] sysrepo-plugind -p /run/sysrepo.pid -n -- Configuration daemon
run if:<boot/netconf> [S] log <pid/sysrepo> sysrepocfg -v4 -E/cfg/startup-config.cfg -f json --
service if:<boot/netconf> name:netopeer [12345789] netopeer2-server -n -- NETCONF daemon
task if:<boot/netconf> [12345789] resolvconf -u -- Update DNS configuration
+2
View File
@@ -0,0 +1,2 @@
d /run/resolvconf/interfaces - - -
C /cfg/startup-config.cfg - - -
+7
View File
@@ -0,0 +1,7 @@
config BR2_PACKAGE_FAUX
bool "faux"
depends on BR2_USE_MMU
help
The library contains a set of auxiliary functions.
https://src.libcode.org/pkun/faux/
+3
View File
@@ -0,0 +1,3 @@
# Locally calculated
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
sha256 8e369f54c2da317030ff4cad905e501303d0489b98e4b208d0ff611b4484c94e faux-tags_2.1.0-br1.tar.gz
+15
View File
@@ -0,0 +1,15 @@
################################################################################
#
# faux
#
################################################################################
FAUX_VERSION = tags/2.1.0
FAUX_SITE = https://src.libcode.org/pkun/faux.git
FAUX_SITE_METHOD = git
FAUX_LICENSE = BSD-3
FAUX_LICENSE_FILES = LICENSE
FAUX_INSTALL_STAGING = YES
FAUX_AUTORECONF = YES
$(eval $(autotools-package))
+22
View File
@@ -150,6 +150,28 @@ config BR2_PACKAGE_FINIT_PLUGIN_RTC
For lxc/docker application builds you do not need this.
if BR2_PACKAGE_FINIT_PLUGIN_RTC
config BR2_PACKAGE_FINIT_RTC_DATE
string "Fallback RTC date"
help
On many embedded systems there is no RTC battery, maybe there
is a "supercap" but on first boot it is usually out of juice.
When this happens the RTC will return its default value, or
the driver will fail and report error. In these cases Linux
will detect a too old/invalid date set from userspace. Any
date prior to 2000-01-01 00:00 is denied. (Previously this
was the start of the UNIX epoch, Jan 1 1970.)
This setting changes the default fallback date/time the RTC
plugin uses in case of such failures. Useful, e.g., for an
HTTPS certificate generated at first boot before any network
connection (DHCP/NTP) has been made to get the current time.
Ensuring the default system time to be closer to actual time
for a period after releasing a firmware image.
endif
config BR2_PACKAGE_FINIT_PLUGIN_TTY
bool "TTY plugin"
help
+6
View File
@@ -89,6 +89,12 @@ else
FINIT_CONF_OPTS += --disable-rtc-plugin
endif
ifeq ($(BR2_PACKAGE_FINIT_RTC_DATE),y)
FINIT_CONF_OPTS += --with-rtc-date="$(BR2_PACKAGE_FINIT_RTC_DATE)"
else
FINIT_CONF_OPTS += --without-rtc-date
endif
ifeq ($(BR2_PACKAGE_FINIT_PLUGIN_TTY),y)
FINIT_CONF_OPTS += --enable-tty-plugin
else
+8
View File
@@ -0,0 +1,8 @@
config BR2_PACKAGE_KLINFIX
bool "klinfix"
select BR2_PACKAGE_KLISH
select BR2_PACKAGE_KLISH_PLUGIN_SYSREPO
select BR2_PACKAGE_SYSREPO
help
A plugin to klish with an opinionated config and some
command extensions, suitable for systems running Infix.
+14
View File
@@ -0,0 +1,14 @@
################################################################################
#
# klinfix
#
################################################################################
KLINFIX_VERSION = 1.0
KLINFIX_LICENSE = BSD-3-Clause
KLINFIX_SITE_METHOD = local
KLINFIX_SITE = $(BR2_EXTERNAL_INFIX_PATH)/src/klinfix
KLINFIX_DEPENDENCIES = klish-plugin-sysrepo
KLINFIX_AUTORECONF = YES
$(eval $(autotools-package))
+22
View File
@@ -0,0 +1,22 @@
config BR2_PACKAGE_KLISH_PLUGIN_SYSREPO
bool "klish-plugin-sysrepo"
depends on BR2_PACKAGE_KLISH
depends on BR2_PACKAGE_SYSREPO
help
This plugin allows klish framework to implement Juniper-like
interface for configuration. It uses sysrepo project as
background for datastores and libyang for yang files parsing
and configuration schemes.
https://src.libcode.org/pkun/klish-plugin-sysrepo/
config BR2_PACKAGE_KLISH_PLUGIN_SYSREPO_XML
bool "install xml"
depends on BR2_PACKAGE_KLISH_PLUGIN_SYSREPO
depends on !BR2_PACKAGE_KLINFIX
default y
help
Install the default XML spec bundled with the project, which
exports the 'show' command, and the 'configure' view, etc.
If unsure, say Y.
@@ -0,0 +1,4 @@
# Locally calculated
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
sha256 515fa2a3c66b41d6ab5ec93d61cdd32097115b2f46e39c522659b84d555eebf5 klish-plugin-sysrepo-cdd3eb51a7f7ee0ed5bd925fa636061d3b1b85fb-br1.tar.gz
@@ -0,0 +1,25 @@
################################################################################
#
# klish-plugin-sysrepo
#
################################################################################
KLISH_PLUGIN_SYSREPO_VERSION = cdd3eb51a7f7ee0ed5bd925fa636061d3b1b85fb
KLISH_PLUGIN_SYSREPO_SITE = https://src.libcode.org/pkun/klish-plugin-sysrepo.git
KLISH_PLUGIN_SYSREPO_SITE_METHOD = git
KLISH_PLUGIN_SYSREPO_LICENSE = BSD-3
KLISH_PLUGIN_SYSREPO_LICENSE_FILES = LICENSE
KLISH_PLUGIN_SYSREPO_DEPENDENCIES = klish sysrepo
KLISH_PLUGIN_SYSREPO_INSTALL_STAGING = YES
KLISH_PLUGIN_SYSREPO_AUTORECONF = YES
ifeq ($(BR2_PACKAGE_KLISH_PLUGIN_SYSREPO_XML),y)
define KLISH_PLUGIN_SYSREPO_INSTALL_XML
$(INSTALL) -t $(TARGET_DIR)/etc/klish -D -m 0644 \
$(@D)/xml/sysrepo.xml
endef
KLISH_PLUGIN_SYSREPO_POST_INSTALL_TARGET_HOOKS += \
KLISH_PLUGIN_SYSREPO_INSTALL_XML
endif
$(eval $(autotools-package))
+22
View File
@@ -0,0 +1,22 @@
config BR2_PACKAGE_KLISH
bool "klish"
depends on BR2_USE_MMU
select BR2_PACKAGE_FAUX
select BR2_PACKAGE_LIBXML2
help
Klish is a framework for implementing a CISCO-like or
Juniper-like CLI on a UNIX systems.
https://src.libcode.org/pkun/klish/
config BR2_PACKAGE_KLISH_DEFAULT_XML
bool "install xml"
depends on BR2_PACKAGE_KLISH
depends on !BR2_PACKAGE_KLINFIX
default y
help
Install a small XML spec containing a definition of an
'exit' command. This lets klish work out-of-the-box, even if
no other specs are available.
If unsure, say Y.
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<KLISH
xmlns="https://klish.libcode.org/klish3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://src.libcode.org/pkun/klish/src/master/klish.xsd">
<PLUGIN name="klish"/>
<ENTRY name="COMMAND" purpose="ptype">
<ENTRY name="completion" purpose="completion">
<ACTION sym="completion_COMMAND"/>
</ENTRY>
<ENTRY name="help" purpose="help">
<ACTION sym="completion_COMMAND"/>
</ENTRY>
<ACTION sym="COMMAND"/>
</ENTRY>
<ENTRY name="main" mode="switch" container="true">
<ENTRY name="prompt" purpose="prompt">
<ACTION sym="prompt">%u@%h&gt; </ACTION>
</ENTRY>
<ENTRY name="exit" help="Exit view">
<ENTRY name="COMMAND" purpose="ptype" ref="/COMMAND"/>
<ACTION sym="nav">pop</ACTION>
<ACTION sym="printl">Exiting klish session</ACTION>
</ENTRY>
<HOTKEY key="^Z" cmd="exit"/>
</ENTRY>
</KLISH>
+3
View File
@@ -0,0 +1,3 @@
# Locally calculated
sha256 9d9d33b873917ca5d0bdcc47a36d2fd385971ab0c045d1472fcadf95ee5bcf5b LICENCE
sha256 e7c09457ed0b030eddd8eaf7d9f7c9344c2fcd7ac2083f1d8ebeaafc4ed6dba6 klish-tags_3.0.0-br1.tar.gz
+46
View File
@@ -0,0 +1,46 @@
################################################################################
#
# klish
#
################################################################################
KLISH_VERSION = tags/3.0.0
KLISH_SITE = https://src.libcode.org/pkun/klish.git
KLISH_SITE_METHOD = git
KLISH_LICENSE = BSD-3
KLISH_LICENSE_FILES = LICENSE
KLISH_DEPENDENCIES = libxml2
KLISH_INSTALL_STAGING = YES
KLISH_AUTORECONF = YES
KLISH_CONF_OPTS += --with-libxml2
define KLISH_INSTALL_CONFIG
$(INSTALL) -t $(TARGET_DIR)/etc/klish -D -m 0644 \
$(@D)/klish.conf $(@D)/klishd.conf
# klish's default pager settings assumes that less understands
# all GNU options, so we disable it.
sed -i -e 's/#UsePager=y/UsePager=n/' \
$(TARGET_DIR)/etc/klish/klish.conf
endef
KLISH_POST_INSTALL_TARGET_HOOKS += KLISH_INSTALL_CONFIG
ifeq ($(BR2_PACKAGE_KLISH_DEFAULT_XML),y)
define KLISH_INSTALL_XML
$(INSTALL) -t $(TARGET_DIR)/etc/klish -D -m 0644 \
$(BR2_EXTERNAL_INFIX_PATH)/package/klish/default.xml
endef
KLISH_POST_INSTALL_TARGET_HOOKS += KLISH_INSTALL_XML
endif
define KLISH_INSTALL_FINIT_SVC
$(INSTALL) -D -m 0644 $(BR2_EXTERNAL_INFIX_PATH)/package/klish/klish.svc \
$(FINIT_D)/available/klish.conf
$(INSTALL) -d -m 0755 $(FINIT_D)/enabled
ln -sf ../available/klish.conf $(FINIT_D)/enabled/klish.conf
endef
KLISH_POST_INSTALL_TARGET_HOOKS += KLISH_INSTALL_FINIT_SVC
$(eval $(autotools-package))
+1
View File
@@ -0,0 +1 @@
service <!> [2345789] /usr/bin/klishd -d -- CLI backend
+3
View File
@@ -15,6 +15,9 @@ define MECH_INSTALL_EXTRA
cp $(MECH_PKGDIR)/clixon.conf $(FINIT_D)/available/
ln -sf ../available/clixon.conf $(FINIT_D)/enabled/clixon.conf
cp $(MECH_PKGDIR)/tmpfiles.conf $(TARGET_DIR)/etc/tmpfiles.d/mech.conf
mkdir -p $(TARGET_DIR)/lib/infix
cp $(MECH_PKGDIR)/prep-db $(TARGET_DIR)/lib/infix/
cp $(MECH_PKGDIR)/clean-etc $(TARGET_DIR)/lib/infix/
endef
MECH_TARGET_FINALIZE_HOOKS += MECH_INSTALL_EXTRA
@@ -22,6 +22,8 @@ define SYSREPO_PLUGIN_SYSTEM_INSTALL_YANG_MODELS
$(TARGET_DIR)/usr/share/yang/modules/sysrepo-plugin-system/iana-crypt-hash@2014-08-06.yang
$(INSTALL) -D -m 0644 $(@D)/yang/ietf-system@2014-08-06.yang \
$(TARGET_DIR)/usr/share/yang/modules/sysrepo-plugin-system/ietf-system@2014-08-06.yang
$(INSTALL) -D -m 0644 $(@D)/yang/infix-system@2014-08-06.yang \
$(TARGET_DIR)/usr/share/yang/modules/sysrepo-plugin-system/infix-system@2014-08-06.yang
endef
SYSREPO_PLUGIN_SYSTEM_POST_INSTALL_TARGET_HOOKS += SYSREPO_PLUGIN_SYSTEM_INSTALL_YANG_MODELS
@@ -0,0 +1,60 @@
commit 9e18bd10df1ac2aabd46a790ac6148c638a513b8
Author: Joachim Wiberg <troglobit@gmail.com>
Date: Fri Mar 31 10:44:32 2023 +0200
netopeer2-server: add support for running in foreground with syslog
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
diff --git a/src/main.c b/src/main.c
index 6d054f5..c403000 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1082,6 +1082,7 @@ print_usage(char *progname)
fprintf(stdout, " -f PATH Path to netopeer2 server files directory (default path is \"%s\")\n", SERVER_DIR);
fprintf(stdout, " -U[PATH] Listen on a local UNIX socket (default path is \"%s\").\n", NP2SRV_UNIX_SOCK_PATH);
fprintf(stdout, " -m MODE Set mode for the listening UNIX socket.\n");
+ fprintf(stdout, " -n Run in foreground and log to syslog.\n");
fprintf(stdout, " -u UID Set UID/user for the listening UNIX socket.\n");
fprintf(stdout, " -g GID Set GID/group for the listening UNIX socket.\n");
fprintf(stdout, " -t TIMEOUT Timeout in seconds of all sysrepo functions (applying edit-config, reading data, ...),\n");
@@ -1112,7 +1113,7 @@ main(int argc, char *argv[])
{
int ret = EXIT_SUCCESS;
int c, *idx, i;
- int daemonize = 1, verb = 0;
+ int daemonize = 1, verb = 0, use_syslog = 0;
int pidfd;
const char *pidfile = NP2SRV_PID_FILE_PATH;
char pid[8];
@@ -1145,7 +1146,7 @@ main(int argc, char *argv[])
np2srv.server_dir = SERVER_DIR;
/* process command line options */
- while ((c = getopt(argc, argv, "dhVp:f:U::m:u:g:t:x:v:c:")) != -1) {
+ while ((c = getopt(argc, argv, "dhVp:f:U::m:nu:g:t:x:v:c:")) != -1) {
switch (c) {
case 'd':
daemonize = 0;
@@ -1202,6 +1203,10 @@ main(int argc, char *argv[])
return EXIT_FAILURE;
}
break;
+ case 'n':
+ daemonize = 0;
+ use_syslog = 1;
+ break;
case 'u':
np2srv.unix_uid = strtoul(optarg, &ptr, 10);
if (*ptr) {
@@ -1306,6 +1311,10 @@ main(int argc, char *argv[])
np2_stderr_log = 0;
}
+ /* use syslog even if running in foreground */
+ if (use_syslog == 1)
+ np2_stderr_log = 0;
+
/* make sure we are the only instance - lock the PID file and write the PID */
pidfd = open(pidfile, O_RDWR | O_CREAT, 0640);
if (pidfd < 0) {
@@ -0,0 +1,64 @@
commit 6fc440089353d76a9b8cb7c480402ee911a1387d
Author: Joachim Wiberg <troglobit@gmail.com>
Date: Tue Mar 28 10:37:53 2023 +0200
sysrepo-plugind: add support for running in foreground with syslog
Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
diff --git a/src/executables/sysrepo-plugind.c b/src/executables/sysrepo-plugind.c
index bd584ed5..3079a855 100644
--- a/src/executables/sysrepo-plugind.c
+++ b/src/executables/sysrepo-plugind.c
@@ -97,6 +97,7 @@ help_print(void)
" Change verbosity to a level (none, error, warning, info, debug) or\n"
" number (0, 1, 2, 3, 4).\n"
" -d, --debug Debug mode - is not daemonized and logs to stderr instead of syslog.\n"
+ " -n, --foreground Run in foreground and log to syslog.\n"
" -P, --plugin-install <path>\n"
" Install a sysrepo-plugind plugin. The plugin is simply copied\n"
" to the designated plugin directory.\n"
@@ -200,6 +201,8 @@ daemon_init(int debug, sr_log_level_t log_level)
if (debug) {
handle_signals();
+ if (debug < 0)
+ goto done;
sr_log_stderr(log_level);
return;
}
@@ -240,6 +243,7 @@ daemon_init(int debug, sr_log_level_t log_level)
close(fd);
}
+done:
/* set verbosity */
sr_log_syslog("sysrepo-plugind", log_level);
}
@@ -459,6 +463,7 @@ main(int argc, char **argv)
{"version", no_argument, NULL, 'V'},
{"verbosity", required_argument, NULL, 'v'},
{"debug", no_argument, NULL, 'd'},
+ {"foreground", no_argument, NULL, 'n'},
{"plugin-install", required_argument, NULL, 'P'},
{"pid-file", required_argument, NULL, 'p'},
{"fatal-plugin-fail", no_argument, NULL, 'f'},
@@ -467,7 +472,7 @@ main(int argc, char **argv)
/* process options */
opterr = 0;
- while ((opt = getopt_long(argc, argv, "hVv:dP:p:f", options, NULL)) != -1) {
+ while ((opt = getopt_long(argc, argv, "hVv:dnP:p:f", options, NULL)) != -1) {
switch (opt) {
case 'h':
version_print();
@@ -499,6 +504,9 @@ main(int argc, char **argv)
case 'd':
debug = 1;
break;
+ case 'n':
+ debug = -1;
+ break;
case 'P':
/* plugin-install */
if (get_plugins_dir(&plugins_dir)) {
+33
View File
@@ -0,0 +1,33 @@
*~
*.o
*.la
*.lo
*.so
.deps
.libs
/aclocal.m4
/autom4te.cache/
/aux
/clixon.xml
/compile
/config.h
/config.h.in
/config.guess
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/m4
/missing
GPATH
GRTAGS
GTAGS
Makefile
Makefile.in
+29
View File
@@ -0,0 +1,29 @@
Copyright (c) 2020 Denis Kalashnikov
Copyright (c) 2023 The KernelKit Authors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of copyright holders nor the names of
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+3
View File
@@ -0,0 +1,3 @@
SUBDIRS = src yang
DISTCLEANFILES = *~ *.d
ACLOCAL_AMFLAGS = -I m4
+15
View File
@@ -0,0 +1,15 @@
Configuration Daemon
====================
`confd` is the Infix configuration daemon that serves as the glue
between sysrepo and netopeer2 (NETCONF) and the UNIX system under
it all.
Origin & References
-------------------
Based on the Open Source [dklibc/sysrepo_plugin_ietf_system][0]
project by Denis Kalashnikov.
[0]: https://github.com/dklibc/sysrepo_plugin_ietf_system
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
autoreconf -W portability -vifm
+55
View File
@@ -0,0 +1,55 @@
AC_PREREQ(2.61)
AC_INIT([confd], [1.0.0],
[https://github.com/kernelkit/infix/issues])
AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
AM_SILENT_RULES(yes)
LT_INIT
AC_CONFIG_FILES([
Makefile
src/Makefile
yang/Makefile
])
AC_PROG_CC
AC_PROG_INSTALL
# Check for pkg-config first, warn if it's not installed
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.36])
PKG_CHECK_MODULES([augeas], [augeas >= 1.12.0])
PKG_CHECK_MODULES([jansson], [jansson >= 2.0.0])
# Plugin installation path for sysrepo-plugind
PKG_CHECK_VAR([srpdplugindir], [sysrepo], [SRPD_PLUGINS_PATH])
AC_SUBST(srpdplugindir)
test "x$prefix" = xNONE && prefix=$ac_default_prefix
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
DATAROOTDIR=`eval echo $datarootdir`
DATAROOTDIR=`eval echo $DATAROOTDIR`
AC_SUBST(DATAROOTDIR)
YANGDIR=`eval echo $datarootdir/yang/modules/confd`
AC_SUBST(YANGDIR)
LIBDIR=`eval echo $libdir`
LIBDIR=`eval echo $LIBDIR`
AC_SUBST(LIBDIR)
LOCALSTATEDIR=`eval echo $localstatedir`
LOCALSTATEDIR=`eval echo $LOCALSTATEDIR`
AC_SUBST(LOCALSTATEDIR)
RUNSTATEDIR=`eval echo $runstatedir`
RUNSTATEDIR=`eval echo $RUNSTATEDIR`
AC_SUBST(RUNSTATEDIR)
SYSCONFDIR=`eval echo $sysconfdir`
SYSCONFDIR=`eval echo $SYSCONFDIR`
AC_SUBST(SYSCONFDIR)
AC_OUTPUT
+10
View File
@@ -0,0 +1,10 @@
CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
AM_CPPFLAGS = -D_DEFAULT_SOURCE -D_XOPEN_SOURCE -DYANG_PATH_=\"$(YANGDIR)/\"
plugindir = $(srpdplugindir)
plugin_LTLIBRARIES = confd-plugin.la
confd_plugin_la_CFLAGS = $(augeas_CFLAGS) $(sysrepo_CFLAGS) $(CFLAGS)
confd_plugin_la_LIBADD = $(augeas_LIBS) $(sysrepo_LIBS)
confd_plugin_la_LDFLAGS = -module -avoid-version -shared
confd_plugin_la_SOURCES = core.c core.h run.c ietf-system.c ietf-interfaces.c
+89
View File
@@ -0,0 +1,89 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "core.h"
static struct confd confd;
static int startup_save_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
if (run("sysrepocfg -X/cfg/startup-config.cfg -d startup -f json"))
return SR_ERR_SYS;
return SR_ERR_OK;
}
static int commit_done_hook(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
if (system("initctl -nbq reload"))
return SR_ERR_SYS;
return SR_ERR_OK;
}
int sr_plugin_init_cb(sr_session_ctx_t *session, void **priv)
{
sr_session_ctx_t *startup;
int rc = SR_ERR_SYS;
openlog("confd", LOG_USER, 0);
/* Save context with default running config datastore for all our models */
*priv = (void *)&confd;
confd.session = session;
confd.conn = sr_session_get_connection(session);
confd.sub = NULL;
if (!confd.conn)
goto err;
confd.aug = aug_init(NULL, "", 0);
if (!confd.aug)
goto err;
rc = ietf_interfaces_init(&confd);
if (rc)
goto err;
rc = ietf_system_init(&confd);
if (rc)
goto err;
/* YOUR_INIT GOES HERE */
/* Set up hook to save startup-config to persisten backend store */
rc = sr_session_start(confd.conn, SR_DS_STARTUP, &startup);
if (rc)
goto err;
rc = sr_module_change_subscribe(startup, "ietf-system", "/ietf-system:system//.",
startup_save_hook, NULL, 0, SR_SUBSCR_PASSIVE | SR_SUBSCR_DONE_ONLY, &confd.sub);
if (rc) {
ERROR("failed setting up startup-config hook: %s", sr_strerror(rc));
goto err;
}
rc = sr_module_change_subscribe(session, "ietf-system", "/ietf-system:system//.",
commit_done_hook, NULL, 0, SR_SUBSCR_PASSIVE | SR_SUBSCR_DONE_ONLY, &confd.sub);
if (rc) {
ERROR("failed setting up startup-config hook: %s", sr_strerror(rc));
goto err;
}
rc = sr_install_module(confd.conn, YANG_PATH_"kernelkit-infix-deviations.yang", NULL, NULL);
if (rc)
goto err;
return SR_ERR_OK;
err:
ERROR("init failed: %s", sr_strerror(rc));
sr_unsubscribe(confd.sub);
return rc;
}
void sr_plugin_cleanup_cb(sr_session_ctx_t *session, void *priv)
{
sr_unsubscribe((sr_subscription_ctx_t *)priv);
closelog();
}
+82
View File
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#ifndef CONFD_CORE_H_
#define CONFD_CORE_H_
#include <errno.h>
#include <stdio.h>
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
#include <augeas.h>
#include <libyang/libyang.h>
#include <sysrepo.h>
#include <sysrepo/values.h>
#include <sysrepo/xpath.h>
#define NELEMS(arr) (sizeof(arr) / sizeof(arr[0]))
#define DEBUG(frmt, ...)
//#define DEBUG(frmt, ...) syslog(LOG_DEBUG, "%s: "frmt, __func__, ##__VA_ARGS__)
#define ERROR(frmt, ...) syslog(LOG_ERR, "%s: " frmt, __func__, ##__VA_ARGS__)
#define ERRNO(frmt, ...) syslog(LOG_ERR, "%s: " frmt ": %s", __func__, ##__VA_ARGS__, strerror(errno))
static inline void print_val(sr_val_t *val)
{
char *str;
if (sr_print_val_mem(&str, val))
return;
ERROR("%s", str);
free(str);
}
#define REGISTER_CHANGE(s,m,x,f,c,a,u) \
if ((rc = register_change(s, m, x, f, c, a, u))) \
goto err
#define REGISTER_RPC(s,x,c,a,u) \
if ((rc = register_rpc(s, x, c, a, u))) \
goto err
struct confd {
sr_session_ctx_t *session;
sr_conn_ctx_t *conn;
sr_subscription_ctx_t *sub;
augeas *aug;
};
static inline int register_change(sr_session_ctx_t *session, const char *module, const char *xpath,
int flags, sr_module_change_cb cb, void *arg, sr_subscription_ctx_t **sub)
{
int rc = sr_module_change_subscribe(session, module, xpath, cb, arg, 0, flags | SR_SUBSCR_DEFAULT, sub);
if (rc)
ERROR("failed subscribing to changes of %s: %s", xpath, sr_strerror(rc));
return rc;
}
static inline int register_rpc(sr_session_ctx_t *session, const char *xpath,
sr_rpc_cb cb, void *arg, sr_subscription_ctx_t **sub)
{
int rc = sr_rpc_subscribe(session, xpath, cb, arg, 0, SR_SUBSCR_DEFAULT, sub);
if (rc)
ERROR("failed subscribing to %s rpc: %s", xpath, sr_strerror(rc));
return rc;
}
/* core.c */
int run(const char *fmt, ...);
/* ietf-interfaces.c */
int ietf_interfaces_init(struct confd *confd);
/* ietf-system.c */
int ietf_system_init(struct confd *confd);
#endif /* CONFD_CORE_H_ */
+8
View File
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "core.h"
int ietf_interfaces_init(struct confd *confd)
{
return SR_ERR_OK;
}
+795
View File
@@ -0,0 +1,795 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "core.h"
#include <ctype.h>
#include <sys/utsname.h>
#include <sys/sysinfo.h>
#define CLOCK_PATH_ "/ietf-system:system-state/clock"
#define PLATFORM_PATH_ "/ietf-system:system-state/platform"
static char *ver = NULL;
static char *rel = NULL;
static char *sys = NULL;
static char *os = NULL;
static char *strip_quotes(char *str)
{
char *ptr;
while (*str && (isspace(*str) || *str == '"'))
str++;
for (ptr = str + strlen(str); ptr > str; ptr--) {
if (*ptr != '"')
continue;
*ptr = 0;
break;
}
return str;
}
static void setvar(char *line, const char *nm, char **var)
{
char *ptr;
if (!strncmp(line, nm, strlen(nm)) && (ptr = strchr(line, '='))) {
if (*var)
free(*var);
*var = strdup(strip_quotes(++ptr));
}
}
static void os_init(void)
{
struct utsname uts;
char line[80];
FILE *fp;
if (!uname(&uts)) {
os = strdup(uts.sysname);
ver = strdup(uts.release);
rel = strdup(uts.release);
sys = strdup(uts.machine);
}
fp = fopen("/etc/os-release", "r");
if (!fp) {
fp = fopen("/usr/lib/os-release", "r");
if (!fp)
return;
}
while (fgets(line, sizeof(line), fp)) {
line[strlen(line) - 1] = 0; /* drop \n */
setvar(line, "NAME", &os);
setvar(line, "VERSION_ID", &ver);
setvar(line, "BUILD_ID", &rel);
setvar(line, "ARCHITECTURE", &sys);
}
fclose(fp);
}
static char *fmtime(time_t t, char *buf, size_t len)
{
const char *isofmt = "%FT%T%z";
struct tm tm;
size_t i, n;
localtime_r(&t, &tm);
n = strftime(buf, len, isofmt, &tm);
i = n - 5;
if (buf[i] == '+' || buf[i] == '-') {
buf[i + 6] = buf[i + 5];
buf[i + 5] = buf[i + 4];
buf[i + 4] = buf[i + 3];
buf[i + 3] = ':';
}
return buf;
}
static int clock_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *path, const char *request_path, uint32_t request_id,
struct lyd_node **parent, void *priv)
{
static char boottime[64] = { 0 };
const struct ly_ctx *ctx;
char curtime[64];
time_t now, boot;
char *buf;
int rc;
DEBUG("path=%s, request_path=%s", path, request_path);
ctx = sr_acquire_context(sr_session_get_connection(session));
rc = lyd_new_path(NULL, ctx, CLOCK_PATH_, NULL, 0, parent);
if (rc) {
fail:
ERROR("Failed building data tree, libyang error %d", rc);
return SR_ERR_INTERNAL;
}
lyd_print_mem(&buf, *parent, LYD_XML, 0);
DEBUG("%s", buf);
now = time(NULL);
if (!boottime[0]) {
struct sysinfo si;
sysinfo(&si);
boot = now - si.uptime;
fmtime(boot, boottime, sizeof(boottime));
}
rc = lyd_new_path(*parent, NULL, CLOCK_PATH_ "/boot-datetime", boottime, 0, NULL);
if (rc)
goto fail;
fmtime(now, curtime, sizeof(curtime));
rc = lyd_new_path(*parent, NULL, CLOCK_PATH_ "/current-datetime", curtime, 0, NULL);
if (rc)
goto fail;
lyd_print_mem(&buf, *parent, LYD_XML, 0);
DEBUG("%s", buf);
return SR_ERR_OK;
}
static int platform_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *path, const char *request_path, uint32_t request_id,
struct lyd_node **parent, void *priv)
{
const struct ly_ctx *ctx;
char *buf;
int rc;
DEBUG("path=%s request_path=%s", path, request_path);
ctx = sr_acquire_context(sr_session_get_connection(session));
rc = lyd_new_path(NULL, ctx, PLATFORM_PATH_, NULL, 0, parent);
if (rc) {
fail:
ERROR("Failed building data tree, libyang error %d", rc);
return SR_ERR_INTERNAL;
}
lyd_print_mem(&buf, *parent, LYD_XML, 0);
DEBUG("%s", buf);
rc = lyd_new_path(*parent, NULL, PLATFORM_PATH_"/os-name", os, 0, NULL);
if (rc)
goto fail;
rc = lyd_new_path(*parent, NULL, PLATFORM_PATH_"/os-release", rel, 0, NULL);
if (rc)
goto fail;
rc = lyd_new_path(*parent, NULL, PLATFORM_PATH_"/os-version", ver, 0, NULL);
if (rc)
goto fail;
rc = lyd_new_path(*parent, NULL, PLATFORM_PATH_"/machine", sys, 0, NULL);
if (rc)
goto fail;
lyd_print_mem(&buf, *parent, LYD_XML, 0);
DEBUG("%s", buf);
return SR_ERR_OK;
}
static int rpc_exec(sr_session_ctx_t *session, uint32_t sub_id, const char *path,
const sr_val_t *input, const size_t input_cnt,
sr_event_t event, unsigned request_id,
sr_val_t **output, size_t *output_cnt,
void *priv)
{
DEBUG("path: %s", path);
if (run(priv))
return SR_ERR_INTERNAL;
return SR_ERR_OK;
}
/* '/ietf-system:set-current-date-time' */
static int rpc_set_datetime(sr_session_ctx_t *session, uint32_t sub_id,
const char *path, const sr_val_t *input,
const size_t input_cnt, sr_event_t event,
unsigned request_id, sr_val_t **output,
size_t *output_cnt, void *priv)
{
const char *isofmt = "%FT%T%z";
static int rc = SR_ERR_SYS;
struct timeval tv;
struct tm tm;
char tz[24];
char *buf;
size_t n;
buf = strdup(input->data.string_val);
if (!buf)
return SR_ERR_NO_MEMORY;
n = strlen(buf);
if (buf[n - 3] == ':' && (buf[n - 6] == '+' || buf[n - 6] == '-')) {
buf[n - 3] = buf[n - 2];
buf[n - 2] = buf[n - 1];
buf[n - 1] = 0;
} else
isofmt = "%FT%TZ";
memset(&tm, 0, sizeof(tm));
if (!strptime(buf, isofmt, &tm)) {
ERRNO("ietf-system:failed strptime: %s", strerror(errno));
goto done;
}
snprintf(tz, sizeof(tz), "UTC%s%ld", tm.tm_gmtoff > 0 ? "+" : "", tm.tm_gmtoff / 3600);
setenv("TZ", tz, 1);
tv.tv_sec = mktime(&tm);
tv.tv_usec = 0;
if (settimeofday(&tv, NULL)) {
ERRNO("ietf-system:settimeofday() failed");
goto done;
}
rc = SR_ERR_OK;
done:
unsetenv("TZ");
free(buf);
return rc;
}
static int sys_reload_services(void)
{
return run("initctl -nbq touch sysklogd lldpd");
}
int sr_get_int(sr_session_ctx_t *session, const char *fmt, ...)
{
sr_val_t *val = NULL;
char *xpath;
va_list ap;
int rc = 0;
int len;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap) + 1;
va_end(ap);
xpath = alloca(len);
if (!xpath)
goto fail;
va_start(ap, fmt);
vsnprintf(xpath, len, fmt, ap);
va_end(ap);
if (sr_get_item(session, xpath, 0, &val) || !val)
goto fail;
switch (val->type) {
case SR_INT8_T:
rc = val->data.int8_val;
break;
case SR_UINT8_T:
rc = val->data.uint8_val;
break;
case SR_INT16_T:
rc = val->data.int16_val;
break;
case SR_UINT16_T:
rc = val->data.uint16_val;
break;
case SR_INT32_T:
rc = val->data.int32_val;
break;
case SR_UINT32_T:
rc = val->data.uint32_val;
break;
case SR_INT64_T:
rc = val->data.int64_val;
break;
case SR_UINT64_T:
rc = val->data.uint64_val;
break;
default:
goto fail;
}
fail:
if (val)
sr_free_val(val);
return rc;
}
int sr_get_bool(sr_session_ctx_t *session, const char *fmt, ...)
{
sr_val_t *val = NULL;
char *xpath;
va_list ap;
int rc = 0;
int len;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap) + 1;
va_end(ap);
xpath = alloca(len);
if (!xpath)
goto fail;
va_start(ap, fmt);
vsnprintf(xpath, len, fmt, ap);
va_end(ap);
if (sr_get_item(session, xpath, 0, &val))
goto fail;
if (!val || val->type != SR_BOOL_T)
goto fail;
rc = val->data.bool_val;
fail:
if (val)
sr_free_val(val);
return rc;
}
char *sr_get_str(sr_session_ctx_t *session, const char *fmt, ...)
{
char *str = NULL;
sr_val_t *val;
char *xpath;
va_list ap;
int len;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap) + 1;
va_end(ap);
xpath = alloca(len);
if (!xpath)
return NULL;
va_start(ap, fmt);
vsnprintf(xpath, len, fmt, ap);
va_end(ap);
if (sr_get_item(session, xpath, 0, &val)) {
ERROR("Failed reading string value from xpath %s", xpath);
goto fail;
}
if (!val || val->type != SR_STRING_T)
goto fail;
str = strdup(val->data.string_val);
fail:
if (val)
sr_free_val(val);
return str;
}
#define TIMEZONE_CONF "/etc/timezone"
#define TIMEZONE_PREV TIMEZONE_CONF "-"
#define TIMEZONE_NEXT TIMEZONE_CONF "+"
static int change_clock(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
char *timezone;
FILE *fp;
switch (event) {
case SR_EV_ENABLED: /* first time, on register. */
case SR_EV_CHANGE: /* regular change (copy cand running) */
/* Set up next timezone */
break;
case SR_EV_ABORT: /* User abort, or other plugin failed */
remove(TIMEZONE_NEXT);
return SR_ERR_OK;
case SR_EV_DONE:
/* Check if passed validation in previous event */
if (access(TIMEZONE_NEXT, F_OK))
return SR_ERR_OK;
remove(TIMEZONE_PREV);
rename(TIMEZONE_CONF, TIMEZONE_PREV);
rename(TIMEZONE_NEXT, TIMEZONE_CONF);
remove("/etc/localtime-");
rename("/etc/localtime", "/etc/localtime-");
rename("/etc/localtime+", "/etc/localtime");
return SR_ERR_OK;
default:
return SR_ERR_OK;
}
/* XXX: add support also for /ietf-system:system/clock/timezone-utc-offset (deviation) */
timezone = sr_get_str(session, "/ietf-system:system/clock/timezone-name");
if (!timezone) {
ERROR("Failed reading timezone-name");
return SR_ERR_VALIDATION_FAILED;
}
remove("/etc/localtime+");
if (run("ln -s /usr/share/zoneinfo/%s /etc/localtime+", timezone)) {
ERROR("No such timezone %s", timezone);
return SR_ERR_VALIDATION_FAILED;
}
fp = fopen(TIMEZONE_NEXT, "w");
if (!fp) {
ERRNO("Failed preparing %s", TIMEZONE_NEXT);
return SR_ERR_SYS;
}
fprintf(fp, "%s\n", timezone);
fclose(fp);
return SR_ERR_OK;
}
#define CHRONY_CONF "/etc/chrony.conf"
#define CHRONY_PREV CHRONY_CONF "-"
#define CHRONY_NEXT CHRONY_CONF "+"
static int change_ntp(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
const char *fn = CHRONY_NEXT;
int valid = -1;
sr_val_t *val;
size_t cnt;
FILE *fp;
int rc;
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 */
remove(CHRONY_NEXT);
return SR_ERR_OK;
case SR_EV_DONE:
/* Check if passed validation in previous event */
if (access(CHRONY_NEXT, F_OK))
return SR_ERR_OK;
remove(CHRONY_PREV);
rename(CHRONY_CONF, CHRONY_PREV);
rename(CHRONY_NEXT, CHRONY_CONF);
if (!sr_get_bool(session, "/ietf-system:system/ntp/enabled")) {
run("initctl -nbq disable chronyd");
return SR_ERR_OK;
}
/*
* If chrony is alrady enabled we tell Finit it's been
* modified , so Finit restarts it, otherwise enable it.
*/
run("initctl -nbq touch chronyd");
run("initctl -nbq enable chronyd");
return SR_ERR_OK;
default:
return SR_ERR_OK;
}
rc = sr_get_items(session, "/ietf-system:system/ntp/server", 0, 0, &val, &cnt);
if (rc) {
remove(CHRONY_NEXT);
return rc;
}
fp = fopen(fn, "w");
if (!fp) {
ERROR("failed updating %s: %s", fn, strerror(errno));
sr_free_values(val, cnt);
return SR_ERR_SYS;
}
for (size_t i = 0; i < cnt; i++) {
const char *xpath = val[i].xpath;
char *type, *ptr;
int server = 0;
/*
* Handle empty startup-config on SR_EV_ENABLED,
* prevents subscribe failure due to false invalid.
*/
if (i == 0)
valid = 0;
/* Get /ietf-system:system/ntp/server[name='foo'] */
ptr = sr_get_str(session, "%s/udp/address", xpath);
if (ptr) {
type = sr_get_str(session, "%s/association-type", xpath);
fprintf(fp, "%s %s", type ?: "server", ptr);
server++;
if (type)
free(type);
free(ptr);
ptr = sr_get_str(session, "%s/udp/port", xpath);
if (ptr) {
fprintf(fp, " port %s", ptr);
free(ptr);
}
}
if (server) {
if (sr_get_bool(session, "%s/iburst", xpath))
fprintf(fp, " iburst");
if (sr_get_bool(session, "%s/prefer", xpath))
fprintf(fp, " prefer");
fprintf(fp, "\n");
valid++;
}
}
sr_free_values(val, cnt);
fprintf(fp, "driftfile /var/lib/chrony/drift\n");
fprintf(fp, "makestep 1.0 3\n");
fprintf(fp, "maxupdateskew 100.0\n");
fprintf(fp, "dumpdir /var/lib/chrony\n");
fprintf(fp, "rtcfile /var/lib/chrony/rtc\n");
fclose(fp);
if (!valid) {
remove(fn);
return SR_ERR_VALIDATION_FAILED;
}
return SR_ERR_OK;
}
#define RESOLV_CONF "/etc/resolv.conf.head"
#define RESOLV_PREV RESOLV_CONF "-"
#define RESOLV_NEXT RESOLV_CONF "+"
static int change_dns(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
const char *fn = RESOLV_NEXT;
int timeout, attempts;
int rc = SR_ERR_SYS;
sr_val_t *val;
size_t cnt;
FILE *fp;
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 */
remove(RESOLV_NEXT);
return SR_ERR_OK;
case SR_EV_DONE:
/* Check if passed validation in previous event */
if (access(RESOLV_NEXT, F_OK))
return SR_ERR_OK;
remove(RESOLV_PREV);
rename(RESOLV_CONF, RESOLV_PREV);
rename(RESOLV_NEXT, RESOLV_CONF);
/* in bootstrap, another resolvconf will soon take your call */
if (run("initctl cond get hook/sys/up"))
return 0;
run("resolvconf -u");
return SR_ERR_OK;
default:
return SR_ERR_OK;
}
fp = fopen(fn, "w");
if (!fp) {
ERROR("failed updating %s: %s", fn, strerror(errno));
sr_free_values(val, cnt);
return SR_ERR_SYS;
}
timeout = sr_get_int(session, "/ietf-system:system/dns-resolver/options/timeout");
attempts = sr_get_int(session, "/ietf-system:system/dns-resolver/options/attempts");
if (timeout || attempts) {
fprintf(fp, "options");
if (timeout)
fprintf(fp, " timeout:%d", timeout);
if (attempts)
fprintf(fp, " attempts:%d ", attempts);
fprintf(fp, "\n");
}
rc = sr_get_items(session, "/ietf-system:system/dns-resolver/search", 0, 0, &val, &cnt);
if (rc)
goto fail;
if (cnt) {
fprintf(fp, "search ");
for (size_t i = 0; i < cnt; i++)
fprintf(fp, "%s ", val[i].data.string_val);
fprintf(fp, "\n");
}
sr_free_values(val, cnt);
rc = sr_get_items(session, "/ietf-system:system/dns-resolver/server", 0, 0, &val, &cnt);
if (rc)
goto fail;
for (size_t i = 0; i < cnt; i++) {
const char *xpath = val[i].xpath;
char *ptr;
/* Get /ietf-system:system/dns-resolver/server[name='foo'] */
ptr = sr_get_str(session, "%s/udp-and-tcp/address", xpath);
if (ptr)
/* XXX: add support also for udp-and-tcp/port */
fprintf(fp, "nameserver %s\n", ptr);
}
sr_free_values(val, cnt);
rc = SR_ERR_OK;
fail:
fclose(fp);
if (rc)
remove(fn);
return rc;
}
static int change_motd(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
char *nm;
FILE *fp;
/* Ignore all events except SR_EV_DONE */
if (event != SR_EV_DONE)
return SR_ERR_OK;
fp = fopen("/etc/motd", "w");
if (!fp)
return SR_ERR_SYS;
nm = sr_get_str(session, xpath);
if (nm) {
fprintf(fp, "%s\n", nm);
} else {
/* XXX: derive from global "options.h" or /usr/share/factory/ */
fprintf(fp, "\033[1;90mNote:\033[0m ");
fprintf(fp, "\033[0;90m use help, show, and setup commands to set up and diagnose the system\033[0m\n");
}
fclose(fp);
return SR_ERR_OK;
}
static int change_hostname(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *priv)
{
struct confd *confd = (struct confd *)priv;
const char *host, *tmp = NULL;
char **hosts, *current;
int err, i, nhosts;
char *nm;
if (event != SR_EV_DONE)
return SR_ERR_OK;
nm = sr_get_str(session, xpath);
if (!nm) {
/* XXX: derive from global "options.h" or /usr/share/factory/ */
nm = strdup("infix");
if (!nm)
goto err;
}
if (aug_get(confd->aug, "etc/hostname/hostname", &tmp) <= 0)
goto err;
current = strdup(tmp);
if (!current)
goto err;
err = sethostname(nm, strlen(nm));
err = err ? : aug_set(confd->aug, "etc/hostname/hostname", nm);
nhosts = aug_match(confd->aug, "etc/hosts/*/canonical", &hosts);
for (i = 0; i < nhosts; i++) {
if (aug_get(confd->aug, hosts[i], &host) <= 0)
continue;
if (!strcmp(host, current))
err = err ? : aug_set(confd->aug, hosts[i], nm);
free(hosts[i]);
}
if (nhosts)
free(hosts);
free(current);
err:
if (nm)
free(nm);
if (err) {
ERROR("Failed activating changes.");
return err;
}
ERROR("Reload hostname-dependent services ...");
err = err ? : aug_save(confd->aug);
if (sys_reload_services())
return SR_ERR_SYS;
return SR_ERR_OK;
}
int ietf_system_init(struct confd *confd)
{
const char *features[] = {
"ntp",
"ntp-udp-port",
"timezone-name",
NULL
};
int rc;
os_init();
if (aug_load_file(confd->aug, "/etc/hostname") ||
aug_load_file(confd->aug, "/etc/hosts")) {
ERROR("ietf-system: Augeas initialization failed");
goto err;
}
rc = sr_install_module(confd->conn, YANG_PATH_"ietf-system@2014-08-06.yang", NULL, features);
if (rc)
goto err;
/* Augment to ietf-systems */
rc = sr_install_module(confd->conn, YANG_PATH_"infix-system@2014-08-06.yang", NULL, NULL);
if (rc)
goto err;
rc = sr_oper_get_subscribe(confd->session, "ietf-system", CLOCK_PATH_,
clock_cb, NULL, SR_SUBSCR_DEFAULT, &confd->sub);
if (rc != SR_ERR_OK)
goto err;
rc = sr_oper_get_subscribe(confd->session, "ietf-system", PLATFORM_PATH_,
platform_cb, NULL, SR_SUBSCR_DEFAULT, &confd->sub);
if (rc != SR_ERR_OK)
goto err;
REGISTER_CHANGE(confd->session, "ietf-system", "/ietf-system:system/hostname", 0, change_hostname, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "ietf-system", "/ietf-system:system/infix-system:motd", 0, change_motd, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "ietf-system", "/ietf-system:system/clock", 0, change_clock, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "ietf-system", "/ietf-system:system/ntp", 0, change_ntp, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "ietf-system", "/ietf-system:system/dns-resolver", 0, change_dns, confd, &confd->sub);
REGISTER_RPC(confd->session, "/ietf-system:system-restart", rpc_exec, "reboot", &confd->sub);
REGISTER_RPC(confd->session, "/ietf-system:system-shutdown", rpc_exec, "poweroff", &confd->sub);
REGISTER_RPC(confd->session, "/ietf-system:set-current-datetime", rpc_set_datetime, NULL, &confd->sub);
return SR_ERR_OK;
err:
ERROR("init failed: %s", sr_strerror(rc));
sr_unsubscribe(confd->sub);
return rc;
}
+52
View File
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: BSD-3-Clause */
#include "core.h"
/**
* Like system(), but takes a formatted string as argument.
* @param fmt printf style format list to command to run
*
* This system() wrapper greatly simplifies operations that usually
* consist of composing a command from parts into a dynamic buffer
* before calling it. The return value from system() is also parsed,
* checking for proper exit and signals.
*
* @returns If the command exits normally, the return code of the command
* is returned. Otherwise, if the command is signalled, the return code
* is -1 and @a errno is set to @c EINTR.
*/
int run(const char *fmt, ...)
{
va_list ap;
char *cmd;
int len;
int rc;
va_start(ap, fmt);
len = vsnprintf(NULL, 0, fmt, ap);
va_end(ap);
cmd = alloca(++len);
if (!cmd) {
errno = ENOMEM;
return -1;
}
va_start(ap, fmt);
vsnprintf(cmd, len, fmt, ap);
va_end(ap);
rc = system(cmd);
if (rc == -1)
return -1;
if (WIFEXITED(rc)) {
errno = 0;
rc = WEXITSTATUS(rc);
} else if (WIFSIGNALED(rc)) {
errno = EINTR;
rc = -1;
}
return rc;
}
+2
View File
@@ -0,0 +1,2 @@
yangdir = $(YANGDIR)
yang_DATA = $(wildcard *.yang)
@@ -0,0 +1,120 @@
module iana-crypt-hash {
namespace "urn:ietf:params:xml:ns:yang:iana-crypt-hash";
prefix ianach;
organization "IANA";
contact
" Internet Assigned Numbers Authority
Postal: ICANN
12025 Waterfront Drive, Suite 300
Los Angeles, CA 90094-2536
United States
Tel: +1 310 301 5800
E-Mail: iana@iana.org>";
description
"This YANG module defines a type for storing passwords
using a hash function and features to indicate which hash
functions are supported by an implementation.
The latest revision of this YANG module can be obtained from
the IANA web site.
Requests for new values should be made to IANA via
email (iana@iana.org).
Copyright (c) 2014 IETF Trust and the persons identified as
authors of the code. 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).
The initial version of this YANG module is part of RFC 7317;
see the RFC itself for full legal notices.";
revision 2014-08-06 {
description
"Initial revision.";
reference
"RFC 7317: A YANG Data Model for System Management";
}
typedef crypt-hash {
type string {
pattern
'$0$.*'
+ '|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}'
+ '|$5$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}'
+ '|$6$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}';
}
description
"The crypt-hash type is used to store passwords using
a hash function. The algorithms for applying the hash
function and encoding the result are implemented in
various UNIX systems as the function crypt(3).
A value of this type matches one of the forms:
$0$<clear text password>
$<id>$<salt>$<password hash>
$<id>$<parameter>$<salt>$<password hash>
The '$0$' prefix signals that the value is clear text. When
such a value is received by the server, a hash value is
calculated, and the string '$<id>$<salt>$' or
$<id>$<parameter>$<salt>$ is prepended to the result. This
value is stored in the configuration data store.
If a value starting with '$<id>$', where <id> is not '0', is
received, the server knows that the value already represents a
hashed value and stores it 'as is' in the data store.
When a server needs to verify a password given by a user, it
finds the stored password hash string for that user, extracts
the salt, and calculates the hash with the salt and given
password as input. If the calculated hash value is the same
as the stored value, the password given by the client is
accepted.
This type defines the following hash functions:
id | hash function | feature
---+---------------+-------------------
1 | MD5 | crypt-hash-md5
5 | SHA-256 | crypt-hash-sha-256
6 | SHA-512 | crypt-hash-sha-512
The server indicates support for the different hash functions
by advertising the corresponding feature.";
reference
"IEEE Std 1003.1-2008 - crypt() function
RFC 1321: The MD5 Message-Digest Algorithm
FIPS.180-4.2012: Secure Hash Standard (SHS)";
}
feature crypt-hash-md5 {
description
"Indicates that the device supports the MD5
hash function in 'crypt-hash' values.";
reference "RFC 1321: The MD5 Message-Digest Algorithm";
}
feature crypt-hash-sha-256 {
description
"Indicates that the device supports the SHA-256
hash function in 'crypt-hash' values.";
reference "FIPS.180-4.2012: Secure Hash Standard (SHS)";
}
feature crypt-hash-sha-512 {
description
"Indicates that the device supports the SHA-512
hash function in 'crypt-hash' values.";
reference "FIPS.180-4.2012: Secure Hash Standard (SHS)";
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,117 @@
module ietf-datastores {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-datastores";
prefix ds;
organization
"IETF Network Modeling (NETMOD) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
Author: Martin Bjorklund
<mailto:mbj@tail-f.com>
Author: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>
Author: Phil Shafer
<mailto:phil@juniper.net>
Author: Kent Watsen
<mailto:kwatsen@juniper.net>
Author: Rob Wilton
<rwilton@cisco.com>";
description
"This YANG module defines a set of identities for identifying
datastores.
Copyright (c) 2018 IETF Trust and the persons identified as
authors of the code. 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
(https://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 8342
(https://www.rfc-editor.org/info/rfc8342); see the RFC itself
for full legal notices.";
revision 2018-02-14 {
description
"Initial revision.";
reference
"RFC 8342: Network Management Datastore Architecture (NMDA)";
}
/*
* Identities
*/
identity datastore {
description
"Abstract base identity for datastore identities.";
}
identity conventional {
base datastore;
description
"Abstract base identity for conventional configuration
datastores.";
}
identity running {
base conventional;
description
"The running configuration datastore.";
}
identity candidate {
base conventional;
description
"The candidate configuration datastore.";
}
identity startup {
base conventional;
description
"The startup configuration datastore.";
}
identity intended {
base conventional;
description
"The intended configuration datastore.";
}
identity dynamic {
base datastore;
description
"Abstract base identity for dynamic configuration datastores.";
}
identity operational {
base datastore;
description
"The operational state datastore.";
}
/*
* Type definitions
*/
typedef datastore-ref {
type identityref {
base datastore;
}
description
"A datastore identity reference.";
}
}
@@ -0,0 +1,458 @@
module ietf-inet-types {
namespace "urn:ietf:params:xml:ns:yang:ietf-inet-types";
prefix "inet";
organization
"IETF NETMOD (NETCONF Data Modeling Language) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
WG Chair: David Kessens
<mailto:david.kessens@nsn.com>
WG Chair: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>
Editor: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>";
description
"This module contains a collection of generally useful derived
YANG data types for Internet addresses and related things.
Copyright (c) 2013 IETF Trust and the persons identified as
authors of the code. 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 6991; see
the RFC itself for full legal notices.";
revision 2013-07-15 {
description
"This revision adds the following new data types:
- ip-address-no-zone
- ipv4-address-no-zone
- ipv6-address-no-zone";
reference
"RFC 6991: Common YANG Data Types";
}
revision 2010-09-24 {
description
"Initial revision.";
reference
"RFC 6021: Common YANG Data Types";
}
/*** collection of types related to protocol fields ***/
typedef ip-version {
type enumeration {
enum unknown {
value "0";
description
"An unknown or unspecified version of the Internet
protocol.";
}
enum ipv4 {
value "1";
description
"The IPv4 protocol as defined in RFC 791.";
}
enum ipv6 {
value "2";
description
"The IPv6 protocol as defined in RFC 2460.";
}
}
description
"This value represents the version of the IP protocol.
In the value set and its semantics, this type is equivalent
to the InetVersion textual convention of the SMIv2.";
reference
"RFC 791: Internet Protocol
RFC 2460: Internet Protocol, Version 6 (IPv6) Specification
RFC 4001: Textual Conventions for Internet Network Addresses";
}
typedef dscp {
type uint8 {
range "0..63";
}
description
"The dscp type represents a Differentiated Services Code Point
that may be used for marking packets in a traffic stream.
In the value set and its semantics, this type is equivalent
to the Dscp textual convention of the SMIv2.";
reference
"RFC 3289: Management Information Base for the Differentiated
Services Architecture
RFC 2474: Definition of the Differentiated Services Field
(DS Field) in the IPv4 and IPv6 Headers
RFC 2780: IANA Allocation Guidelines For Values In
the Internet Protocol and Related Headers";
}
typedef ipv6-flow-label {
type uint32 {
range "0..1048575";
}
description
"The ipv6-flow-label type represents the flow identifier or Flow
Label in an IPv6 packet header that may be used to
discriminate traffic flows.
In the value set and its semantics, this type is equivalent
to the IPv6FlowLabel textual convention of the SMIv2.";
reference
"RFC 3595: Textual Conventions for IPv6 Flow Label
RFC 2460: Internet Protocol, Version 6 (IPv6) Specification";
}
typedef port-number {
type uint16 {
range "0..65535";
}
description
"The port-number type represents a 16-bit port number of an
Internet transport-layer protocol such as UDP, TCP, DCCP, or
SCTP. Port numbers are assigned by IANA. A current list of
all assignments is available from <http://www.iana.org/>.
Note that the port number value zero is reserved by IANA. In
situations where the value zero does not make sense, it can
be excluded by subtyping the port-number type.
In the value set and its semantics, this type is equivalent
to the InetPortNumber textual convention of the SMIv2.";
reference
"RFC 768: User Datagram Protocol
RFC 793: Transmission Control Protocol
RFC 4960: Stream Control Transmission Protocol
RFC 4340: Datagram Congestion Control Protocol (DCCP)
RFC 4001: Textual Conventions for Internet Network Addresses";
}
/*** collection of types related to autonomous systems ***/
typedef as-number {
type uint32;
description
"The as-number type represents autonomous system numbers
which identify an Autonomous System (AS). An AS is a set
of routers under a single technical administration, using
an interior gateway protocol and common metrics to route
packets within the AS, and using an exterior gateway
protocol to route packets to other ASes. IANA maintains
the AS number space and has delegated large parts to the
regional registries.
Autonomous system numbers were originally limited to 16
bits. BGP extensions have enlarged the autonomous system
number space to 32 bits. This type therefore uses an uint32
base type without a range restriction in order to support
a larger autonomous system number space.
In the value set and its semantics, this type is equivalent
to the InetAutonomousSystemNumber textual convention of
the SMIv2.";
reference
"RFC 1930: Guidelines for creation, selection, and registration
of an Autonomous System (AS)
RFC 4271: A Border Gateway Protocol 4 (BGP-4)
RFC 4001: Textual Conventions for Internet Network Addresses
RFC 6793: BGP Support for Four-Octet Autonomous System (AS)
Number Space";
}
/*** collection of types related to IP addresses and hostnames ***/
typedef ip-address {
type union {
type inet:ipv4-address;
type inet:ipv6-address;
}
description
"The ip-address type represents an IP address and is IP
version neutral. The format of the textual representation
implies the IP version. This type supports scoped addresses
by allowing zone identifiers in the address format.";
reference
"RFC 4007: IPv6 Scoped Address Architecture";
}
typedef ipv4-address {
type string {
pattern
'(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ '(%[\p{N}\p{L}]+)?';
}
description
"The ipv4-address type represents an IPv4 address in
dotted-quad notation. The IPv4 address may include a zone
index, separated by a % sign.
The zone index is used to disambiguate identical address
values. For link-local addresses, the zone index will
typically be the interface index number or the name of an
interface. If the zone index is not present, the default
zone of the device will be used.
The canonical format for the zone index is the numerical
format";
}
typedef ipv6-address {
type string {
pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ '(%[\p{N}\p{L}]+)?';
pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ '(%.+)?';
}
description
"The ipv6-address type represents an IPv6 address in full,
mixed, shortened, and shortened-mixed notation. The IPv6
address may include a zone index, separated by a % sign.
The zone index is used to disambiguate identical address
values. For link-local addresses, the zone index will
typically be the interface index number or the name of an
interface. If the zone index is not present, the default
zone of the device will be used.
The canonical format of IPv6 addresses uses the textual
representation defined in Section 4 of RFC 5952. The
canonical format for the zone index is the numerical
format as described in Section 11.2 of RFC 4007.";
reference
"RFC 4291: IP Version 6 Addressing Architecture
RFC 4007: IPv6 Scoped Address Architecture
RFC 5952: A Recommendation for IPv6 Address Text
Representation";
}
typedef ip-address-no-zone {
type union {
type inet:ipv4-address-no-zone;
type inet:ipv6-address-no-zone;
}
description
"The ip-address-no-zone type represents an IP address and is
IP version neutral. The format of the textual representation
implies the IP version. This type does not support scoped
addresses since it does not allow zone identifiers in the
address format.";
reference
"RFC 4007: IPv6 Scoped Address Architecture";
}
typedef ipv4-address-no-zone {
type inet:ipv4-address {
pattern '[0-9\.]*';
}
description
"An IPv4 address without a zone index. This type, derived from
ipv4-address, may be used in situations where the zone is
known from the context and hence no zone index is needed.";
}
typedef ipv6-address-no-zone {
type inet:ipv6-address {
pattern '[0-9a-fA-F:\.]*';
}
description
"An IPv6 address without a zone index. This type, derived from
ipv6-address, may be used in situations where the zone is
known from the context and hence no zone index is needed.";
reference
"RFC 4291: IP Version 6 Addressing Architecture
RFC 4007: IPv6 Scoped Address Architecture
RFC 5952: A Recommendation for IPv6 Address Text
Representation";
}
typedef ip-prefix {
type union {
type inet:ipv4-prefix;
type inet:ipv6-prefix;
}
description
"The ip-prefix type represents an IP prefix and is IP
version neutral. The format of the textual representations
implies the IP version.";
}
typedef ipv4-prefix {
type string {
pattern
'(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])'
+ '/(([0-9])|([1-2][0-9])|(3[0-2]))';
}
description
"The ipv4-prefix type represents an IPv4 address prefix.
The prefix length is given by the number following the
slash character and must be less than or equal to 32.
A prefix length value of n corresponds to an IP address
mask that has n contiguous 1-bits from the most
significant bit (MSB) and all other bits set to 0.
The canonical format of an IPv4 prefix has all bits of
the IPv4 address set to zero that are not part of the
IPv4 prefix.";
}
typedef ipv6-prefix {
type string {
pattern '((:|[0-9a-fA-F]{0,4}):)([0-9a-fA-F]{0,4}:){0,5}'
+ '((([0-9a-fA-F]{0,4}:)?(:|[0-9a-fA-F]{0,4}))|'
+ '(((25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])\.){3}'
+ '(25[0-5]|2[0-4][0-9]|[01]?[0-9]?[0-9])))'
+ '(/(([0-9])|([0-9]{2})|(1[0-1][0-9])|(12[0-8])))';
pattern '(([^:]+:){6}(([^:]+:[^:]+)|(.*\..*)))|'
+ '((([^:]+:)*[^:]+)?::(([^:]+:)*[^:]+)?)'
+ '(/.+)';
}
description
"The ipv6-prefix type represents an IPv6 address prefix.
The prefix length is given by the number following the
slash character and must be less than or equal to 128.
A prefix length value of n corresponds to an IP address
mask that has n contiguous 1-bits from the most
significant bit (MSB) and all other bits set to 0.
The IPv6 address should have all bits that do not belong
to the prefix set to zero.
The canonical format of an IPv6 prefix has all bits of
the IPv6 address set to zero that are not part of the
IPv6 prefix. Furthermore, the IPv6 address is represented
as defined in Section 4 of RFC 5952.";
reference
"RFC 5952: A Recommendation for IPv6 Address Text
Representation";
}
/*** collection of domain name and URI types ***/
typedef domain-name {
type string {
pattern
'((([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.)*'
+ '([a-zA-Z0-9_]([a-zA-Z0-9\-_]){0,61})?[a-zA-Z0-9]\.?)'
+ '|\.';
length "1..253";
}
description
"The domain-name type represents a DNS domain name. The
name SHOULD be fully qualified whenever possible.
Internet domain names are only loosely specified. Section
3.5 of RFC 1034 recommends a syntax (modified in Section
2.1 of RFC 1123). The pattern above is intended to allow
for current practice in domain name use, and some possible
future expansion. It is designed to hold various types of
domain names, including names used for A or AAAA records
(host names) and other records, such as SRV records. Note
that Internet host names have a stricter syntax (described
in RFC 952) than the DNS recommendations in RFCs 1034 and
1123, and that systems that want to store host names in
schema nodes using the domain-name type are recommended to
adhere to this stricter standard to ensure interoperability.
The encoding of DNS names in the DNS protocol is limited
to 255 characters. Since the encoding consists of labels
prefixed by a length bytes and there is a trailing NULL
byte, only 253 characters can appear in the textual dotted
notation.
The description clause of schema nodes using the domain-name
type MUST describe when and how these names are resolved to
IP addresses. Note that the resolution of a domain-name value
may require to query multiple DNS records (e.g., A for IPv4
and AAAA for IPv6). The order of the resolution process and
which DNS record takes precedence can either be defined
explicitly or may depend on the configuration of the
resolver.
Domain-name values use the US-ASCII encoding. Their canonical
format uses lowercase US-ASCII characters. Internationalized
domain names MUST be A-labels as per RFC 5890.";
reference
"RFC 952: DoD Internet Host Table Specification
RFC 1034: Domain Names - Concepts and Facilities
RFC 1123: Requirements for Internet Hosts -- Application
and Support
RFC 2782: A DNS RR for specifying the location of services
(DNS SRV)
RFC 5890: Internationalized Domain Names in Applications
(IDNA): Definitions and Document Framework";
}
typedef host {
type union {
type inet:ip-address;
type inet:domain-name;
}
description
"The host type represents either an IP address or a DNS
domain name.";
}
typedef uri {
type string;
description
"The uri type represents a Uniform Resource Identifier
(URI) as defined by STD 66.
Objects using the uri type MUST be in US-ASCII encoding,
and MUST be normalized as described by RFC 3986 Sections
6.2.1, 6.2.2.1, and 6.2.2.2. All unnecessary
percent-encoding is removed, and all case-insensitive
characters are set to lowercase except for hexadecimal
digits, which are normalized to uppercase as described in
Section 6.2.2.1.
The purpose of this normalization is to help provide
unique URIs. Note that this normalization is not
sufficient to provide uniqueness. Two URIs that are
textually distinct after this normalization may still be
equivalent.
Objects using the uri type may restrict the schemes that
they permit. For example, 'data:' and 'urn:' schemes
might not be appropriate.
A zero-length URI is not a valid URI. This can be used to
express 'URI absent' where required.
In the value set and its semantics, this type is equivalent
to the Uri SMIv2 textual convention defined in RFC 5017.";
reference
"RFC 3986: Uniform Resource Identifier (URI): Generic Syntax
RFC 3305: Report from the Joint W3C/IETF URI Planning Interest
Group: Uniform Resource Identifiers (URIs), URLs,
and Uniform Resource Names (URNs): Clarifications
and Recommendations
RFC 5017: MIB Textual Conventions for Uniform Resource
Identifiers (URIs)";
}
}
File diff suppressed because it is too large Load Diff
+876
View File
@@ -0,0 +1,876 @@
module ietf-ip {
yang-version 1.1;
namespace "urn:ietf:params:xml:ns:yang:ietf-ip";
prefix ip;
import ietf-interfaces {
prefix if;
}
import ietf-inet-types {
prefix inet;
}
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETMOD (Network Modeling) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"This module contains a collection of YANG definitions for
managing IP implementations.
Copyright (c) 2018 IETF Trust and the persons identified as
authors of the code. 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
(https://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 8344; see
the RFC itself for full legal notices.";
revision 2018-02-22 {
description
"Updated to support NMDA.";
reference
"RFC 8344: A YANG Data Model for IP Management";
}
revision 2014-06-16 {
description
"Initial revision.";
reference
"RFC 7277: A YANG Data Model for IP Management";
}
/*
* Features
*/
feature ipv4-non-contiguous-netmasks {
description
"Indicates support for configuring non-contiguous
subnet masks.";
}
feature ipv6-privacy-autoconf {
description
"Indicates support for privacy extensions for stateless address
autoconfiguration in IPv6.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6";
}
/*
* Typedefs
*/
typedef ip-address-origin {
type enumeration {
enum other {
description
"None of the following.";
}
enum static {
description
"Indicates that the address has been statically
configured -- for example, using the Network Configuration
Protocol (NETCONF) or a command line interface.";
}
enum dhcp {
description
"Indicates an address that has been assigned to this
system by a DHCP server.";
}
enum link-layer {
description
"Indicates an address created by IPv6 stateless
autoconfiguration that embeds a link-layer address in its
interface identifier.";
}
enum random {
description
"Indicates an address chosen by the system at
random, e.g., an IPv4 address within 169.254/16, a
temporary address as described in RFC 4941, or a
semantically opaque address as described in RFC 7217.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
RFC 7217: A Method for Generating Semantically Opaque
Interface Identifiers with IPv6 Stateless
Address Autoconfiguration (SLAAC)";
}
}
description
"The origin of an address.";
}
typedef neighbor-origin {
type enumeration {
enum other {
description
"None of the following.";
}
enum static {
description
"Indicates that the mapping has been statically
configured -- for example, using NETCONF or a command line
interface.";
}
enum dynamic {
description
"Indicates that the mapping has been dynamically resolved
using, for example, IPv4 ARP or the IPv6 Neighbor
Discovery protocol.";
}
}
description
"The origin of a neighbor entry.";
}
/*
* Data nodes
*/
augment "/if:interfaces/if:interface" {
description
"IP parameters on interfaces.
If an interface is not capable of running IP, the server
must not allow the client to configure these parameters.";
container ipv4 {
presence
"Enables IPv4 unless the 'enabled' leaf
(which defaults to 'true') is set to 'false'";
description
"Parameters for the IPv4 address family.";
leaf enabled {
type boolean;
default true;
description
"Controls whether IPv4 is enabled or disabled on this
interface. When IPv4 is enabled, this interface is
connected to an IPv4 stack, and the interface can send
and receive IPv4 packets.";
}
leaf forwarding {
type boolean;
default false;
description
"Controls IPv4 packet forwarding of datagrams received by,
but not addressed to, this interface. IPv4 routers
forward datagrams. IPv4 hosts do not (except those
source-routed via the host).";
}
leaf mtu {
type uint16 {
range "68..max";
}
units "octets";
description
"The size, in octets, of the largest IPv4 packet that the
interface will send and receive.
The server may restrict the allowed values for this leaf,
depending on the interface's type.
If this leaf is not configured, the operationally used MTU
depends on the interface's type.";
reference
"RFC 791: Internet Protocol";
}
list address {
key "ip";
description
"The list of IPv4 addresses on the interface.";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address on the interface.";
}
choice subnet {
mandatory true;
description
"The subnet can be specified as a prefix length or,
if the server supports non-contiguous netmasks, as
a netmask.";
leaf prefix-length {
type uint8 {
range "0..32";
}
description
"The length of the subnet prefix.";
}
leaf netmask {
if-feature ipv4-non-contiguous-netmasks;
type yang:dotted-quad;
description
"The subnet specified as a netmask.";
}
}
leaf origin {
type ip-address-origin;
config false;
description
"The origin of this address.";
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv4 addresses to
link-layer addresses.
Entries in this list in the intended configuration are
used as static entries in the ARP Cache.
In the operational state, this list represents the ARP
Cache.";
reference
"RFC 826: An Ethernet Address Resolution Protocol";
leaf ip {
type inet:ipv4-address-no-zone;
description
"The IPv4 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
mandatory true;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
config false;
description
"The origin of this neighbor entry.";
}
}
}
container ipv6 {
presence
"Enables IPv6 unless the 'enabled' leaf
(which defaults to 'true') is set to 'false'";
description
"Parameters for the IPv6 address family.";
leaf enabled {
type boolean;
default true;
description
"Controls whether IPv6 is enabled or disabled on this
interface. When IPv6 is enabled, this interface is
connected to an IPv6 stack, and the interface can send
and receive IPv6 packets.";
}
leaf forwarding {
type boolean;
default false;
description
"Controls IPv6 packet forwarding of datagrams received by,
but not addressed to, this interface. IPv6 routers
forward datagrams. IPv6 hosts do not (except those
source-routed via the host).";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 6.2.1, IsRouter";
}
leaf mtu {
type uint32 {
range "1280..max";
}
units "octets";
description
"The size, in octets, of the largest IPv6 packet that the
interface will send and receive.
The server may restrict the allowed values for this leaf,
depending on the interface's type.
If this leaf is not configured, the operationally used MTU
depends on the interface's type.";
reference
"RFC 8200: Internet Protocol, Version 6 (IPv6)
Specification
Section 5";
}
list address {
key "ip";
description
"The list of IPv6 addresses on the interface.";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address on the interface.";
}
leaf prefix-length {
type uint8 {
range "0..128";
}
mandatory true;
description
"The length of the subnet prefix.";
}
leaf origin {
type ip-address-origin;
config false;
description
"The origin of this address.";
}
leaf status {
type enumeration {
enum preferred {
description
"This is a valid address that can appear as the
destination or source address of a packet.";
}
enum deprecated {
description
"This is a valid but deprecated address that should
no longer be used as a source address in new
communications, but packets addressed to such an
address are processed as expected.";
}
enum invalid {
description
"This isn't a valid address, and it shouldn't appear
as the destination or source address of a packet.";
}
enum inaccessible {
description
"The address is not accessible because the interface
to which this address is assigned is not
operational.";
}
enum unknown {
description
"The status cannot be determined for some reason.";
}
enum tentative {
description
"The uniqueness of the address on the link is being
verified. Addresses in this state should not be
used for general communication and should only be
used to determine the uniqueness of the address.";
}
enum duplicate {
description
"The address has been determined to be non-unique on
the link and so must not be used.";
}
enum optimistic {
description
"The address is available for use, subject to
restrictions, while its uniqueness on a link is
being verified.";
}
}
config false;
description
"The status of an address. Most of the states correspond
to states from the IPv6 Stateless Address
Autoconfiguration protocol.";
reference
"RFC 4293: Management Information Base for the
Internet Protocol (IP)
- IpAddressStatusTC
RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
}
list neighbor {
key "ip";
description
"A list of mappings from IPv6 addresses to
link-layer addresses.
Entries in this list in the intended configuration are
used as static entries in the Neighbor Cache.
In the operational state, this list represents the
Neighbor Cache.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
leaf ip {
type inet:ipv6-address-no-zone;
description
"The IPv6 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
mandatory true;
description
"The link-layer address of the neighbor node.
In the operational state, if the neighbor's 'state' leaf
is 'incomplete', this leaf is not instantiated.";
}
leaf origin {
type neighbor-origin;
config false;
description
"The origin of this neighbor entry.";
}
leaf is-router {
type empty;
config false;
description
"Indicates that the neighbor node acts as a router.";
}
leaf state {
type enumeration {
enum incomplete {
description
"Address resolution is in progress, and the
link-layer address of the neighbor has not yet been
determined.";
}
enum reachable {
description
"Roughly speaking, the neighbor is known to have been
reachable recently (within tens of seconds ago).";
}
enum stale {
description
"The neighbor is no longer known to be reachable, but
until traffic is sent to the neighbor no attempt
should be made to verify its reachability.";
}
enum delay {
description
"The neighbor is no longer known to be reachable, and
traffic has recently been sent to the neighbor.
Rather than probe the neighbor immediately, however,
delay sending probes for a short while in order to
give upper-layer protocols a chance to provide
reachability confirmation.";
}
enum probe {
description
"The neighbor is no longer known to be reachable, and
unicast Neighbor Solicitation probes are being sent
to verify reachability.";
}
}
config false;
description
"The Neighbor Unreachability Detection state of this
entry.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 7.3.2";
}
}
leaf dup-addr-detect-transmits {
type uint32;
default 1;
description
"The number of consecutive Neighbor Solicitation messages
sent while performing Duplicate Address Detection on a
tentative address. A value of zero indicates that
Duplicate Address Detection is not performed on
tentative addresses. A value of one indicates a single
transmission with no follow-up retransmissions.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
container autoconf {
description
"Parameters to control the autoconfiguration of IPv6
addresses, as described in RFC 4862.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration";
leaf create-global-addresses {
type boolean;
default true;
description
"If enabled, the host creates global addresses as
described in RFC 4862.";
reference
"RFC 4862: IPv6 Stateless Address Autoconfiguration
Section 5.5";
}
leaf create-temporary-addresses {
if-feature ipv6-privacy-autoconf;
type boolean;
default false;
description
"If enabled, the host creates temporary addresses as
described in RFC 4941.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6";
}
leaf temporary-valid-lifetime {
if-feature ipv6-privacy-autoconf;
type uint32;
units "seconds";
default 604800;
description
"The time period during which the temporary address
is valid.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
- TEMP_VALID_LIFETIME";
}
leaf temporary-preferred-lifetime {
if-feature ipv6-privacy-autoconf;
type uint32;
units "seconds";
default 86400;
description
"The time period during which the temporary address is
preferred.";
reference
"RFC 4941: Privacy Extensions for Stateless Address
Autoconfiguration in IPv6
- TEMP_PREFERRED_LIFETIME";
}
}
}
}
/*
* Legacy operational state data nodes
*/
augment "/if:interfaces-state/if:interface" {
status deprecated;
description
"Data nodes for the operational state of IP on interfaces.";
container ipv4 {
presence
"Present if IPv4 is enabled on this interface";
config false;
status deprecated;
description
"Interface-specific parameters for the IPv4 address family.";
leaf forwarding {
type boolean;
status deprecated;
description
"Indicates whether IPv4 packet forwarding is enabled or
disabled on this interface.";
}
leaf mtu {
type uint16 {
range "68..max";
}
units "octets";
status deprecated;
description
"The size, in octets, of the largest IPv4 packet that the
interface will send and receive.";
reference
"RFC 791: Internet Protocol";
}
list address {
key "ip";
status deprecated;
description
"The list of IPv4 addresses on the interface.";
leaf ip {
type inet:ipv4-address-no-zone;
status deprecated;
description
"The IPv4 address on the interface.";
}
choice subnet {
status deprecated;
description
"The subnet can be specified as a prefix length or,
if the server supports non-contiguous netmasks, as
a netmask.";
leaf prefix-length {
type uint8 {
range "0..32";
}
status deprecated;
description
"The length of the subnet prefix.";
}
leaf netmask {
if-feature ipv4-non-contiguous-netmasks;
type yang:dotted-quad;
status deprecated;
description
"The subnet specified as a netmask.";
}
}
leaf origin {
type ip-address-origin;
status deprecated;
description
"The origin of this address.";
}
}
list neighbor {
key "ip";
status deprecated;
description
"A list of mappings from IPv4 addresses to
link-layer addresses.
This list represents the ARP Cache.";
reference
"RFC 826: An Ethernet Address Resolution Protocol";
leaf ip {
type inet:ipv4-address-no-zone;
status deprecated;
description
"The IPv4 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
status deprecated;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
status deprecated;
description
"The origin of this neighbor entry.";
}
}
}
container ipv6 {
presence
"Present if IPv6 is enabled on this interface";
config false;
status deprecated;
description
"Parameters for the IPv6 address family.";
leaf forwarding {
type boolean;
default false;
status deprecated;
description
"Indicates whether IPv6 packet forwarding is enabled or
disabled on this interface.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 6.2.1, IsRouter";
}
leaf mtu {
type uint32 {
range "1280..max";
}
units "octets";
status deprecated;
description
"The size, in octets, of the largest IPv6 packet that the
interface will send and receive.";
reference
"RFC 8200: Internet Protocol, Version 6 (IPv6)
Specification
Section 5";
}
list address {
key "ip";
status deprecated;
description
"The list of IPv6 addresses on the interface.";
leaf ip {
type inet:ipv6-address-no-zone;
status deprecated;
description
"The IPv6 address on the interface.";
}
leaf prefix-length {
type uint8 {
range "0..128";
}
mandatory true;
status deprecated;
description
"The length of the subnet prefix.";
}
leaf origin {
type ip-address-origin;
status deprecated;
description
"The origin of this address.";
}
leaf status {
type enumeration {
enum preferred {
description
"This is a valid address that can appear as the
destination or source address of a packet.";
}
enum deprecated {
description
"This is a valid but deprecated address that should
no longer be used as a source address in new
communications, but packets addressed to such an
address are processed as expected.";
}
enum invalid {
description
"This isn't a valid address, and it shouldn't appear
as the destination or source address of a packet.";
}
enum inaccessible {
description
"The address is not accessible because the interface
to which this address is assigned is not
operational.";
}
enum unknown {
description
"The status cannot be determined for some reason.";
}
enum tentative {
description
"The uniqueness of the address on the link is being
verified. Addresses in this state should not be
used for general communication and should only be
used to determine the uniqueness of the address.";
}
enum duplicate {
description
"The address has been determined to be non-unique on
the link and so must not be used.";
}
enum optimistic {
description
"The address is available for use, subject to
restrictions, while its uniqueness on a link is
being verified.";
}
}
status deprecated;
description
"The status of an address. Most of the states correspond
to states from the IPv6 Stateless Address
Autoconfiguration protocol.";
reference
"RFC 4293: Management Information Base for the
Internet Protocol (IP)
- IpAddressStatusTC
RFC 4862: IPv6 Stateless Address Autoconfiguration";
}
}
list neighbor {
key "ip";
status deprecated;
description
"A list of mappings from IPv6 addresses to
link-layer addresses.
This list represents the Neighbor Cache.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)";
leaf ip {
type inet:ipv6-address-no-zone;
status deprecated;
description
"The IPv6 address of the neighbor node.";
}
leaf link-layer-address {
type yang:phys-address;
status deprecated;
description
"The link-layer address of the neighbor node.";
}
leaf origin {
type neighbor-origin;
status deprecated;
description
"The origin of this neighbor entry.";
}
leaf is-router {
type empty;
status deprecated;
description
"Indicates that the neighbor node acts as a router.";
}
leaf state {
type enumeration {
enum incomplete {
description
"Address resolution is in progress, and the
link-layer address of the neighbor has not yet been
determined.";
}
enum reachable {
description
"Roughly speaking, the neighbor is known to have been
reachable recently (within tens of seconds ago).";
}
enum stale {
description
"The neighbor is no longer known to be reachable, but
until traffic is sent to the neighbor no attempt
should be made to verify its reachability.";
}
enum delay {
description
"The neighbor is no longer known to be reachable, and
traffic has recently been sent to the neighbor.
Rather than probe the neighbor immediately, however,
delay sending probes for a short while in order to
give upper-layer protocols a chance to provide
reachability confirmation.";
}
enum probe {
description
"The neighbor is no longer known to be reachable, and
unicast Neighbor Solicitation probes are being sent
to verify reachability.";
}
}
status deprecated;
description
"The Neighbor Unreachability Detection state of this
entry.";
reference
"RFC 4861: Neighbor Discovery for IP version 6 (IPv6)
Section 7.3.2";
}
}
}
}
}
@@ -0,0 +1,464 @@
module ietf-netconf-acm {
namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm";
prefix nacm;
import ietf-yang-types {
prefix yang;
}
organization
"IETF NETCONF (Network Configuration) Working Group";
contact
"WG Web: <https://datatracker.ietf.org/wg/netconf/>
WG List: <mailto:netconf@ietf.org>
Author: Andy Bierman
<mailto:andy@yumaworks.com>
Author: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"Network Configuration Access Control Model.
Copyright (c) 2012 - 2018 IETF Trust and the persons
identified as authors of the code. 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
(https://trustee.ietf.org/license-info).
This version of this YANG module is part of RFC 8341; see
the RFC itself for full legal notices.";
revision "2018-02-14" {
description
"Added support for YANG 1.1 actions and notifications tied to
data nodes. Clarified how NACM extensions can be used by
other data models.";
reference
"RFC 8341: Network Configuration Access Control Model";
}
revision "2012-02-22" {
description
"Initial version.";
reference
"RFC 6536: Network Configuration Protocol (NETCONF)
Access Control Model";
}
/*
* Extension statements
*/
extension default-deny-write {
description
"Used to indicate that the data model node
represents a sensitive security system parameter.
If present, the NETCONF server will only allow the designated
'recovery session' to have write access to the node. An
explicit access control rule is required for all other users.
If the NACM module is used, then it must be enabled (i.e.,
/nacm/enable-nacm object equals 'true'), or this extension
is ignored.
The 'default-deny-write' extension MAY appear within a data
definition statement. It is ignored otherwise.";
}
extension default-deny-all {
description
"Used to indicate that the data model node
controls a very sensitive security system parameter.
If present, the NETCONF server will only allow the designated
'recovery session' to have read, write, or execute access to
the node. An explicit access control rule is required for all
other users.
If the NACM module is used, then it must be enabled (i.e.,
/nacm/enable-nacm object equals 'true'), or this extension
is ignored.
The 'default-deny-all' extension MAY appear within a data
definition statement, 'rpc' statement, or 'notification'
statement. It is ignored otherwise.";
}
/*
* Derived types
*/
typedef user-name-type {
type string {
length "1..max";
}
description
"General-purpose username string.";
}
typedef matchall-string-type {
type string {
pattern '\*';
}
description
"The string containing a single asterisk '*' is used
to conceptually represent all possible values
for the particular leaf using this data type.";
}
typedef access-operations-type {
type bits {
bit create {
description
"Any protocol operation that creates a
new data node.";
}
bit read {
description
"Any protocol operation or notification that
returns the value of a data node.";
}
bit update {
description
"Any protocol operation that alters an existing
data node.";
}
bit delete {
description
"Any protocol operation that removes a data node.";
}
bit exec {
description
"Execution access to the specified protocol operation.";
}
}
description
"Access operation.";
}
typedef group-name-type {
type string {
length "1..max";
pattern '[^\*].*';
}
description
"Name of administrative group to which
users can be assigned.";
}
typedef action-type {
type enumeration {
enum permit {
description
"Requested action is permitted.";
}
enum deny {
description
"Requested action is denied.";
}
}
description
"Action taken by the server when a particular
rule matches.";
}
typedef node-instance-identifier {
type yang:xpath1.0;
description
"Path expression used to represent a special
data node, action, or notification instance-identifier
string.
A node-instance-identifier value is an
unrestricted YANG instance-identifier expression.
All the same rules as an instance-identifier apply,
except that predicates for keys are optional. If a key
predicate is missing, then the node-instance-identifier
represents all possible server instances for that key.
This XML Path Language (XPath) expression is evaluated in the
following context:
o The set of namespace declarations are those in scope on
the leaf element where this type is used.
o The set of variable bindings contains one variable,
'USER', which contains the name of the user of the
current session.
o The function library is the core function library, but
note that due to the syntax restrictions of an
instance-identifier, no functions are allowed.
o The context node is the root node in the data tree.
The accessible tree includes actions and notifications tied
to data nodes.";
}
/*
* Data definition statements
*/
container nacm {
nacm:default-deny-all;
description
"Parameters for NETCONF access control model.";
leaf enable-nacm {
type boolean;
default "true";
description
"Enables or disables all NETCONF access control
enforcement. If 'true', then enforcement
is enabled. If 'false', then enforcement
is disabled.";
}
leaf read-default {
type action-type;
default "permit";
description
"Controls whether read access is granted if
no appropriate rule is found for a
particular read request.";
}
leaf write-default {
type action-type;
default "deny";
description
"Controls whether create, update, or delete access
is granted if no appropriate rule is found for a
particular write request.";
}
leaf exec-default {
type action-type;
default "permit";
description
"Controls whether exec access is granted if no appropriate
rule is found for a particular protocol operation request.";
}
leaf enable-external-groups {
type boolean;
default "true";
description
"Controls whether the server uses the groups reported by the
NETCONF transport layer when it assigns the user to a set of
NACM groups. If this leaf has the value 'false', any group
names reported by the transport layer are ignored by the
server.";
}
leaf denied-operations {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that a
protocol operation request was denied.";
}
leaf denied-data-writes {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that a
protocol operation request to alter
a configuration datastore was denied.";
}
leaf denied-notifications {
type yang:zero-based-counter32;
config false;
mandatory true;
description
"Number of times since the server last restarted that
a notification was dropped for a subscription because
access to the event type was denied.";
}
container groups {
description
"NETCONF access control groups.";
list group {
key name;
description
"One NACM group entry. This list will only contain
configured entries, not any entries learned from
any transport protocols.";
leaf name {
type group-name-type;
description
"Group name associated with this entry.";
}
leaf-list user-name {
type user-name-type;
description
"Each entry identifies the username of
a member of the group associated with
this entry.";
}
}
}
list rule-list {
key name;
ordered-by user;
description
"An ordered collection of access control rules.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule-list.";
}
leaf-list group {
type union {
type matchall-string-type;
type group-name-type;
}
description
"List of administrative groups that will be
assigned the associated access rights
defined by the 'rule' list.
The string '*' indicates that all groups apply to the
entry.";
}
list rule {
key name;
ordered-by user;
description
"One access control rule.
Rules are processed in user-defined order until a match is
found. A rule matches if 'module-name', 'rule-type', and
'access-operations' match the request. If a rule
matches, the 'action' leaf determines whether or not
access is granted.";
leaf name {
type string {
length "1..max";
}
description
"Arbitrary name assigned to the rule.";
}
leaf module-name {
type union {
type matchall-string-type;
type string;
}
default "*";
description
"Name of the module associated with this rule.
This leaf matches if it has the value '*' or if the
object being accessed is defined in the module with the
specified module name.";
}
choice rule-type {
description
"This choice matches if all leafs present in the rule
match the request. If no leafs are present, the
choice matches all requests.";
case protocol-operation {
leaf rpc-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if
its value equals the requested protocol operation
name.";
}
}
case notification {
leaf notification-name {
type union {
type matchall-string-type;
type string;
}
description
"This leaf matches if it has the value '*' or if its
value equals the requested notification name.";
}
}
case data-node {
leaf path {
type node-instance-identifier;
mandatory true;
description
"Data node instance-identifier associated with the
data node, action, or notification controlled by
this rule.
Configuration data or state data
instance-identifiers start with a top-level
data node. A complete instance-identifier is
required for this type of path value.
The special value '/' refers to all possible
datastore contents.";
}
}
}
leaf access-operations {
type union {
type matchall-string-type;
type access-operations-type;
}
default "*";
description
"Access operations associated with this rule.
This leaf matches if it has the value '*' or if the
bit corresponding to the requested operation is set.";
}
leaf action {
type action-type;
mandatory true;
description
"The access control action associated with the
rule. If a rule has been determined to match a
particular request, then this object is used
to determine whether to permit or deny the
request.";
}
leaf comment {
type string;
description
"A textual description of the access rule.";
}
}
}
}
}
+800
View File
@@ -0,0 +1,800 @@
module ietf-system {
namespace "urn:ietf:params:xml:ns:yang:ietf-system";
prefix "sys";
import ietf-yang-types {
prefix yang;
}
import ietf-inet-types {
prefix inet;
}
import ietf-netconf-acm {
prefix nacm;
}
import iana-crypt-hash {
prefix ianach;
}
organization
"IETF NETMOD (NETCONF Data Modeling Language) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
WG Chair: Thomas Nadeau
<mailto:tnadeau@lucidvision.com>
WG Chair: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>
Editor: Andy Bierman
<mailto:andy@yumaworks.com>
Editor: Martin Bjorklund
<mailto:mbj@tail-f.com>";
description
"This module contains a collection of YANG definitions for the
configuration and identification of some common system
properties within a device containing a NETCONF server. This
includes data node definitions for system identification,
time-of-day management, user management, DNS resolver
configuration, and some protocol operations for system
management.
Copyright (c) 2014 IETF Trust and the persons identified as
authors of the code. 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 7317; see
the RFC itself for full legal notices.";
revision 2014-08-06 {
description
"Initial revision.";
reference
"RFC 7317: A YANG Data Model for System Management";
}
/*
* Typedefs
*/
typedef timezone-name {
type string;
description
"A time zone name as used by the Time Zone Database,
sometimes referred to as the 'Olson Database'.
The exact set of valid values is an implementation-specific
matter. Client discovery of the exact set of time zone names
for a particular server is out of scope.";
reference
"RFC 6557: Procedures for Maintaining the Time Zone Database";
}
/*
* Features
*/
feature radius {
description
"Indicates that the device can be configured as a RADIUS
client.";
reference
"RFC 2865: Remote Authentication Dial In User Service (RADIUS)";
}
feature authentication {
description
"Indicates that the device supports configuration of
user authentication.";
}
feature local-users {
if-feature authentication;
description
"Indicates that the device supports configuration of
local user authentication.";
}
feature radius-authentication {
if-feature radius;
if-feature authentication;
description
"Indicates that the device supports configuration of user
authentication over RADIUS.";
reference
"RFC 2865: Remote Authentication Dial In User Service (RADIUS)
RFC 5607: Remote Authentication Dial-In User Service (RADIUS)
Authorization for Network Access Server (NAS)
Management";
}
feature ntp {
description
"Indicates that the device can be configured to use one or
more NTP servers to set the system date and time.";
}
feature ntp-udp-port {
if-feature ntp;
description
"Indicates that the device supports the configuration of
the UDP port for NTP servers.
This is a 'feature', since many implementations do not support
any port other than the default port.";
}
feature timezone-name {
description
"Indicates that the local time zone on the device
can be configured to use the TZ database
to set the time zone and manage daylight saving time.";
reference
"RFC 6557: Procedures for Maintaining the Time Zone Database";
}
feature dns-udp-tcp-port {
description
"Indicates that the device supports the configuration of
the UDP and TCP port for DNS servers.
This is a 'feature', since many implementations do not support
any port other than the default port.";
}
/*
* Identities
*/
identity authentication-method {
description
"Base identity for user authentication methods.";
}
identity radius {
base authentication-method;
description
"Indicates user authentication using RADIUS.";
reference
"RFC 2865: Remote Authentication Dial In User Service (RADIUS)
RFC 5607: Remote Authentication Dial-In User Service (RADIUS)
Authorization for Network Access Server (NAS)
Management";
}
identity local-users {
base authentication-method;
description
"Indicates password-based authentication of locally
configured users.";
}
identity radius-authentication-type {
description
"Base identity for RADIUS authentication types.";
}
identity radius-pap {
base radius-authentication-type;
description
"The device requests Password Authentication Protocol (PAP)
authentication from the RADIUS server.";
reference
"RFC 2865: Remote Authentication Dial In User Service (RADIUS)";
}
identity radius-chap {
base radius-authentication-type;
description
"The device requests Challenge Handshake Authentication
Protocol (CHAP) authentication from the RADIUS server.";
reference
"RFC 2865: Remote Authentication Dial In User Service (RADIUS)";
}
/*
* Configuration data nodes
*/
container system {
description
"System group configuration.";
leaf contact {
type string;
description
"The administrator contact information for the system.
A server implementation MAY map this leaf to the sysContact
MIB object. Such an implementation needs to use some
mechanism to handle the differences in size and characters
allowed between this leaf and sysContact. The definition of
such a mechanism is outside the scope of this document.";
reference
"RFC 3418: Management Information Base (MIB) for the
Simple Network Management Protocol (SNMP)
SNMPv2-MIB.sysContact";
}
leaf hostname {
type inet:domain-name;
description
"The name of the host. This name can be a single domain
label or the fully qualified domain name of the host.";
}
leaf location {
type string;
description
"The system location.
A server implementation MAY map this leaf to the sysLocation
MIB object. Such an implementation needs to use some
mechanism to handle the differences in size and characters
allowed between this leaf and sysLocation. The definition
of such a mechanism is outside the scope of this document.";
reference
"RFC 3418: Management Information Base (MIB) for the
Simple Network Management Protocol (SNMP)
SNMPv2-MIB.sysLocation";
}
container clock {
description
"Configuration of the system date and time properties.";
choice timezone {
description
"The system time zone information.";
case timezone-name {
if-feature timezone-name;
leaf timezone-name {
type timezone-name;
description
"The TZ database name to use for the system, such
as 'Europe/Stockholm'.";
}
}
case timezone-utc-offset {
leaf timezone-utc-offset {
type int16 {
range "-1500 .. 1500";
}
units "minutes";
description
"The number of minutes to add to UTC time to
identify the time zone for this system. For example,
'UTC - 8:00 hours' would be represented as '-480'.
Note that automatic daylight saving time adjustment
is not provided if this object is used.";
}
}
}
}
container ntp {
if-feature ntp;
presence
"Enables the NTP client unless the 'enabled' leaf
(which defaults to 'true') is set to 'false'";
description
"Configuration of the NTP client.";
leaf enabled {
type boolean;
default true;
description
"Indicates that the system should attempt to
synchronize the system clock with an NTP server
from the 'ntp/server' list.";
}
list server {
key name;
description
"List of NTP servers to use for system clock
synchronization. If '/system/ntp/enabled'
is 'true', then the system will attempt to
contact and utilize the specified NTP servers.";
leaf name {
type string;
description
"An arbitrary name for the NTP server.";
}
choice transport {
mandatory true;
description
"The transport-protocol-specific parameters for this
server.";
case udp {
container udp {
description
"Contains UDP-specific configuration parameters
for NTP.";
leaf address {
type inet:host;
mandatory true;
description
"The address of the NTP server.";
}
leaf port {
if-feature ntp-udp-port;
type inet:port-number;
default 123;
description
"The port number of the NTP server.";
}
}
}
}
leaf association-type {
type enumeration {
enum server {
description
"Use client association mode. This device
will not provide synchronization to the
configured NTP server.";
}
enum peer {
description
"Use symmetric active association mode.
This device may provide synchronization
to the configured NTP server.";
}
enum pool {
description
"Use client association mode with one or
more of the NTP servers found by DNS
resolution of the domain name given by
the 'address' leaf. This device will not
provide synchronization to the servers.";
}
}
default server;
description
"The desired association type for this NTP server.";
}
leaf iburst {
type boolean;
default false;
description
"Indicates whether this server should enable burst
synchronization or not.";
}
leaf prefer {
type boolean;
default false;
description
"Indicates whether this server should be preferred
or not.";
}
}
}
container dns-resolver {
description
"Configuration of the DNS resolver.";
leaf-list search {
type inet:domain-name;
ordered-by user;
description
"An ordered list of domains to search when resolving
a host name.";
}
list server {
key name;
ordered-by user;
description
"List of the DNS servers that the resolver should query.
When the resolver is invoked by a calling application, it
sends the query to the first name server in this list. If
no response has been received within 'timeout' seconds,
the resolver continues with the next server in the list.
If no response is received from any server, the resolver
continues with the first server again. When the resolver
has traversed the list 'attempts' times without receiving
any response, it gives up and returns an error to the
calling application.
Implementations MAY limit the number of entries in this
list.";
leaf name {
type string;
description
"An arbitrary name for the DNS server.";
}
choice transport {
mandatory true;
description
"The transport-protocol-specific parameters for this
server.";
case udp-and-tcp {
container udp-and-tcp {
description
"Contains UDP- and TCP-specific configuration
parameters for DNS.";
reference
"RFC 1035: Domain Names - Implementation and
Specification
RFC 5966: DNS Transport over TCP - Implementation
Requirements";
leaf address {
type inet:ip-address;
mandatory true;
description
"The address of the DNS server.";
}
leaf port {
if-feature dns-udp-tcp-port;
type inet:port-number;
default 53;
description
"The UDP and TCP port number of the DNS server.";
}
}
}
}
}
container options {
description
"Resolver options. The set of available options has been
limited to those that are generally available across
different resolver implementations and generally useful.";
leaf timeout {
type uint8 {
range "1..max";
}
units "seconds";
default "5";
description
"The amount of time the resolver will wait for a
response from each remote name server before
retrying the query via a different name server.";
}
leaf attempts {
type uint8 {
range "1..max";
}
default "2";
description
"The number of times the resolver will send a query to
all of its name servers before giving up and returning
an error to the calling application.";
}
}
}
container radius {
if-feature radius;
description
"Configuration of the RADIUS client.";
list server {
key name;
ordered-by user;
description
"List of RADIUS servers used by the device.
When the RADIUS client is invoked by a calling
application, it sends the query to the first server in
this list. If no response has been received within
'timeout' seconds, the client continues with the next
server in the list. If no response is received from any
server, the client continues with the first server again.
When the client has traversed the list 'attempts' times
without receiving any response, it gives up and returns an
error to the calling application.";
leaf name {
type string;
description
"An arbitrary name for the RADIUS server.";
}
choice transport {
mandatory true;
description
"The transport-protocol-specific parameters for this
server.";
case udp {
container udp {
description
"Contains UDP-specific configuration parameters
for RADIUS.";
leaf address {
type inet:host;
mandatory true;
description
"The address of the RADIUS server.";
}
leaf authentication-port {
type inet:port-number;
default "1812";
description
"The port number of the RADIUS server.";
}
leaf shared-secret {
type string;
mandatory true;
nacm:default-deny-all;
description
"The shared secret, which is known to both the
RADIUS client and server.";
reference
"RFC 2865: Remote Authentication Dial In User
Service (RADIUS)";
}
}
}
}
leaf authentication-type {
type identityref {
base radius-authentication-type;
}
default radius-pap;
description
"The authentication type requested from the RADIUS
server.";
}
}
container options {
description
"RADIUS client options.";
leaf timeout {
type uint8 {
range "1..max";
}
units "seconds";
default "5";
description
"The number of seconds the device will wait for a
response from each RADIUS server before trying with a
different server.";
}
leaf attempts {
type uint8 {
range "1..max";
}
default "2";
description
"The number of times the device will send a query to
all of its RADIUS servers before giving up.";
}
}
}
container authentication {
nacm:default-deny-write;
if-feature authentication;
description
"The authentication configuration subtree.";
leaf-list user-authentication-order {
type identityref {
base authentication-method;
}
must '(. != "sys:radius" or ../../radius/server)' {
error-message
"When 'radius' is used, a RADIUS server"
+ " must be configured.";
description
"When 'radius' is used as an authentication method,
a RADIUS server must be configured.";
}
ordered-by user;
description
"When the device authenticates a user with a password,
it tries the authentication methods in this leaf-list in
order. If authentication with one method fails, the next
method is used. If no method succeeds, the user is
denied access.
An empty user-authentication-order leaf-list still allows
authentication of users using mechanisms that do not
involve a password.
If the 'radius-authentication' feature is advertised by
the NETCONF server, the 'radius' identity can be added to
this list.
If the 'local-users' feature is advertised by the
NETCONF server, the 'local-users' identity can be
added to this list.";
}
list user {
if-feature local-users;
key name;
description
"The list of local users configured on this device.";
leaf name {
type string;
description
"The user name string identifying this entry.";
}
leaf password {
type ianach:crypt-hash;
description
"The password for this entry.";
}
list authorized-key {
key name;
description
"A list of public SSH keys for this user. These keys
are allowed for SSH authentication, as described in
RFC 4253.";
reference
"RFC 4253: The Secure Shell (SSH) Transport Layer
Protocol";
leaf name {
type string;
description
"An arbitrary name for the SSH key.";
}
leaf algorithm {
type string;
mandatory true;
description
"The public key algorithm name for this SSH key.
Valid values are the values in the IANA 'Secure Shell
(SSH) Protocol Parameters' registry, Public Key
Algorithm Names.";
reference
"IANA 'Secure Shell (SSH) Protocol Parameters'
registry, Public Key Algorithm Names";
}
leaf key-data {
type binary;
mandatory true;
description
"The binary public key data for this SSH key, as
specified by RFC 4253, Section 6.6, i.e.:
string certificate or public key format
identifier
byte[n] key/certificate data.";
reference
"RFC 4253: The Secure Shell (SSH) Transport Layer
Protocol";
}
}
}
}
}
/*
* Operational state data nodes
*/
container system-state {
config false;
description
"System group operational state.";
container platform {
description
"Contains vendor-specific information for
identifying the system platform and operating system.";
reference
"IEEE Std 1003.1-2008 - sys/utsname.h";
leaf os-name {
type string;
description
"The name of the operating system in use -
for example, 'Linux'.";
reference
"IEEE Std 1003.1-2008 - utsname.sysname";
}
leaf os-release {
type string;
description
"The current release level of the operating
system in use. This string MAY indicate
the OS source code revision.";
reference
"IEEE Std 1003.1-2008 - utsname.release";
}
leaf os-version {
type string;
description
"The current version level of the operating
system in use. This string MAY indicate
the specific OS build date and target variant
information.";
reference
"IEEE Std 1003.1-2008 - utsname.version";
}
leaf machine {
type string;
description
"A vendor-specific identifier string representing
the hardware in use.";
reference
"IEEE Std 1003.1-2008 - utsname.machine";
}
}
container clock {
description
"Monitoring of the system date and time properties.";
leaf current-datetime {
type yang:date-and-time;
description
"The current system date and time.";
}
leaf boot-datetime {
type yang:date-and-time;
description
"The system date and time when the system last restarted.";
}
}
}
rpc set-current-datetime {
nacm:default-deny-all;
description
"Set the /system-state/clock/current-datetime leaf
to the specified value.
If the system is using NTP (i.e., /system/ntp/enabled
is set to 'true'), then this operation will fail with
error-tag 'operation-failed' and error-app-tag value of
'ntp-active'.";
input {
leaf current-datetime {
type yang:date-and-time;
mandatory true;
description
"The current system date and time.";
}
}
}
rpc system-restart {
nacm:default-deny-all;
description
"Request that the entire system be restarted immediately.
A server SHOULD send an rpc reply to the client before
restarting the system.";
}
rpc system-shutdown {
nacm:default-deny-all;
description
"Request that the entire system be shut down immediately.
A server SHOULD send an rpc reply to the client before
shutting down the system.";
}
}
@@ -0,0 +1,474 @@
module ietf-yang-types {
namespace "urn:ietf:params:xml:ns:yang:ietf-yang-types";
prefix "yang";
organization
"IETF NETMOD (NETCONF Data Modeling Language) Working Group";
contact
"WG Web: <http://tools.ietf.org/wg/netmod/>
WG List: <mailto:netmod@ietf.org>
WG Chair: David Kessens
<mailto:david.kessens@nsn.com>
WG Chair: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>
Editor: Juergen Schoenwaelder
<mailto:j.schoenwaelder@jacobs-university.de>";
description
"This module contains a collection of generally useful derived
YANG data types.
Copyright (c) 2013 IETF Trust and the persons identified as
authors of the code. 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 6991; see
the RFC itself for full legal notices.";
revision 2013-07-15 {
description
"This revision adds the following new data types:
- yang-identifier
- hex-string
- uuid
- dotted-quad";
reference
"RFC 6991: Common YANG Data Types";
}
revision 2010-09-24 {
description
"Initial revision.";
reference
"RFC 6021: Common YANG Data Types";
}
/*** collection of counter and gauge types ***/
typedef counter32 {
type uint32;
description
"The counter32 type represents a non-negative integer
that monotonically increases until it reaches a
maximum value of 2^32-1 (4294967295 decimal), when it
wraps around and starts increasing again from zero.
Counters have no defined 'initial' value, and thus, a
single value of a counter has (in general) no information
content. Discontinuities in the monotonically increasing
value normally occur at re-initialization of the
management system, and at other times as specified in the
description of a schema node using this type. If such
other times can occur, for example, the creation of
a schema node of type counter32 at times other than
re-initialization, then a corresponding schema node
should be defined, with an appropriate type, to indicate
the last discontinuity.
The counter32 type should not be used for configuration
schema nodes. A default statement SHOULD NOT be used in
combination with the type counter32.
In the value set and its semantics, this type is equivalent
to the Counter32 type of the SMIv2.";
reference
"RFC 2578: Structure of Management Information Version 2
(SMIv2)";
}
typedef zero-based-counter32 {
type yang:counter32;
default "0";
description
"The zero-based-counter32 type represents a counter32
that has the defined 'initial' value zero.
A schema node of this type will be set to zero (0) on creation
and will thereafter increase monotonically until it reaches
a maximum value of 2^32-1 (4294967295 decimal), when it
wraps around and starts increasing again from zero.
Provided that an application discovers a new schema node
of this type within the minimum time to wrap, it can use the
'initial' value as a delta. It is important for a management
station to be aware of this minimum time and the actual time
between polls, and to discard data if the actual time is too
long or there is no defined minimum time.
In the value set and its semantics, this type is equivalent
to the ZeroBasedCounter32 textual convention of the SMIv2.";
reference
"RFC 4502: Remote Network Monitoring Management Information
Base Version 2";
}
typedef counter64 {
type uint64;
description
"The counter64 type represents a non-negative integer
that monotonically increases until it reaches a
maximum value of 2^64-1 (18446744073709551615 decimal),
when it wraps around and starts increasing again from zero.
Counters have no defined 'initial' value, and thus, a
single value of a counter has (in general) no information
content. Discontinuities in the monotonically increasing
value normally occur at re-initialization of the
management system, and at other times as specified in the
description of a schema node using this type. If such
other times can occur, for example, the creation of
a schema node of type counter64 at times other than
re-initialization, then a corresponding schema node
should be defined, with an appropriate type, to indicate
the last discontinuity.
The counter64 type should not be used for configuration
schema nodes. A default statement SHOULD NOT be used in
combination with the type counter64.
In the value set and its semantics, this type is equivalent
to the Counter64 type of the SMIv2.";
reference
"RFC 2578: Structure of Management Information Version 2
(SMIv2)";
}
typedef zero-based-counter64 {
type yang:counter64;
default "0";
description
"The zero-based-counter64 type represents a counter64 that
has the defined 'initial' value zero.
A schema node of this type will be set to zero (0) on creation
and will thereafter increase monotonically until it reaches
a maximum value of 2^64-1 (18446744073709551615 decimal),
when it wraps around and starts increasing again from zero.
Provided that an application discovers a new schema node
of this type within the minimum time to wrap, it can use the
'initial' value as a delta. It is important for a management
station to be aware of this minimum time and the actual time
between polls, and to discard data if the actual time is too
long or there is no defined minimum time.
In the value set and its semantics, this type is equivalent
to the ZeroBasedCounter64 textual convention of the SMIv2.";
reference
"RFC 2856: Textual Conventions for Additional High Capacity
Data Types";
}
typedef gauge32 {
type uint32;
description
"The gauge32 type represents a non-negative integer, which
may increase or decrease, but shall never exceed a maximum
value, nor fall below a minimum value. The maximum value
cannot be greater than 2^32-1 (4294967295 decimal), and
the minimum value cannot be smaller than 0. The value of
a gauge32 has its maximum value whenever the information
being modeled is greater than or equal to its maximum
value, and has its minimum value whenever the information
being modeled is smaller than or equal to its minimum value.
If the information being modeled subsequently decreases
below (increases above) the maximum (minimum) value, the
gauge32 also decreases (increases).
In the value set and its semantics, this type is equivalent
to the Gauge32 type of the SMIv2.";
reference
"RFC 2578: Structure of Management Information Version 2
(SMIv2)";
}
typedef gauge64 {
type uint64;
description
"The gauge64 type represents a non-negative integer, which
may increase or decrease, but shall never exceed a maximum
value, nor fall below a minimum value. The maximum value
cannot be greater than 2^64-1 (18446744073709551615), and
the minimum value cannot be smaller than 0. The value of
a gauge64 has its maximum value whenever the information
being modeled is greater than or equal to its maximum
value, and has its minimum value whenever the information
being modeled is smaller than or equal to its minimum value.
If the information being modeled subsequently decreases
below (increases above) the maximum (minimum) value, the
gauge64 also decreases (increases).
In the value set and its semantics, this type is equivalent
to the CounterBasedGauge64 SMIv2 textual convention defined
in RFC 2856";
reference
"RFC 2856: Textual Conventions for Additional High Capacity
Data Types";
}
/*** collection of identifier-related types ***/
typedef object-identifier {
type string {
pattern '(([0-1](\.[1-3]?[0-9]))|(2\.(0|([1-9]\d*))))'
+ '(\.(0|([1-9]\d*)))*';
}
description
"The object-identifier type represents administratively
assigned names in a registration-hierarchical-name tree.
Values of this type are denoted as a sequence of numerical
non-negative sub-identifier values. Each sub-identifier
value MUST NOT exceed 2^32-1 (4294967295). Sub-identifiers
are separated by single dots and without any intermediate
whitespace.
The ASN.1 standard restricts the value space of the first
sub-identifier to 0, 1, or 2. Furthermore, the value space
of the second sub-identifier is restricted to the range
0 to 39 if the first sub-identifier is 0 or 1. Finally,
the ASN.1 standard requires that an object identifier
has always at least two sub-identifiers. The pattern
captures these restrictions.
Although the number of sub-identifiers is not limited,
module designers should realize that there may be
implementations that stick with the SMIv2 limit of 128
sub-identifiers.
This type is a superset of the SMIv2 OBJECT IDENTIFIER type
since it is not restricted to 128 sub-identifiers. Hence,
this type SHOULD NOT be used to represent the SMIv2 OBJECT
IDENTIFIER type; the object-identifier-128 type SHOULD be
used instead.";
reference
"ISO9834-1: Information technology -- Open Systems
Interconnection -- Procedures for the operation of OSI
Registration Authorities: General procedures and top
arcs of the ASN.1 Object Identifier tree";
}
typedef object-identifier-128 {
type object-identifier {
pattern '\d*(\.\d*){1,127}';
}
description
"This type represents object-identifiers restricted to 128
sub-identifiers.
In the value set and its semantics, this type is equivalent
to the OBJECT IDENTIFIER type of the SMIv2.";
reference
"RFC 2578: Structure of Management Information Version 2
(SMIv2)";
}
typedef yang-identifier {
type string {
length "1..max";
pattern '[a-zA-Z_][a-zA-Z0-9\-_.]*';
pattern '.|..|[^xX].*|.[^mM].*|..[^lL].*';
}
description
"A YANG identifier string as defined by the 'identifier'
rule in Section 12 of RFC 6020. An identifier must
start with an alphabetic character or an underscore
followed by an arbitrary sequence of alphabetic or
numeric characters, underscores, hyphens, or dots.
A YANG identifier MUST NOT start with any possible
combination of the lowercase or uppercase character
sequence 'xml'.";
reference
"RFC 6020: YANG - A Data Modeling Language for the Network
Configuration Protocol (NETCONF)";
}
/*** collection of types related to date and time***/
typedef date-and-time {
type string {
pattern '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?'
+ '(Z|[\+\-]\d{2}:\d{2})';
}
description
"The date-and-time type is a profile of the ISO 8601
standard for representation of dates and times using the
Gregorian calendar. The profile is defined by the
date-time production in Section 5.6 of RFC 3339.
The date-and-time type is compatible with the dateTime XML
schema type with the following notable exceptions:
(a) The date-and-time type does not allow negative years.
(b) The date-and-time time-offset -00:00 indicates an unknown
time zone (see RFC 3339) while -00:00 and +00:00 and Z
all represent the same time zone in dateTime.
(c) The canonical format (see below) of data-and-time values
differs from the canonical format used by the dateTime XML
schema type, which requires all times to be in UTC using
the time-offset 'Z'.
This type is not equivalent to the DateAndTime textual
convention of the SMIv2 since RFC 3339 uses a different
separator between full-date and full-time and provides
higher resolution of time-secfrac.
The canonical format for date-and-time values with a known time
zone uses a numeric time zone offset that is calculated using
the device's configured known offset to UTC time. A change of
the device's offset to UTC time will cause date-and-time values
to change accordingly. Such changes might happen periodically
in case a server follows automatically daylight saving time
(DST) time zone offset changes. The canonical format for
date-and-time values with an unknown time zone (usually
referring to the notion of local time) uses the time-offset
-00:00.";
reference
"RFC 3339: Date and Time on the Internet: Timestamps
RFC 2579: Textual Conventions for SMIv2
XSD-TYPES: XML Schema Part 2: Datatypes Second Edition";
}
typedef timeticks {
type uint32;
description
"The timeticks type represents a non-negative integer that
represents the time, modulo 2^32 (4294967296 decimal), in
hundredths of a second between two epochs. When a schema
node is defined that uses this type, the description of
the schema node identifies both of the reference epochs.
In the value set and its semantics, this type is equivalent
to the TimeTicks type of the SMIv2.";
reference
"RFC 2578: Structure of Management Information Version 2
(SMIv2)";
}
typedef timestamp {
type yang:timeticks;
description
"The timestamp type represents the value of an associated
timeticks schema node at which a specific occurrence
happened. The specific occurrence must be defined in the
description of any schema node defined using this type. When
the specific occurrence occurred prior to the last time the
associated timeticks attribute was zero, then the timestamp
value is zero. Note that this requires all timestamp values
to be reset to zero when the value of the associated timeticks
attribute reaches 497+ days and wraps around to zero.
The associated timeticks schema node must be specified
in the description of any schema node using this type.
In the value set and its semantics, this type is equivalent
to the TimeStamp textual convention of the SMIv2.";
reference
"RFC 2579: Textual Conventions for SMIv2";
}
/*** collection of generic address types ***/
typedef phys-address {
type string {
pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
}
description
"Represents media- or physical-level addresses represented
as a sequence octets, each octet represented by two hexadecimal
numbers. Octets are separated by colons. The canonical
representation uses lowercase characters.
In the value set and its semantics, this type is equivalent
to the PhysAddress textual convention of the SMIv2.";
reference
"RFC 2579: Textual Conventions for SMIv2";
}
typedef mac-address {
type string {
pattern '[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}';
}
description
"The mac-address type represents an IEEE 802 MAC address.
The canonical representation uses lowercase characters.
In the value set and its semantics, this type is equivalent
to the MacAddress textual convention of the SMIv2.";
reference
"IEEE 802: IEEE Standard for Local and Metropolitan Area
Networks: Overview and Architecture
RFC 2579: Textual Conventions for SMIv2";
}
/*** collection of XML-specific types ***/
typedef xpath1.0 {
type string;
description
"This type represents an XPATH 1.0 expression.
When a schema node is defined that uses this type, the
description of the schema node MUST specify the XPath
context in which the XPath expression is evaluated.";
reference
"XPATH: XML Path Language (XPath) Version 1.0";
}
/*** collection of string types ***/
typedef hex-string {
type string {
pattern '([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?';
}
description
"A hexadecimal string with octets represented as hex digits
separated by colons. The canonical representation uses
lowercase characters.";
}
typedef uuid {
type string {
pattern '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-'
+ '[0-9a-fA-F]{4}-[0-9a-fA-F]{12}';
}
description
"A Universally Unique IDentifier in the string representation
defined in RFC 4122. The canonical representation uses
lowercase characters.
The following is an example of a UUID in string representation:
f81d4fae-7dec-11d0-a765-00a0c91e6bf6
";
reference
"RFC 4122: A Universally Unique IDentifier (UUID) URN
Namespace";
}
typedef dotted-quad {
type string {
pattern
'(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}'
+ '([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])';
}
description
"An unsigned 32-bit number expressed in the dotted-quad
notation, i.e., four octets written as decimal numbers
and separated with the '.' (full stop) character.";
}
}
@@ -0,0 +1,20 @@
module infix-system {
prefix "infix-sys";
yang-version 1.1;
namespace "urn:infix:params:xml:ns:yang:infix-system";
import ietf-system {
prefix "sys";
}
revision 2014-08-06 {
description "Initial revision.";
}
augment "/sys:system" {
leaf motd {
type string;
description "Set the MotD (Message of the Day), which is shown after login";
}
}
}
@@ -0,0 +1,19 @@
module kernelkit-infix-deviations {
yang-version 1.1;
namespace "urn:kernelkit:params:xml:ns:yang:infix";
prefix "kkit";
import ietf-system {
prefix sys;
}
organization "KernelKit";
contact "kernelkit@googlegroups.com";
description "Infix deviations in standard yang models.";
revision "2023-04-03" {
description "Initial revision.";
}
deviation /sys:system/sys:clock/sys:timezone/sys:timezone-utc-offset/sys:timezone-utc-offset {
deviate not-supported;
}
}
+33
View File
@@ -0,0 +1,33 @@
*~
*.o
*.la
*.lo
*.so
.deps
.libs
/aclocal.m4
/autom4te.cache/
/aux
/clixon.xml
/compile
/config.h
/config.h.in
/config.guess
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/m4
/missing
GPATH
GRTAGS
GTAGS
Makefile
Makefile.in
+3
View File
@@ -0,0 +1,3 @@
SUBDIRS = src xml
DISTCLEANFILES = *~ *.d
ACLOCAL_AMFLAGS = -I m4
+74
View File
@@ -0,0 +1,74 @@
Infix Extensions to klish-plugin-sysrepo
----------------------------------------
Tailor `klish` and `klish-plugin-sysrepo` to suit Infix. The resulting
CLI is, in many respects, still very similar to Juniper's JunOS, since
that is also the primary inspiration for klish, with some additions
and modifications.
Broadly speaking, the CLI is split up in two major modes:
- Admin/Exec: This is the default mode, where operational commands can
be issued.
- Configure: Where a new candidate configuration is prepared.
### Admin/Exec Mode
The following table lists the available commands:
| Command | Description | Hotkey |
|--------------------|---------------------------------------|--------|
| `configure` | Enter configuration mode | |
| `copy <src> <dst>` | Copy `<src>` to `<dst>` | |
| `exit` | Exit | `C-d` |
| `logout` | Alias for `exit` | |
| `shell` | Start system shell | |
| `show <item>` | Show various configuration and status | |
#### Copy Command
`copy <src> <dst>`
Where `<src>` is one of:
- `factory-config`
- `startup-config`
- `running-config`
And `<dst>` is one of:
- `startup-config`
- `running-config`
#### Show Command
`show <item>`
The following table lists the available items:
| Item | Description |
|------------------|--------------------------------------|
| `running-config` | Show the active system configuration |
| | |
### Configure
The following table lists the available commands:
| Command | Description | Hotkey |
|------------|--------------------------------------------------------------|--------|
| `abort` | Abandon candidate | `C-d` |
| `check` | Validate candidate | |
| `commit` | Commit current candidate to running-config | |
| `delete` | Delete configuration setting(s) | |
| `diff` | Summarize uncommitted changes | |
| `do` | Execute operational mode command | |
| `edit` | Descend to the specified configuration node | |
| `exit` | Ascend to the parent configuration node, or abort (from top) | |
| `leave` | Finalize candidate and apply to running-config | `C-z` |
| `no` | Alias for delete | |
| `rollback` | Restore candidate to running-config | |
| `set` | Set configuration setting | |
| `show` | Show configuration | |
| `top` | Ascend to the configuration root | |
| `up` | Ascend to the parent configuration node | `C-c` |
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
autoreconf -W portability -vifm
+33
View File
@@ -0,0 +1,33 @@
AC_PREREQ(2.61)
AC_INIT([klinfix], [1.0.0],
[https://github.com/kernelkit/infix/issues])
AM_INIT_AUTOMAKE(1.11 foreign subdir-objects)
AM_SILENT_RULES(yes)
LT_INIT
AC_CONFIG_FILES([
Makefile
src/Makefile
xml/Makefile
])
AC_PROG_CC
AC_PROG_INSTALL
# Check for pkg-config first, warn if it's not installed
PKG_PROG_PKG_CONFIG
PKG_CHECK_MODULES([sysrepo], [sysrepo >= 2.2.60])
AC_CHECK_LIB([klish], [kplugin_new],, AC_MSG_ERROR([Klish not found]))
test "x$prefix" = xNONE && prefix=$ac_default_prefix
test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
KLISHLIBDIR=`eval echo $libdir/klish`
AC_SUBST(KLISHLIBDIR)
KLISHCONFDIR=`eval echo $sysconfdir/klish`
AC_SUBST(KLISHCONFDIR)
AC_OUTPUT
+9
View File
@@ -0,0 +1,9 @@
CFLAGS = -Wall -Wextra -Werror -Wno-unused-parameter
pluginsdir = $(KLISHLIBDIR)/plugins
plugins_LTLIBRARIES = kplugin-klinfix.la
kplugin_klinfix_la_CFLAGS = $(sysrepo_CFLAGS) $(klish_CFLAGS) $(CFLAGS)
kplugin_klinfix_la_LIBADD = $(sysrepo_LIBS) $(klish_LIBS)
kplugin_klinfix_la_LDFLAGS = -module -avoid-version -shared
kplugin_klinfix_la_SOURCES = klinfix.c
+155
View File
@@ -0,0 +1,155 @@
#include <assert.h>
#include <stdio.h>
#include <sysrepo.h>
#include <sysrepo_types.h>
#include <klish/kplugin.h>
#include <klish/kcontext.h>
const uint8_t kplugin_klinfix_major = 1;
const uint8_t kplugin_klinfix_minor = 0;
int klix_ds_from_str(const char *text, sr_datastore_t *ds)
{
size_t len = strlen(text);
if (!strncmp("startup-config", text, len))
*ds = SR_DS_STARTUP;
else if (!strncmp("running-config", text, len))
*ds = SR_DS_RUNNING;
else if (!strncmp("candidate-config", text, len))
*ds = SR_DS_CANDIDATE;
else if (!strncmp("operational-config", text, len))
*ds = SR_DS_OPERATIONAL;
else if (!strncmp("factory-config", text, len))
*ds = SR_DS_FACTORY_DEFAULT;
else
return -1;
return 0;
}
int klix_copy(kcontext_t *ctx)
{
kpargv_t *pargv = kcontext_pargv(ctx);
sr_datastore_t srcds, dstds;
kparg_t *srcarg, *dstarg;
sr_session_ctx_t *sess;
sr_conn_ctx_t *conn;
int err;
srcarg = kpargv_find(pargv, "src");
dstarg = kpargv_find(pargv, "dst");
if (!srcarg || !dstarg)
goto err;
if (klix_ds_from_str(kparg_value(srcarg), &srcds)) {
fprintf(stderr,
"Error: \"%s\" is not the name of any known datastore\n",
kparg_value(srcarg));
goto err;
}
if (klix_ds_from_str(kparg_value(dstarg), &dstds)) {
fprintf(stderr,
"Error: \"%s\" is not the name of any known datastore\n",
kparg_value(dstarg));
goto err;
}
switch (srcds) {
case SR_DS_STARTUP:
case SR_DS_RUNNING:
case SR_DS_FACTORY_DEFAULT:
break;
default:
fprintf(stderr,
"Error: \"%s\" is not a valid source datastore\n",
kparg_value(srcarg));
goto err;
}
switch (dstds) {
case SR_DS_STARTUP:
case SR_DS_RUNNING:
break;
default:
fprintf(stderr,
"Error: \"%s\" is not a valid destination datastore\n",
kparg_value(dstarg));
goto err;
}
if (sr_connect(SR_CONN_DEFAULT, &conn)) {
fprintf(stderr, "Error: Connection to datastore failed\n");
goto err;
}
if (sr_session_start(conn, dstds, &sess)) {
fprintf(stderr, "Error: Unable to open transaction to %s\n",
kparg_value(dstarg));
goto err_disconnect;
}
err = sr_copy_config(sess, NULL, srcds, 0);
if (err) {
fprintf(stderr, "Error: Unable to copy configuration (%d)\n",
err);
goto err_disconnect;
}
sr_disconnect(conn);
return 0;
err_disconnect:
sr_disconnect(conn);
err:
return -1;
}
int klix_commit(kcontext_t *ctx)
{
sr_session_ctx_t *sess;
sr_conn_ctx_t *conn;
int err;
if (sr_connect(SR_CONN_DEFAULT, &conn)) {
fprintf(stderr, "Error: Connection to datastore failed\n");
goto err;
}
if (sr_session_start(conn, SR_DS_RUNNING, &sess)) {
fprintf(stderr,
"Error: Unable to open transaction to running-config\n");
goto err_disconnect;
}
err = sr_copy_config(sess, NULL, SR_DS_CANDIDATE, 0);
if (err) {
fprintf(stderr,
"Error: Unable to commit candidate to running (%d)\n",
err);
goto err;
}
sr_disconnect(conn);
return 0;
err_disconnect:
sr_disconnect(conn);
err:
return -1;
}
int kplugin_klinfix_fini(kcontext_t *ctx)
{
return 0;
}
int kplugin_klinfix_init(kcontext_t *ctx)
{
kplugin_t *plugin = kcontext_plugin(ctx);
kplugin_add_syms(plugin, ksym_new("klix_copy", klix_copy));
kplugin_add_syms(plugin, ksym_new("klix_commit", klix_commit));
return 0;
}
+2
View File
@@ -0,0 +1,2 @@
klishconfdir = $(KLISHCONFDIR)
klishconf_DATA = $(wildcard *.xml)
+215
View File
@@ -0,0 +1,215 @@
<?xml version="1.0" encoding="UTF-8"?>
<KLISH
xmlns="https://klish.libcode.org/klish3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://src.libcode.org/pkun/klish/src/master/klish.xsd">
<PLUGIN name="klish"/>
<PLUGIN name="klinfix"/>
<PLUGIN name="script"/>
<PLUGIN name="sysrepo">
ShowBrackets = y
ShowSemicolons = y
FirstKeyWithStatement = n
MultiKeysWithStatement = y
Colorize = y
Indent = 2
</PLUGIN>
<PTYPE name="DATASTORE">
<COMPL>
<ACTION sym="printl">factory-config</ACTION>
<ACTION sym="printl">startup-config</ACTION>
<ACTION sym="printl">running-config</ACTION>
</COMPL>
<ACTION sym="STRING"/>
</PTYPE>
<PTYPE name="RW_DATASTORE">
<COMPL>
<ACTION sym="printl">startup-config</ACTION>
<ACTION sym="printl">running-config</ACTION>
</COMPL>
<ACTION sym="STRING"/>
</PTYPE>
<PTYPE name="PLINE_SET">
<COMPL>
<ACTION sym="srp_compl@sysrepo"/>
</COMPL>
<HELP>
<ACTION sym="srp_help@sysrepo"/>
</HELP>
<ACTION sym="PLINE_SET@sysrepo"/>
</PTYPE>
<PTYPE name="PLINE_DEL">
<COMPL>
<ACTION sym="srp_compl@sysrepo"/>
</COMPL>
<HELP>
<ACTION sym="srp_help@sysrepo"/>
</HELP>
<ACTION sym="PLINE_DEL@sysrepo"/>
</PTYPE>
<PTYPE name="PLINE_EDIT">
<COMPL>
<ACTION sym="srp_compl@sysrepo"/>
</COMPL>
<HELP>
<ACTION sym="srp_help@sysrepo"/>
</HELP>
<ACTION sym="PLINE_EDIT@sysrepo"/>
</PTYPE>
<PTYPE name="PLINE_INSERT_FROM">
<COMPL>
<ACTION sym="srp_compl@sysrepo"/>
</COMPL>
<HELP>
<ACTION sym="srp_help@sysrepo"/>
</HELP>
<ACTION sym="PLINE_INSERT_FROM@sysrepo"/>
</PTYPE>
<PTYPE name="PLINE_INSERT_TO">
<COMPL>
<ACTION sym="srp_compl_insert_to@sysrepo"/>
</COMPL>
<HELP>
<ACTION sym="srp_help_insert_to@sysrepo"/>
</HELP>
<ACTION sym="PLINE_INSERT_TO@sysrepo"/>
</PTYPE>
<VIEW name="main">
<HOTKEY key="^D" cmd="exit"/>
<PROMPT>
<ACTION sym="prompt" exec_on="always">%u@%h:exec> </ACTION>
</PROMPT>
<COMMAND name="shell" help="Enter system shell">
<ACTION sym="script" interactive="true">/bin/sh -l</ACTION>
</COMMAND>
<COMMAND name="exit" help="Exit">
<ACTION sym="nav">pop</ACTION>
</COMMAND>
<COMMAND name="logout" help="Alias for exit">
<ACTION sym="nav">pop</ACTION>
</COMMAND>
<COMMAND name="configure" help="Create new candidate-config based on running-config">
<ACTION sym="nav">replace configure</ACTION>
</COMMAND>
<COMMAND name="copy" help="Copy">
<PARAM name="src" ptype="/DATASTORE" help="Source datastore"/>
<PARAM name="dst" ptype="/RW_DATASTORE" help="Destination datastore"/>
<ACTION sym="klix_copy@klinfix"/>
</COMMAND>
<COMMAND name="show" help="Show" mode="switch">
<COMMAND name="running-config" help="Show running-config">
<ACTION sym="srp_show_running@sysrepo"/>
</COMMAND>
</COMMAND>
</VIEW>
<VIEW name="configure">
<PROMPT>
<ACTION sym="srp_prompt_edit_path@sysrepo"/>
<ACTION sym="prompt" exec_on="always">%u@%h:configure> </ACTION>
</PROMPT>
<HOTKEY key="^C" cmd="up"/>
<HOTKEY key="^D" cmd="abort"/>
<HOTKEY key="^Z" cmd="leave"/>
<!-- Move -->
<COMMAND name="abort" help="Abandon candidate">
<ACTION sym="srp_rollback@sysrepo"/>
<ACTION sym="nav">replace main</ACTION>
</COMMAND>
<COMMAND name="leave" help="Finalize candidate and apply to running-config">
<ACTION sym="klix_commit@klinfix"/>
<ACTION sym="nav" exec_on="success">replace main</ACTION>
</COMMAND>
<COMMAND name="exit" help="Ascend to the parent configuration node, or abort (from top)">
<ACTION sym="srp_up@sysrepo"/>
<ACTION sym="klix_commit@klinfix" exec_on="fail"/>
<ACTION sym="nav" exec_on="success">replace main</ACTION>
</COMMAND>
<COMMAND name="up" help="Ascend to the parent configuration node">
<ACTION sym="srp_up@sysrepo"/>
</COMMAND>
<COMMAND name="top" help="Ascend to the configuration root">
<ACTION sym="srp_top@sysrepo"/>
</COMMAND>
<COMMAND name="edit" help="Descend to the specified configuration node">
<PARAM name="path" ptype="/PLINE_EDIT" max="100"/>
<ACTION sym="srp_edit@sysrepo"/>
</COMMAND>
<!-- Edit -->
<COMMAND name="set" help="Set configuration setting">
<PARAM name="path" ptype="/PLINE_SET" max="100"/>
<ACTION sym="srp_set@sysrepo"/>
</COMMAND>
<COMMAND name="delete" help="Delete configuration setting(s)">
<PARAM name="path" ptype="/PLINE_DEL" max="100"/>
<ACTION sym="srp_del@sysrepo"/>
</COMMAND>
<COMMAND name="no" help="Alias for delete">
<PARAM name="path" ptype="/PLINE_DEL" max="100"/>
<ACTION sym="srp_del@sysrepo"/>
</COMMAND>
<COMMAND name="commit" help="Commit current candidate to running-config">
<ACTION sym="klix_commit@klinfix"/>
</COMMAND>
<COMMAND name="rollback" help="Restore candidate to running-config">
<ACTION sym="srp_rollback@sysrepo"/>
</COMMAND>
<!-- Display -->
<COMMAND name="check" help="Validate candidate">
<ACTION sym="srp_verify@sysrepo"/>
</COMMAND>
<COMMAND name="show" help="Show configuration">
<PARAM name="path" ptype="/PLINE_EDIT" min="0" max="100"/>
<ACTION sym="srp_show@sysrepo"/>
</COMMAND>
<COMMAND name="diff" help="Summarize uncommitted changes">
<PARAM name="path" ptype="/PLINE_EDIT" min="0" max="100"/>
<ACTION sym="srp_diff@sysrepo"/>
</COMMAND>
<!-- Misc -->
<COMMAND name="do" help="Execute operational mode command">
<VIEW name="operational" ref="/main"/>
</COMMAND>
</VIEW>
</KLISH>
+2
View File
@@ -0,0 +1,2 @@
*.o
net
+14
View File
@@ -0,0 +1,14 @@
LDLIBS = -lite
EXEC = net
net: main.o
$(CC) -o $@ $^ $(LDLIBS)
check: $(EXEC)
./test.sh
clean:
$(RM) net
distclean: clean
$(RM) *.o *~
+158
View File
@@ -0,0 +1,158 @@
net - an iproute2 wrapper
=========================
The net tool is a wrapper around the iproute2 and ethtool programs to
set up networking on a Linux system. It also handles transitions from
a previously configured state to the next while attempting to keep the
amount of changes needed to a minimum.
Concept Overview
----------------
net applies a configuration from `/run/net/next`, where `next` is a file
that holds the number of the current generation. It is up to the user,
i.e., sysrepo, to create the below tree structure with commands to run.
The setup we start with and later move `eth4` from `lag0` to `br0`.
vlan1
______/____
[____br0____]
/ / \ \
eth1 eth2 eth3 lag0
/ \
eth4 eth5
Consider the case when `next` contains `0`, net reads all interfaces, in
dependency order, from `/run/net/0/` running all `ip-link` and `ip-addr`
commands. When done, it writes `0` to the file `/run/net/gen` and then
removes the `next` file to confirm the generation has been activated.
**Note:** it is currently up to the user to remove any old generation.
/run/net/0/
|-- lo/
| |-- deps/
| |-- ip-link.up
| `-- ip-addr.up
|-- br0/
| |-- deps/
| | |-- eth1 -> ../../eth1
| | |-- eth2 -> ../../eth2
| | |-- eth3 -> ../../eth3
| | `-- lag0 -> ../../lag0
| |-- ip-link.up
| `-- ip-addr.up
|-- eth0/
| |-- deps/
| |-- ip-link.up
| `-- ip-addr.up
|-- ethX/
| |-- ...
: :
|-- lag0/
| |-- deps/
| | |-- eth4 -> ../../eth4
| | `-- eth5 -> ../../eth5
| |-- ip-link.up
| `-- ip-addr.up
`-- vlan1/
|-- deps/
| `-- br0 -> ../../br0
|-- ip-link.up
`-- ip-addr.up
The `deps/` sub-directory for each of the interfaces contains symlinks
to any interfaces this interface may depend on. I.e., those interfaces
are evaluated first.
Essentially, all leaves must be set up before their parents. Moving a
leaf from one parent to another, e.g., from lag0 to br0, is tricky, it
involves traversing the previous dependency order when removing leaves,
and traversing the next dependency order when addning, see next section
for an example.
Example
-------
net is built to complement sysrepo. The idea is to listen to any added,
deleted, modified states in sysrepo changes to a candidate configuration
and generate the `next` generation. However, as mentioned previously,
when moving leaves between parents we must do so in the dependency order
of the current generation.
So, the user (sysrepo) needs to add `.dn` scripts in the current tree,
and `.up` scripts in the next.
In our example, interface `eth4` is moved from `lag0` to `br0`, so we
need to run `eth4/ip-link.dn` in the current generation first to remove
`eth4` from `lag0` before its `ip-link.up` script in the next generation
sets `eth4` as a bridge member instead.
We traverse the current generation and execute all `.dn` scripts:
/run/net/<GEN>/
|-- lo/
| `-- deps/
|-- br0/
| |-- deps/
| | |-- eth1 -> ../../eth1
| | |-- eth2 -> ../../eth2
| | |-- eth3 -> ../../eth3
| | `-- lag0 -> ../../lag0
| `-- ip-link.up
|-- eth0/
| |-- deps/
| |-- ip-link.up
| `-- ip-addr.up
|-- eth4/
| |-- deps/
| |-- ip-link.dn
| `-- ip-link.up
|-- lag0/
| |-- deps/
| | |-- eth4 -> ../../eth4
| | `-- eth5 -> ../../eth5
| `-- ip-link.up
`-- vlan1/
|-- deps/
| `-- br0 -> ../../br0
|-- ip-link.up
`-- ip-addr.up
Now we can run all the `.up` scripts in the next generation:
/run/net/<GEN+1>/
|-- lo/
| |-- deps/
| |-- ip-link.up
| `-- ip-addr.up
|-- br0/
| |-- deps/
| | |-- eth1 -> ../../eth1
| | |-- eth2 -> ../../eth2
| | |-- eth3 -> ../../eth3
| | |-- eth4 -> ../../eth4
| | `-- lag0 -> ../../lag0
| `-- ip-link.up
|-- eth0/
| |-- deps/
| |-- ip-link.up
| `-- ip-addr.up
|-- eth4/
| |-- deps/
| |-- ip-link.dn
| `-- ip-link.up
|-- lag0/
| |-- deps/
| | `-- eth5 -> ../../eth5
| `-- ip-link.up
`-- vlan1/
|-- deps/
| `-- br0 -> ../../br0
|-- ip-link.up
`-- ip-addr.up
+222
View File
@@ -0,0 +1,222 @@
#include <err.h>
#include <libgen.h>
#include <stdlib.h>
#include <sysexits.h>
#include <net/if.h>
#include <libite/lite.h>
#define _PATH_NET "/run/net"
#define DEBUG 0
#define dbg(fmt, args...) if (DEBUG) warnx(fmt, ##args)
static char **handled;
static int if_num;
static void if_alloc(int num)
{
handled = calloc(num, sizeof(char *));
if (!handled)
err(1, "calloc");
if_num = num;
}
static void if_free()
{
for (int i = 0; i < if_num; i++) {
if (handled[i])
free(handled[i]);
}
free(handled);
}
static void if_done(char *ifname)
{
for (int i = 0; i < if_num; i++) {
if (handled[i]) {
if (strcmp(handled[i], ifname))
continue;
return;
}
handled[i] = strdup(ifname);
break;
}
}
static int if_find(char *ifname)
{
for (int i = 0; i < if_num; i++) {
if (handled[i] && !strcmp(handled[i], ifname))
return 1;
}
return 0;
}
static int deps(char *ipath, char *ifname, const char *action)
{
char path[strlen(ipath) + 42];
int num, rc = -1;
char **files;
char *cmd;
snprintf(path, sizeof(path), "%s/deps", ipath);
num = dir(path, NULL, NULL, &files, 0);
for (int i = 0; i < num; i++) {
char dpath[sizeof(path) + strlen(files[i])];
char *ifnm = files[i];
char *rp;
snprintf(dpath, sizeof(dpath), "%s/%s", path, ifnm);
rp = realpath(dpath, NULL);
if (!rp)
continue;
deps(rp, ifnm, action);
free(ifnm);
free(rp);
}
if (if_find(ifname))
return 0;
snprintf(path, sizeof(path), "%s/%s", ipath, action);
cmd = realpath(path, NULL);
if (!cmd || access(cmd, X_OK)) {
if (errno == ENOENT)
rc = 0; /* no action for this interface */
goto done;
}
rc = systemf("%s", cmd);
done:
free(cmd);
if_done(ifname);
return rc;
}
static int iter(char *path, size_t len, const char *action)
{
char **files;
int rc = 0;
int num;
num = dir(path, NULL, NULL, &files, 0);
if_alloc(num);
for (int j = 0; j < num; j++) {
char *ifname = files[j];
char ipath[len];
snprintf(ipath, sizeof(ipath), "%s/%s", path, ifname);
dbg("Calling deps(%s, %s, %s)", ipath, ifname, action);
rc += deps(ipath, ifname, action);
free(ifname);
}
if_free();
return rc;
}
static int deactivate(const char *net, char *gen)
{
char path[strlen(net) + strlen(gen) + 5 + IFNAMSIZ];
char *action[] = {
"ip-addr.dn",
"ip-link.dn",
};
int rc = 0;
snprintf(path, sizeof(path), "%s/%s", net, gen);
for (size_t i = 0; i < NELEMS(action); i++)
rc += iter(path, sizeof(path), action[i]);
return rc;
}
static int activate(const char *net, char *gen)
{
char path[strlen(net) + strlen(gen) + 5 + IFNAMSIZ];
char *action[] = {
"ip-link.up",
"ip-addr.up",
};
int rc = 0;
snprintf(path, sizeof(path), "%s/%s", net, gen);
for (size_t i = 0; i < NELEMS(action); i++)
rc += iter(path, sizeof(path), action[i]);
return rc;
}
static int load_gen(const char *net, char *gen, char *buf, size_t len)
{
FILE *fp;
fp = fopenf("r", "%s/%s", net, gen);
if (!fp)
return EX_OSFILE;
if (!fgets(buf, len, fp))
return EX_IOERR;
fclose(fp);
chomp(buf);
return 0;
}
static int save_gen(const char *net, char *gen, char *buf)
{
FILE *fp;
fp = fopenf("w", "%s/%s", net, gen);
if (!fp)
return -1;
fprintf(fp, "%s\n", buf);
fclose(fp);
return 0;
}
int main(void)
{
const char *net = _PATH_NET;
char curr[512], next[512];
int rc;
if (getenv("NET_DIR"))
net = getenv("NET_DIR");
if (access(net, X_OK)) {
if (makedir(net, 0755))
err(1, "makedir");
}
if ((rc = load_gen(net, "next", next, sizeof(next)))) {
if (rc == EX_IOERR)
warnx("missing next generation");
exit(0); /* nothing to do */
}
if ((rc = load_gen(net, "gen", curr, sizeof(curr)))) {
if (rc == EX_IOERR)
errx(rc, "missing current generation");
/* no current generation */
} else {
rc = deactivate(net, curr);
if (rc)
err(1, "failed deactivating current generation");
}
rc = activate(net, next);
if (rc)
err(1, "failed activating next generation");
if (save_gen(net, "gen", next))
err(1, "next generation applied, failed current");
if (fremove("%s/next", net))
err(1, "failed removing %s/next", net);
return 0;
}
+101
View File
@@ -0,0 +1,101 @@
#!/bin/sh
# Generate test directory structure for verifying the net tool
set -e
NET_DIR=/tmp/net
export NET_DIR
gensh()
{
cat <<-EOF >"$1"
#!/bin/sh
echo "Running \$0 ..."
EOF
chmod +x "$1"
}
check()
{
if ./net; then
printf "\n[\033[1;32m OK \033[0m] Checking %s\n" "$1"
return 0
fi
printf "\n[\033[1;31FAIL\033[0m] Checking %s\n" "$1"
return 1
}
echo "Verify that all leaves and dependencies are"
echo "evaluated first:"
echo " vlan1"
echo " ______/____"
echo " [____br0____]"
echo " / / \\ \\"
echo "lo eth0 eth1 eth2 eth3 lag0"
echo " / \\"
echo " eth4 eth5"
printf "_______________________________________________\n\n"
mkdir -p $NET_DIR/0
echo 0 > $NET_DIR/next
mkdir -p $NET_DIR/0/lo/deps
gensh $NET_DIR/0/lo/ip-link.up
mkdir -p $NET_DIR/0/eth0/deps
mkdir -p $NET_DIR/0/eth1/deps
mkdir -p $NET_DIR/0/eth2/deps
mkdir -p $NET_DIR/0/eth3/deps
mkdir -p $NET_DIR/0/eth4/deps
gensh $NET_DIR/0/eth0/ip-link.up
gensh $NET_DIR/0/eth1/ip-link.up
gensh $NET_DIR/0/eth2/ip-link.up
gensh $NET_DIR/0/eth3/ip-link.up
gensh $NET_DIR/0/eth4/ip-link.up
mkdir -p $NET_DIR/0/lag0/deps
ln -sf ../../eth3 $NET_DIR/0/lag0/deps/
ln -sf ../../eth4 $NET_DIR/0/lag0/deps/
gensh $NET_DIR/0/lag0/ip-link.up
mkdir -p $NET_DIR/0/br0/deps
ln -sf ../../eth1 $NET_DIR/0/br0/deps/
ln -sf ../../eth2 $NET_DIR/0/br0/deps/
ln -sf ../../lag0 $NET_DIR/0/br0/deps/
gensh $NET_DIR/0/br0/ip-link.up
mkdir -p $NET_DIR/0/vlan1/deps
ln -sf ../../br0 $NET_DIR/0/vlan1/deps/
gensh $NET_DIR/0/vlan1/ip-link.up
check "initial startup, gen 0"
printf "_______________________________________________\n\n"
mkdir -p $NET_DIR/1
mkdir -p $NET_DIR/1/lo/deps
mkdir -p $NET_DIR/1/eth0/deps
mkdir -p $NET_DIR/1/eth1/deps
mkdir -p $NET_DIR/1/eth2/deps
mkdir -p $NET_DIR/1/eth3/deps
mkdir -p $NET_DIR/1/eth4/deps
mkdir -p $NET_DIR/1/lag0/deps
ln -sf ../../eth4 $NET_DIR/1/lag0/deps/
mkdir -p $NET_DIR/1/br0/deps
ln -sf ../../eth1 $NET_DIR/1/br0/deps/
ln -sf ../../eth2 $NET_DIR/1/br0/deps/
ln -sf ../../eth3 $NET_DIR/1/br0/deps/
mkdir -p $NET_DIR/1/vlan1/deps
ln -sf ../../br0 $NET_DIR/1/vlan1/deps/
gensh $NET_DIR/0/eth3/ip-link.dn
gensh $NET_DIR/1/eth3/ip-link.up
echo 1 > $NET_DIR/next
check "move eth3 from lag0 to br0"
exit 0