diff --git a/doc/networking.md b/doc/networking.md index f276ef67..a800829c 100644 --- a/doc/networking.md +++ b/doc/networking.md @@ -598,6 +598,13 @@ As shown, the link-local IPv4 address is configured with `set autconf enabled true`. The resulting address (169.254.1.3/16) is of type *random* ([ietf-ip.yang][2]). +The IPv4LL client also supports a `request-address` setting which can be +used to "seed" the client's starting address. If the address is free it +will be used, otherwise it falls back to the default algorithm. + + admin@example:/config/interface/eth0/ipv4/> set autoconf request-address 169.254.1.2 + + #### Use of DHCP for IPv4 address assignment ![Using DHCP for IPv4 address assignment](img/ip-address-example-ipv4-dhcp.svg) diff --git a/package/skeleton-init-finit/skeleton-init-finit.mk b/package/skeleton-init-finit/skeleton-init-finit.mk index 0d1be306..c1339e22 100644 --- a/package/skeleton-init-finit/skeleton-init-finit.mk +++ b/package/skeleton-init-finit/skeleton-init-finit.mk @@ -45,8 +45,10 @@ endif ifeq ($(BR2_PACKAGE_AVAHI_AUTOIPD),y) define SKELETON_INIT_FINIT_SET_AVAHI_AUTOIPD - echo "service [2345789] name:zeroconf :%i avahi-autoipd --force-bind --syslog %i -- ZeroConf for %i" \ - > $(FINIT_D)/available/zeroconf@.conf + echo 'service name:zeroconf :%i env:-/etc/default/zeroconf-%i \' >$(FINIT_D)/available/zeroconf@.conf + echo ' kill:5 pid:!/run/avahi-autoipd.%i.pid \'>>$(FINIT_D)/available/zeroconf@.conf + echo ' [2345789] log avahi-autoipd $$ZEROCONF_ARGS %i \'>>$(FINIT_D)/available/zeroconf@.conf + echo ' -- ZeroConf for %i' >>$(FINIT_D)/available/zeroconf@.conf endef SKELETON_INIT_FINIT_POST_INSTALL_TARGET_HOOKS += SKELETON_INIT_FINIT_SET_AVAHI_AUTOIPD endif diff --git a/src/confd/src/ietf-interfaces.c b/src/confd/src/ietf-interfaces.c index 772abbc3..56392f56 100644 --- a/src/confd/src/ietf-interfaces.c +++ b/src/confd/src/ietf-interfaces.c @@ -537,45 +537,89 @@ static int netdag_gen_ipv6_autoconf(struct dagger *net, struct lyd_node *cif, return 0; } +/* + * Check if ipv4 is enabled, only then can autoconf be enabled, in all + * other cases it must be disabled. Since we have multiple settings in + * autoconf, we check if either is modified (diff), in which case we not + * only enable, but also "touch" the Finit service for avahi-autoipd to + * ensure it is (re)started. + * + * Note: in Infix, regardless of the IPv4 configuration, any link-local + * link-local address is disabled when the interface is being used + * as a bridge port. + * + * Also, IPv4LL is not defined for loopback, so always skip there. + */ static int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif, struct lyd_node *dif) { struct lyd_node *ipconf = lydx_get_child(cif, "ipv4"); struct lyd_node *ipdiff = lydx_get_child(dif, "ipv4"); const char *ifname = lydx_get_cattr(dif, "name"); - struct lyd_node *node, *zcip; - struct lydx_diff nd; + struct lyd_node *zcip; + char defaults[64]; FILE *initctl; int err = 0; + if (!strcmp(ifname, "lo")) + return 0; + + /* client defults for this interface, needed in both cases */ + snprintf(defaults, sizeof(defaults), "/etc/default/zeroconf-%s", ifname); + + /* no ipv4 at all, ipv4 selectively disabled, or interface is a bridge port */ if (!ipconf || !lydx_is_enabled(ipconf, "enabled") || is_bridge_port(cif)) goto disable; - if (lydx_get_op(lydx_get_child(ipdiff, "enabled")) == LYDX_OP_REPLACE) { - node = lydx_get_child(ipconf, "autoconf"); - if (node && lydx_is_enabled(node, "enabled")) - goto enable; - goto disable; - } + /* + * when enabled, we may have been enabled before, but skipped + * for various reasons: was bridge port, ipv4 was disabled... + */ + zcip = lydx_get_child(ipconf, "autoconf"); + if (zcip && lydx_is_enabled(zcip, "enabled")) { + struct lyd_node *node; + const char *addr; + int diff = 0; + FILE *fp; - zcip = lydx_get_child(ipdiff, "autoconf"); - if (!zcip || !(node = lydx_get_child(zcip, "enabled"))) - return 0; + /* check for any changes in this container */ + node = lydx_get_child(ipdiff, "autoconf"); + if (node) { + const struct lyd_node *tmp; - lydx_get_diff(node, &nd); - if (nd.new && !strcmp(nd.val, "true")) { - enable: - initctl = dagger_fopen_next(net, "init", ifname, - 60, "zeroconf-up.sh"); + tmp = lydx_get_child(node, "enabled"); + if (tmp) + diff++; + tmp = lydx_get_child(node, "request-address"); + if (tmp) + diff++; + } + + fp = fopen(defaults, "w"); + if (!fp) { + ERRNO("Failed creating %s, cannot enable IPv4LL on %s", defaults, ifname); + return -EIO; + } + + fprintf(fp, "ZEROCONF_ARGS=\"--force-bind --syslog "); + addr = lydx_get_cattr(zcip, "request-address"); + if (addr) + fprintf(fp, "--start=%s", addr); + fprintf(fp, "\"\n"); + fclose(fp); + + initctl = dagger_fopen_next(net, "init", ifname, 60, "zeroconf-up.sh"); if (!initctl) return -EIO; - fprintf(initctl, - "initctl -bnq enable zeroconf@%s.conf\n", ifname); + /* on enable, or reactivation, it is enough to ensure the service is enabled */ + fprintf(initctl, "initctl -bnq enable zeroconf@%s.conf\n", ifname); + /* on changes to autoconf we must ensure Finit restarts the service */ + if (diff) + fprintf(initctl, "initctl -bnq touch zeroconf@%s.conf\n", ifname); } else { disable: - initctl = dagger_fopen_current(net, "exit", ifname, - 40, "zeroconf-down.sh"); + initctl = dagger_fopen_current(net, "exit", ifname, 40, "zeroconf-down.sh"); if (!initctl) { /* check if in bootstrap (pre gen 0) */ if (errno == EUNATCH) @@ -583,9 +627,8 @@ static int netdag_gen_ipv4_autoconf(struct dagger *net, struct lyd_node *cif, return -EIO; } - fprintf(initctl, - "initctl -bnq disable zeroconf@%s.conf\n", ifname); - + fprintf(initctl, "initctl -bnq disable zeroconf@%s.conf\n", ifname); + fprintf(initctl, "rm -f %s\n", defaults); err = netdag_exit_reload(net); } diff --git a/src/confd/yang/confd.inc b/src/confd/yang/confd.inc index 78b17949..35458c45 100644 --- a/src/confd/yang/confd.inc +++ b/src/confd/yang/confd.inc @@ -25,7 +25,7 @@ MODULES=( "ietf-hardware@2018-03-13.yang -e hardware-state" "infix-hardware@2024-04-25.yang" "ieee802-dot1q-types@2022-10-29.yang" - "infix-ip@2023-09-14.yang" + "infix-ip@2024-09-16.yang" "infix-if-type@2024-01-29.yang" "infix-routing@2024-09-20.yang" "ieee802-dot1ab-lldp@2022-03-15.yang" diff --git a/src/confd/yang/infix-ip@2023-09-14.yang b/src/confd/yang/infix-ip@2023-09-14.yang deleted file mode 100644 index 9730b92b..00000000 --- a/src/confd/yang/infix-ip@2023-09-14.yang +++ /dev/null @@ -1,68 +0,0 @@ -module infix-ip { - yang-version 1.1; - namespace "urn:infix:params:xml:ns:yang:infix-ip"; - prefix infix-ip; - - import ietf-interfaces { - prefix if; - } - import ietf-ip { - prefix ip; - } - import ietf-inet-types { - prefix inet; - } - import ietf-yang-types { - prefix yang; - } - - description - "This module augments ietf-ip with an IPv4 link-local autoconf"; - - revision 2023-09-14 { - description - "Added deviations for unsupported parts of ietf-ip."; - } - - revision 2023-04-24 { - description - "Initial revision."; - reference - "RFC 7277: A YANG Data Model for IP Management"; - } - - /* - * Data nodes - */ - augment "/if:interfaces/if:interface/ip:ipv4" { - container autoconf { - description - "Parameters to control the autoconfiguration of IPv4 address."; - - leaf enabled { - type boolean; - default false; - description - "Use a ZeroConf/IPv4LL agent to retrieve an 169.254/16 address."; - reference - "RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses"; - } - } - } - - deviation "/if:interfaces/if:interface/ip:ipv4/ip:address/ip:subnet/ip:netmask" { - deviate not-supported; - } - - deviation "/if:interfaces/if:interface/ip:ipv4/ip:neighbor" { - deviate not-supported; - } - - deviation "/if:interfaces/if:interface/ip:ipv6/ip:address/ip:status" { - deviate not-supported; - } - - deviation "/if:interfaces/if:interface/ip:ipv6/ip:neighbor" { - deviate not-supported; - } -} diff --git a/src/confd/yang/infix-ip@2024-09-16.yang b/src/confd/yang/infix-ip@2024-09-16.yang new file mode 100644 index 00000000..88a1c93f --- /dev/null +++ b/src/confd/yang/infix-ip@2024-09-16.yang @@ -0,0 +1,79 @@ +module infix-ip { + yang-version 1.1; + namespace "urn:infix:params:xml:ns:yang:infix-ip"; + prefix infix-ip; + + import ietf-interfaces { + prefix if; + } + import ietf-ip { + prefix ip; + } + import ietf-inet-types { + prefix inet; + } + import ietf-yang-types { + prefix yang; + } + + description "This module augments ietf-ip with Infix extensions and deviations."; + + revision 2024-09-16 { + description "Add support for IPv4LL request-address."; + reference "Internal."; + } + revision 2023-09-14 { + description "Added deviations for unsupported parts of ietf-ip."; + reference "Internal."; + } + revision 2023-04-24 { + description "Initial revision."; + reference "RFC 7277: A YANG Data Model for IP Management"; + } + + /* + * Data nodes + */ + augment "/if:interfaces/if:interface/ip:ipv4" { + container autoconf { + description "Parameters to control the autoconfiguration of IPv4 address."; + reference "RFC 3927: Dynamic Configuration of IPv4 Link-Local Addresses"; + + leaf enabled { + description "Use a ZeroConf/IPv4LL agent to retrieve an 169.254/16 address."; + type boolean; + } + + leaf request-address { + description "Try to acquire the specified IP address, if available. + + With this setting the IPv4LL client will start by + requesting this address. However, if it is not + available it falls back to the default algorithm."; + type inet:ipv4-address; + must "substring(., 1, 7) = '169.254'" { + error-message "Must be from the IPv4LL range 169.254.0.0/16."; + } + must "not(substring(., string-length(.) - 1, 2) = '.0' or substring(., string-length(.) - 3, 4) = '.255')" { + error-message "Addresses ending in .0 or .255 are reserved."; + } + } + } + } + + deviation "/if:interfaces/if:interface/ip:ipv4/ip:address/ip:subnet/ip:netmask" { + deviate not-supported; + } + + deviation "/if:interfaces/if:interface/ip:ipv4/ip:neighbor" { + deviate not-supported; + } + + deviation "/if:interfaces/if:interface/ip:ipv6/ip:address/ip:status" { + deviate not-supported; + } + + deviation "/if:interfaces/if:interface/ip:ipv6/ip:neighbor" { + deviate not-supported; + } +}