diff --git a/Makefile b/Makefile index a87ef80..797208e 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ LIBBSD_CFLAGS = LIBBSD_LIBS = PACKAGE_NAME := ifupdown-ng -PACKAGE_VERSION := 0.10.0 +PACKAGE_VERSION := 0.10.2 PACKAGE_BUGREPORT := https://github.com/ifupdown-ng/ifupdown-ng/issues/new diff --git a/cmd/ifquery.c b/cmd/ifquery.c index 720ab58..ffb62f2 100644 --- a/cmd/ifquery.c +++ b/cmd/ifquery.c @@ -182,7 +182,8 @@ list_state(struct lif_dict *state, struct match_options *opts) if (listing_running) printf("%s\n", entry->key); else - printf("%s=%s %zu\n", entry->key, rec->mapped_if, rec->refcount); + printf("%s=%s %zu%s\n", entry->key, rec->mapped_if, rec->refcount, + rec->is_explicit ? " explicit" : ""); } } diff --git a/cmd/ifupdown.c b/cmd/ifupdown.c index 1edc2d0..2c32706 100644 --- a/cmd/ifupdown.c +++ b/cmd/ifupdown.c @@ -95,7 +95,7 @@ acquire_state_lock(const char *state_path, const char *lifname) } bool -skip_interface(struct lif_interface *iface, const char *ifname) +skip_interface(struct lif_interface *iface, const char *ifname, struct lif_dict *state, bool update_state) { if (iface->is_template) { @@ -123,16 +123,23 @@ skip_interface(struct lif_interface *iface, const char *ifname) if (up && iface->refcount > 0) { if (exec_opts.verbose) - fprintf(stderr, "%s: skipping auto interface %s (already configured), use --force to force configuration\n", - argv0, ifname); + fprintf(stderr, "%s: skipping %sinterface %s (already configured), use --force to force configuration\n", + argv0, iface->is_auto ? "auto " : "", ifname); + + if (update_state) + { + iface->is_explicit = true; + lif_state_upsert(state, ifname, iface); + } + return true; } if (!up && iface->refcount == 0) { if (exec_opts.verbose) - fprintf(stderr, "%s: skipping auto interface %s (already deconfigured), use --force to force deconfiguration\n", - argv0, ifname); + fprintf(stderr, "%s: skipping %sinterface %s (already deconfigured), use --force to force deconfiguration\n", + argv0, iface->is_auto ? "auto " : "", ifname); return true; } @@ -140,7 +147,7 @@ skip_interface(struct lif_interface *iface, const char *ifname) } bool -change_interface(struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *ifname) +change_interface(struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *ifname, bool update_state) { int lockfd = acquire_state_lock(exec_opts.state_file, ifname); @@ -150,7 +157,7 @@ change_interface(struct lif_interface *iface, struct lif_dict *collection, struc return false; } - if (skip_interface(iface, ifname)) + if (skip_interface(iface, ifname, state, update_state)) { if (lockfd != -1) close(lockfd); @@ -178,6 +185,12 @@ change_interface(struct lif_interface *iface, struct lif_dict *collection, struc if (lockfd != -1) close(lockfd); + if (up && update_state) + { + iface->is_explicit = true; + lif_state_upsert(state, ifname, iface); + } + return true; } @@ -202,7 +215,7 @@ change_auto_interfaces(struct lif_dict *collection, struct lif_dict *state, stru fnmatch(opts->include_pattern, iface->ifname, 0)) continue; - if (!change_interface(iface, collection, state, iface->ifname)) + if (!change_interface(iface, collection, state, iface->ifname, false)) return false; } @@ -307,7 +320,7 @@ ifupdown_main(int argc, char *argv[]) iface = entry->data; } - if (!change_interface(iface, &collection, &state, ifname)) + if (!change_interface(iface, &collection, &state, ifname, true)) return update_state_file_and_exit(EXIT_FAILURE, &state); } diff --git a/cmd/multicall.c b/cmd/multicall.c index 705000f..582765c 100644 --- a/cmd/multicall.c +++ b/cmd/multicall.c @@ -66,6 +66,8 @@ applet_cmp(const void *a, const void *b) void multicall_usage(int status) __attribute__((noreturn)); +struct if_applet ifupdown_applet; + int main(int argc, char *argv[]) { @@ -85,9 +87,11 @@ main(int argc, char *argv[]) } self_applet = *app; - process_options(*app, argc, argv); - return (*app)->main(argc, argv); + if (self_applet != &ifupdown_applet) + process_options(*app, argc, argv); + + return self_applet->main(argc, argv); } int @@ -99,8 +103,6 @@ multicall_main(int argc, char *argv[]) return main(argc - 1, argv + 1); } -struct if_applet ifupdown_applet; - void multicall_usage(int status) { diff --git a/libifupdown/interface-file.c b/libifupdown/interface-file.c index d2e88ac..5d94fc8 100644 --- a/libifupdown/interface-file.c +++ b/libifupdown/interface-file.c @@ -172,6 +172,9 @@ handle_auto(struct lif_interface_file_parse_state *state, char *token, char *buf if (!state->cur_iface->is_template) state->cur_iface->is_auto = true; + if (state->cur_iface->is_auto) + state->cur_iface->is_explicit = true; + return true; } @@ -253,6 +256,12 @@ handle_iface(struct lif_interface_file_parse_state *state, char *token, char *bu return true; } + /* if we have a current interface, call lif_interface_finalize to finalize any + * address properties by converting them to CIDR and flushing the netmask property. + */ + if (state->cur_iface != NULL) + lif_interface_finalize(state->cur_iface); + state->cur_iface = lif_interface_collection_find(state->collection, ifname); if (state->cur_iface == NULL) { @@ -491,6 +500,11 @@ lif_interface_file_parse(struct lif_interface_file_parse_state *state, const cha } fclose(f); + + /* finalize any open interface */ + if (state->cur_iface != NULL) + lif_interface_finalize(state->cur_iface); + state->cur_filename = old_filename; state->cur_lineno = old_lineno; return true; diff --git a/libifupdown/interface.c b/libifupdown/interface.c index 532edba..0294383 100644 --- a/libifupdown/interface.c +++ b/libifupdown/interface.c @@ -84,6 +84,19 @@ count_set_bits(const char *netmask) return r; } +static inline size_t +determine_interface_netmask(const struct lif_interface *iface, const struct lif_address *addr) +{ + /* if netmask is not set, default to /24 or /64, ifupdown does so too */ + size_t netmask = addr->domain == AF_INET6 ? 64 : 24; + + struct lif_dict_entry *entry = lif_dict_find(&iface->vars, "netmask"); + if (entry != NULL) + netmask = count_set_bits(entry->data); + + return netmask; +} + bool lif_address_format_cidr(const struct lif_interface *iface, struct lif_dict_entry *entry, char *buf, size_t buflen) { @@ -91,14 +104,7 @@ lif_address_format_cidr(const struct lif_interface *iface, struct lif_dict_entry size_t orig_netmask = addr->netmask; if (!addr->netmask) - { - /* if netmask is not set, default to 255.255.255.0, ifupdown does so too */ - addr->netmask = 24; - - struct lif_dict_entry *entry = lif_dict_find(&iface->vars, "netmask"); - if (entry != NULL) - addr->netmask = count_set_bits(entry->data); - } + addr->netmask = determine_interface_netmask(iface, addr); if (!lif_address_unparse(addr, buf, buflen, true)) { @@ -122,16 +128,6 @@ lif_interface_init(struct lif_interface *interface, const char *ifname) /* keep the 'vlan' executor as a config hint for backwards compatibility */ if (strchr(ifname, '.') != NULL) lif_interface_use_executor(interface, "vlan"); - - if (!lif_config.use_hostname_for_dhcp) - return; - - /* learn a reasonable default hostname */ - struct utsname un; - if (uname(&un) < 0) - return; - - lif_dict_add(&interface->vars, "hostname", strdup(un.nodename)); } bool @@ -211,6 +207,46 @@ lif_interface_use_executor(struct lif_interface *interface, const char *executor interface->is_bridge = true; else if (!strcmp(executor, "bond")) interface->is_bond = true; + + if (strcmp(executor, "dhcp") || !lif_config.use_hostname_for_dhcp) + return; + + /* learn a reasonable default hostname */ + struct utsname un; + if (uname(&un) < 0) + return; + + lif_dict_add(&interface->vars, "hostname", strdup(un.nodename)); +} + +void +lif_interface_finalize(struct lif_interface *interface) +{ + struct lif_node *iter; + + /* convert all addresses to CIDR notation. */ + LIF_DICT_FOREACH(iter, &interface->vars) + { + struct lif_dict_entry *entry = iter->data; + + if (strcmp(entry->key, "address")) + continue; + + struct lif_address *addr = entry->data; + + if (!addr->netmask) + addr->netmask = determine_interface_netmask(interface, addr); + } + + /* with all addresses converted to CIDR, netmask property is no longer needed. */ + struct lif_dict_entry *entry = lif_dict_find(&interface->vars, "netmask"); + + if (entry != NULL) + { + free(entry->data); + + lif_dict_delete_entry(&interface->vars, entry); + } } void @@ -223,6 +259,7 @@ lif_interface_collection_init(struct lif_dict *collection) /* always enable loopback interface as part of a collection */ if_lo = lif_interface_collection_find(collection, "lo"); if_lo->is_auto = true; + if_lo->is_explicit = true; lif_interface_use_executor(if_lo, "loopback"); } diff --git a/libifupdown/interface.h b/libifupdown/interface.h index c86efdc..49c867d 100644 --- a/libifupdown/interface.h +++ b/libifupdown/interface.h @@ -50,6 +50,7 @@ struct lif_interface { bool is_bond; bool is_template; bool is_pending; + bool is_explicit; bool has_config_error; /* error found in interface configuration */ @@ -74,6 +75,7 @@ extern bool lif_interface_address_add(struct lif_interface *interface, const cha extern void lif_interface_address_delete(struct lif_interface *interface, const char *address); extern void lif_interface_fini(struct lif_interface *interface); extern void lif_interface_use_executor(struct lif_interface *interface, const char *executor); +extern void lif_interface_finalize(struct lif_interface *interface); extern void lif_interface_collection_init(struct lif_dict *collection); extern void lif_interface_collection_fini(struct lif_dict *collection); diff --git a/libifupdown/lifecycle.c b/libifupdown/lifecycle.c index c12bb38..056783f 100644 --- a/libifupdown/lifecycle.c +++ b/libifupdown/lifecycle.c @@ -396,6 +396,15 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par continue; } + if (!up && iface->is_explicit) + { + if (opts->verbose) + fprintf(stderr, "ifupdown: skipping dependent interface %s (of %s) -- interface is marked as explicitly configured\n", + iface->ifname, parent->ifname); + + continue; + } + if (opts->verbose) fprintf(stderr, "ifupdown: changing state of dependent interface %s (of %s) to %s\n", iface->ifname, parent->ifname, up ? "up" : "down"); diff --git a/libifupdown/state.c b/libifupdown/state.c index ed4f344..05701ce 100644 --- a/libifupdown/state.c +++ b/libifupdown/state.c @@ -29,8 +29,13 @@ lif_state_read(struct lif_dict *state, FILE *fd) char *bufp = linebuf; char *ifname = lif_next_token(&bufp); char *refcount = lif_next_token(&bufp); + char *explicit = lif_next_token(&bufp); size_t rc = 1; char *equals_p = strchr(linebuf, '='); + bool is_explicit = false; + + if (*explicit && !strcmp(explicit, "explicit")) + is_explicit = true; if (*refcount) { @@ -42,12 +47,12 @@ lif_state_read(struct lif_dict *state, FILE *fd) if (equals_p == NULL) { - lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = ifname, .refcount = rc }); + lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = ifname, .refcount = rc, .is_explicit = is_explicit }); continue; } *equals_p++ = '\0'; - lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = equals_p, .refcount = rc }); + lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = equals_p, .refcount = rc, .is_explicit = is_explicit }); } return true; @@ -99,6 +104,7 @@ lif_state_upsert(struct lif_dict *state, const char *ifname, struct lif_interfac rec->mapped_if = strdup(iface->ifname); rec->refcount = iface->refcount; + rec->is_explicit = iface->is_explicit; lif_dict_add(state, ifname, rec); } @@ -128,7 +134,8 @@ lif_state_write(const struct lif_dict *state, FILE *f) struct lif_dict_entry *entry = iter->data; struct lif_state_record *rec = entry->data; - fprintf(f, "%s=%s %zu\n", entry->key, rec->mapped_if, rec->refcount); + fprintf(f, "%s=%s %zu%s\n", entry->key, rec->mapped_if, rec->refcount, + rec->is_explicit ? " explicit" : ""); } } @@ -175,6 +182,7 @@ lif_state_sync(struct lif_dict *state, struct lif_dict *if_collection) struct lif_interface *iface = lif_interface_collection_find(if_collection, rec->mapped_if); iface->refcount = rec->refcount; + iface->is_explicit = rec->is_explicit; } return true; diff --git a/libifupdown/state.h b/libifupdown/state.h index bec039c..ae2ba5c 100644 --- a/libifupdown/state.h +++ b/libifupdown/state.h @@ -17,11 +17,14 @@ #define LIBIFUPDOWN_STATE_H__GUARD #include +#include #include "libifupdown/interface.h" struct lif_state_record { char *mapped_if; size_t refcount; + + bool is_explicit; }; extern bool lif_state_read(struct lif_dict *state, FILE *f); diff --git a/tests/Kyuafile b/tests/Kyuafile index a7fb909..6a8019b 100644 --- a/tests/Kyuafile +++ b/tests/Kyuafile @@ -2,6 +2,7 @@ syntax(2) test_suite('ifupdown-ng') +atf_test_program{name='multicall_test'} atf_test_program{name='ifquery_test'} atf_test_program{name='ifup_test'} atf_test_program{name='ifdown_test'} diff --git a/tests/fixtures/stanza-merging.interfaces b/tests/fixtures/stanza-merging.interfaces new file mode 100644 index 0000000..6327cbc --- /dev/null +++ b/tests/fixtures/stanza-merging.interfaces @@ -0,0 +1,14 @@ +# cidr and without-cidr should be equivalent +iface cidr + address 203.0.113.1/32 + +iface cidr + address 203.0.113.2/24 + +iface without-cidr + address 203.0.113.1 + netmask 32 + +iface without-cidr + address 203.0.113.2 + netmask 24 diff --git a/tests/fixtures/without-netmask.interfaces b/tests/fixtures/without-netmask.interfaces new file mode 100644 index 0000000..7537c67 --- /dev/null +++ b/tests/fixtures/without-netmask.interfaces @@ -0,0 +1,5 @@ +iface v6 + address 2001:470:1f10::1 + +iface v4 + address 203.0.113.2 diff --git a/tests/ifquery_test b/tests/ifquery_test index e095255..d85cd27 100755 --- a/tests/ifquery_test +++ b/tests/ifquery_test @@ -34,7 +34,11 @@ tests_init \ vlan_explicit_learned_dependency \ vlan_guessed_learned_dependency \ vlan_complex_learned_dependency \ - wireguard + wireguard \ + default_netmask_v4 \ + default_netmask_v6 \ + stanza_merging_with_cidr \ + stanza_merging_without_cidr noargs_body() { atf_check -s exit:1 -e ignore ifquery -S/dev/null @@ -226,3 +230,29 @@ wireguard_body() { -o match:"use wireguard" \ ifquery -E $EXECUTORS_LINUX -i $FIXTURES/wireguard.interfaces wg0 } + +default_netmask_v4_body() { + atf_check -s exit:0 \ + -o match:"203.0.113.2/24" \ + ifquery -i $FIXTURES/without-netmask.interfaces -p address v4 +} + +default_netmask_v6_body() { + atf_check -s exit:0 \ + -o match:"2001:470:1f10::1/64" \ + ifquery -i $FIXTURES/without-netmask.interfaces -p address v6 +} + +stanza_merging_with_cidr_body() { + atf_check -s exit:0 \ + -o match:"203.0.113.1/32" \ + -o match:"203.0.113.2/24" \ + ifquery -i $FIXTURES/stanza-merging.interfaces -p address cidr +} + +stanza_merging_without_cidr_body() { + atf_check -s exit:0 \ + -o match:"203.0.113.1/32" \ + -o match:"203.0.113.2/24" \ + ifquery -i $FIXTURES/stanza-merging.interfaces -p address without-cidr +} diff --git a/tests/multicall_test b/tests/multicall_test new file mode 100755 index 0000000..0b4c3ab --- /dev/null +++ b/tests/multicall_test @@ -0,0 +1,11 @@ +#!/usr/bin/env atf-sh + +. $(atf_get_srcdir)/test_env.sh + +tests_init \ + regress_getopt + +regress_getopt_body() { + atf_check -e not-inline:'-F: applet not found' -o ignore -s exit:1 \ + ifupdown ifquery -F +}