Compare commits

...

20 commits

Author SHA1 Message Date
Ariadne Conill
975e4f1bea ifupdown-ng 0.10.2. 2020-12-02 11:52:01 -07:00
Ariadne Conill
bcf090f7ad tests: ifquery: add tests to verify stanza merging works as expected 2020-12-02 11:51:34 -07:00
Ariadne Conill
f671e2d410 interface: add lif_interface_finalize() which rewrites addresses as CIDR when an interface stanza ends 2020-12-02 11:51:27 -07:00
Ariadne Conill
6da55d9299 tests: add fixture illustrating how smart stanza merging should work 2020-12-02 11:51:14 -07:00
Ariadne Conill
726a888c8d interface: fix default netmask size for AF_INET6 addresses (closes #130) 2020-12-02 11:10:00 -07:00
Ariadne Conill
b425ad75e3 tests: ifquery: add tests for checking the default netmasks 2020-12-02 11:08:48 -07:00
Ariadne Conill
bd730bece4 interface: learn hostname from uname(2) only if an interface requests the dhcp executor
Closes #74.
2020-12-02 10:50:38 -07:00
Ariadne Conill
9e859d458b tests: add getopt regression test for multicall stub 2020-12-02 10:49:13 -07:00
Ariadne Conill
40c7ed53e1 multicall: do not call getopt_long() inside the stub applet 2020-12-02 10:49:09 -07:00
Ariadne Conill
42836934e9 ifupdown-ng 0.10.1. 2020-10-21 09:21:21 -06:00
Ariadne Conill
93dff79567 state: explicitly check for explicit keyword when loading from the ifstate file 2020-10-21 09:19:55 -06:00
Ariadne Conill
61da61dfdb ifupdown: upgrade dependent interfaces to explicit interfaces if explicitly requested 2020-10-21 09:19:45 -06:00
Ariadne Conill
e16c24203e ifupdown: record explicitly configured interfaces as explicit in ifstate 2020-10-21 09:19:32 -06:00
Ariadne Conill
ebae949462 ifquery: when querying state, denote presence of the explicit flag 2020-10-21 09:19:15 -06:00
Ariadne Conill
0464118429 state: synchronize is_explicit from state records to parsed interface collections 2020-10-21 09:19:09 -06:00
Ariadne Conill
69999cd357 state: write and restore explicit flag from ifstate 2020-10-21 09:19:03 -06:00
Ariadne Conill
d92cfdd184 state: add lif_state_record::is_explicit 2020-10-21 09:18:58 -06:00
Ariadne Conill
045211514b lifecycle: skip parent interfaces marked as explicitly configured when going down
these interfaces will be taken down by ifdown itself when appropriate
2020-10-21 09:18:54 -06:00
Ariadne Conill
0d28f94a47 interface: auto interfaces are always explicit 2020-10-21 09:18:30 -06:00
Ariadne Conill
0abafedb9a interface: add lif_interface::is_explicit 2020-10-21 09:18:18 -06:00
15 changed files with 187 additions and 37 deletions

View file

@ -4,7 +4,7 @@ LIBBSD_CFLAGS =
LIBBSD_LIBS = LIBBSD_LIBS =
PACKAGE_NAME := ifupdown-ng 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 PACKAGE_BUGREPORT := https://github.com/ifupdown-ng/ifupdown-ng/issues/new

View file

@ -182,7 +182,8 @@ list_state(struct lif_dict *state, struct match_options *opts)
if (listing_running) if (listing_running)
printf("%s\n", entry->key); printf("%s\n", entry->key);
else 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" : "");
} }
} }

View file

@ -95,7 +95,7 @@ acquire_state_lock(const char *state_path, const char *lifname)
} }
bool 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) if (iface->is_template)
{ {
@ -123,16 +123,23 @@ skip_interface(struct lif_interface *iface, const char *ifname)
if (up && iface->refcount > 0) if (up && iface->refcount > 0)
{ {
if (exec_opts.verbose) if (exec_opts.verbose)
fprintf(stderr, "%s: skipping auto interface %s (already configured), use --force to force configuration\n", fprintf(stderr, "%s: skipping %sinterface %s (already configured), use --force to force configuration\n",
argv0, ifname); argv0, iface->is_auto ? "auto " : "", ifname);
if (update_state)
{
iface->is_explicit = true;
lif_state_upsert(state, ifname, iface);
}
return true; return true;
} }
if (!up && iface->refcount == 0) if (!up && iface->refcount == 0)
{ {
if (exec_opts.verbose) if (exec_opts.verbose)
fprintf(stderr, "%s: skipping auto interface %s (already deconfigured), use --force to force deconfiguration\n", fprintf(stderr, "%s: skipping %sinterface %s (already deconfigured), use --force to force deconfiguration\n",
argv0, ifname); argv0, iface->is_auto ? "auto " : "", ifname);
return true; return true;
} }
@ -140,7 +147,7 @@ skip_interface(struct lif_interface *iface, const char *ifname)
} }
bool 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); 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; return false;
} }
if (skip_interface(iface, ifname)) if (skip_interface(iface, ifname, state, update_state))
{ {
if (lockfd != -1) if (lockfd != -1)
close(lockfd); close(lockfd);
@ -178,6 +185,12 @@ change_interface(struct lif_interface *iface, struct lif_dict *collection, struc
if (lockfd != -1) if (lockfd != -1)
close(lockfd); close(lockfd);
if (up && update_state)
{
iface->is_explicit = true;
lif_state_upsert(state, ifname, iface);
}
return true; 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)) fnmatch(opts->include_pattern, iface->ifname, 0))
continue; continue;
if (!change_interface(iface, collection, state, iface->ifname)) if (!change_interface(iface, collection, state, iface->ifname, false))
return false; return false;
} }
@ -307,7 +320,7 @@ ifupdown_main(int argc, char *argv[])
iface = entry->data; 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); return update_state_file_and_exit(EXIT_FAILURE, &state);
} }

View file

@ -66,6 +66,8 @@ applet_cmp(const void *a, const void *b)
void multicall_usage(int status) __attribute__((noreturn)); void multicall_usage(int status) __attribute__((noreturn));
struct if_applet ifupdown_applet;
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
@ -85,9 +87,11 @@ main(int argc, char *argv[])
} }
self_applet = *app; self_applet = *app;
if (self_applet != &ifupdown_applet)
process_options(*app, argc, argv); process_options(*app, argc, argv);
return (*app)->main(argc, argv); return self_applet->main(argc, argv);
} }
int int
@ -99,8 +103,6 @@ multicall_main(int argc, char *argv[])
return main(argc - 1, argv + 1); return main(argc - 1, argv + 1);
} }
struct if_applet ifupdown_applet;
void void
multicall_usage(int status) multicall_usage(int status)
{ {

View file

@ -172,6 +172,9 @@ handle_auto(struct lif_interface_file_parse_state *state, char *token, char *buf
if (!state->cur_iface->is_template) if (!state->cur_iface->is_template)
state->cur_iface->is_auto = true; state->cur_iface->is_auto = true;
if (state->cur_iface->is_auto)
state->cur_iface->is_explicit = true;
return true; return true;
} }
@ -253,6 +256,12 @@ handle_iface(struct lif_interface_file_parse_state *state, char *token, char *bu
return true; 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); state->cur_iface = lif_interface_collection_find(state->collection, ifname);
if (state->cur_iface == NULL) if (state->cur_iface == NULL)
{ {
@ -491,6 +500,11 @@ lif_interface_file_parse(struct lif_interface_file_parse_state *state, const cha
} }
fclose(f); fclose(f);
/* finalize any open interface */
if (state->cur_iface != NULL)
lif_interface_finalize(state->cur_iface);
state->cur_filename = old_filename; state->cur_filename = old_filename;
state->cur_lineno = old_lineno; state->cur_lineno = old_lineno;
return true; return true;

View file

@ -84,6 +84,19 @@ count_set_bits(const char *netmask)
return r; 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 bool
lif_address_format_cidr(const struct lif_interface *iface, struct lif_dict_entry *entry, char *buf, size_t buflen) 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; size_t orig_netmask = addr->netmask;
if (!addr->netmask) if (!addr->netmask)
{ addr->netmask = determine_interface_netmask(iface, addr);
/* 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);
}
if (!lif_address_unparse(addr, buf, buflen, true)) 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 */ /* keep the 'vlan' executor as a config hint for backwards compatibility */
if (strchr(ifname, '.') != NULL) if (strchr(ifname, '.') != NULL)
lif_interface_use_executor(interface, "vlan"); 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 bool
@ -211,6 +207,46 @@ lif_interface_use_executor(struct lif_interface *interface, const char *executor
interface->is_bridge = true; interface->is_bridge = true;
else if (!strcmp(executor, "bond")) else if (!strcmp(executor, "bond"))
interface->is_bond = true; 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 void
@ -223,6 +259,7 @@ lif_interface_collection_init(struct lif_dict *collection)
/* always enable loopback interface as part of a collection */ /* always enable loopback interface as part of a collection */
if_lo = lif_interface_collection_find(collection, "lo"); if_lo = lif_interface_collection_find(collection, "lo");
if_lo->is_auto = true; if_lo->is_auto = true;
if_lo->is_explicit = true;
lif_interface_use_executor(if_lo, "loopback"); lif_interface_use_executor(if_lo, "loopback");
} }

View file

@ -50,6 +50,7 @@ struct lif_interface {
bool is_bond; bool is_bond;
bool is_template; bool is_template;
bool is_pending; bool is_pending;
bool is_explicit;
bool has_config_error; /* error found in interface configuration */ 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_address_delete(struct lif_interface *interface, const char *address);
extern void lif_interface_fini(struct lif_interface *interface); 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_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_init(struct lif_dict *collection);
extern void lif_interface_collection_fini(struct lif_dict *collection); extern void lif_interface_collection_fini(struct lif_dict *collection);

View file

@ -396,6 +396,15 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par
continue; 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) if (opts->verbose)
fprintf(stderr, "ifupdown: changing state of dependent interface %s (of %s) to %s\n", fprintf(stderr, "ifupdown: changing state of dependent interface %s (of %s) to %s\n",
iface->ifname, parent->ifname, up ? "up" : "down"); iface->ifname, parent->ifname, up ? "up" : "down");

View file

@ -29,8 +29,13 @@ lif_state_read(struct lif_dict *state, FILE *fd)
char *bufp = linebuf; char *bufp = linebuf;
char *ifname = lif_next_token(&bufp); char *ifname = lif_next_token(&bufp);
char *refcount = lif_next_token(&bufp); char *refcount = lif_next_token(&bufp);
char *explicit = lif_next_token(&bufp);
size_t rc = 1; size_t rc = 1;
char *equals_p = strchr(linebuf, '='); char *equals_p = strchr(linebuf, '=');
bool is_explicit = false;
if (*explicit && !strcmp(explicit, "explicit"))
is_explicit = true;
if (*refcount) if (*refcount)
{ {
@ -42,12 +47,12 @@ lif_state_read(struct lif_dict *state, FILE *fd)
if (equals_p == NULL) 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; continue;
} }
*equals_p++ = '\0'; *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; 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->mapped_if = strdup(iface->ifname);
rec->refcount = iface->refcount; rec->refcount = iface->refcount;
rec->is_explicit = iface->is_explicit;
lif_dict_add(state, ifname, rec); 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_dict_entry *entry = iter->data;
struct lif_state_record *rec = entry->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); struct lif_interface *iface = lif_interface_collection_find(if_collection, rec->mapped_if);
iface->refcount = rec->refcount; iface->refcount = rec->refcount;
iface->is_explicit = rec->is_explicit;
} }
return true; return true;

View file

@ -17,11 +17,14 @@
#define LIBIFUPDOWN_STATE_H__GUARD #define LIBIFUPDOWN_STATE_H__GUARD
#include <stdio.h> #include <stdio.h>
#include <stdbool.h>
#include "libifupdown/interface.h" #include "libifupdown/interface.h"
struct lif_state_record { struct lif_state_record {
char *mapped_if; char *mapped_if;
size_t refcount; size_t refcount;
bool is_explicit;
}; };
extern bool lif_state_read(struct lif_dict *state, FILE *f); extern bool lif_state_read(struct lif_dict *state, FILE *f);

View file

@ -2,6 +2,7 @@ syntax(2)
test_suite('ifupdown-ng') test_suite('ifupdown-ng')
atf_test_program{name='multicall_test'}
atf_test_program{name='ifquery_test'} atf_test_program{name='ifquery_test'}
atf_test_program{name='ifup_test'} atf_test_program{name='ifup_test'}
atf_test_program{name='ifdown_test'} atf_test_program{name='ifdown_test'}

View file

@ -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

View file

@ -0,0 +1,5 @@
iface v6
address 2001:470:1f10::1
iface v4
address 203.0.113.2

View file

@ -34,7 +34,11 @@ tests_init \
vlan_explicit_learned_dependency \ vlan_explicit_learned_dependency \
vlan_guessed_learned_dependency \ vlan_guessed_learned_dependency \
vlan_complex_learned_dependency \ vlan_complex_learned_dependency \
wireguard wireguard \
default_netmask_v4 \
default_netmask_v6 \
stanza_merging_with_cidr \
stanza_merging_without_cidr
noargs_body() { noargs_body() {
atf_check -s exit:1 -e ignore ifquery -S/dev/null atf_check -s exit:1 -e ignore ifquery -S/dev/null
@ -226,3 +230,29 @@ wireguard_body() {
-o match:"use wireguard" \ -o match:"use wireguard" \
ifquery -E $EXECUTORS_LINUX -i $FIXTURES/wireguard.interfaces wg0 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
}

11
tests/multicall_test Executable file
View file

@ -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
}