Compare commits

...

625 commits

Author SHA1 Message Date
Zola
e978d1a42c Fix a bug in the wifi executor script
When stopping wpa_supplicant, the code is supposed to check if a file named $PIDFILE exists and kill the process listed inside. Instead it was checking if a directory named $PIDFILE exists and because this was never the case, killing of the wpa_supplicant process would always silently fail.

This would, after a few invocations of the ifup command, leave the system with large number of running wpa_supplicant processes, all trying to take control of the same interface.
2021-11-25 22:44:19 -06:00
Maximilian Wilhelm
80074c997f
Merge pull request #162 from BarbarossaTM/feature/vxlan-vtep-list
vxlan: Add support for PTMP setups and rename options to vxlan-peer-{ips,group}
2021-10-31 19:44:57 +01:00
Maximilian Wilhelm
0e99af7669 vxlan: Document that vxlan-phsydev is required for multicast setups
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-10-16 15:28:13 +02:00
Maximilian Wilhelm
b75e509f3d vxlan: Add support for PTMP setups and rename options to vxlan-peer-{ip,group}
This commit adds support for configuring static PTMP overlays with VXLAN by
  allowing to specify multiple IPs for »vxlan-peer-ips«.  If more than one IP
  is given ifupdown-ng will set up additional FDB entries for all peer IPs and
  the Linux Kernel will do ingres / head-end replication for BUM traffic.

  For a cleaner naming schema and simliar names to commercial vendor CLIs the
  options to specify unicast or multicast peers have been renamed and aliases
  added for compatibility to previous versions of ifupdown-ng:
   * »vxlan-remote-ip« now is named »vxlan-peer-ips«
   * »vxlan-remote-group« now is called »vxlan-peer-group«

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-10-16 15:27:30 +02:00
Ariadne Conill
d83c8259e6 tighten fallback busyloop implementation 2021-09-19 13:05:12 -05:00
Ariadne Conill
2477e7266c use process descriptors where available for readiness notification 2021-09-19 13:05:12 -05:00
Ariadne Conill
97b1a11be0 doc: document timeout parameter in manual pages 2021-09-19 13:05:12 -05:00
Ariadne Conill
941d7c51d7 implement execution timeouts for executors
Previously, it was possible for an executor to hang forever.  To mitigate
this, we now implement process execution timeouts for executors, by looping
with waitpid(..., WNOHANG) and sleeping.

This could be implemented more efficiently with process descriptors, see
pidfd_open(2), but that interface is Linux-specific and is only available
on Linux 5.3 or newer.
2021-09-19 13:05:12 -05:00
Ariadne Conill
7a46b61996
Merge pull request #160 from BarbarossaTM/chore/pedantic-gcc
Make gcc complain more and treat warnings as errors
2021-09-13 05:40:48 -05:00
Maximilian Wilhelm
571786ae91 Makefile: Make gcc be more pendantic and bail out on warnings
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-09-12 18:27:41 +02:00
Maximilian Wilhelm
dd3a99cfa8 Fix prototypes for ifctrstat(-linux)
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-09-12 18:27:11 +02:00
Maximilian Wilhelm
67fc80fc78 Fix missingp prototypes for static functions
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-09-12 18:26:07 +02:00
Maximilian Wilhelm
65e5e07c5f Fix prototype of append_to_buffer()
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-09-12 18:11:43 +02:00
Maximilian Wilhelm
0547924ee8 Fix delcaration/prototype for lif_compat_apply()
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-09-12 18:10:51 +02:00
Maximilian Wilhelm
4033f6374f Update copyright notice
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-09-01 19:21:21 +02:00
Ariadne Conill
b25448f42f kyua is available on Debian now 2021-08-08 12:14:20 -06:00
Ariadne Conill
96fa8ccbf9 build: add EXECUTOR_SCRIPTS_NATIVE 2021-07-10 18:28:55 -06:00
Maximilian Wilhelm
108c88014d doc: Clarify expected config file format for wireguard.
The configuration file format for use with 'wg-quick' and 'wg setconf' are
  imcompatible.  Explicitly state which format is required and how to convert
  a present configuration.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-06-17 18:12:04 +02:00
Ariadne Conill
9c00111932 ifupdown-ng 0.11.3. 2021-06-03 15:01:33 -06:00
Ariadne Conill
12ffc3de12 admin guide: update IRC server 2021-06-03 15:00:17 -06:00
Ariadne Conill
34753136b8 update IRC server 2021-06-03 11:18:27 -06:00
Maximilian Wilhelm
559b4ad942
Merge pull request #153 from BarbarossaTM/feature/tunnel-pimps
Brush up tunnels
2021-06-03 18:54:36 +02:00
Maximilian Wilhelm
ed9aae85ed doc: Add man page for tunnel interfaces
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-06-03 18:53:36 +02:00
Maximilian Wilhelm
73d9788fab tunnel executore: Add support for setting pmtudisc / ignore-df for tunnels.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-06-03 18:53:25 +02:00
Maximilian Wilhelm
2b358fafc6 tunnel executor: Add support for tunnel-local-dev option
The new "tunnel-local-dev <iface>" option allows to specify an interface
  to read the IPv4/IPv6 address from which should be used as "tunnel-local"
  address.  The address family is determined based on the given "tunnel-mode".

Closes #118

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-06-03 18:53:06 +02:00
Ariadne Conill
9467f85067 multicall: fix handling of -X options, closes #155 2021-06-01 10:31:06 -06:00
Ariadne Conill
6175961880
Merge pull request #152 from BarbarossaTM/chore/docs
doc: Clarify some interface file properties
2021-05-16 19:29:27 -08:00
Maximilian Wilhelm
c8a05a742b doc: Clarify some interface file properties
* Interface file is case-sensitive and all lower-case
 * Clariry netmask handling and fallback

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-05-16 17:59:24 +02:00
Ariadne Conill
d4907af84f ifupdown-ng 0.11.2. 2021-04-22 01:45:29 -06:00
Ariadne Conill
98241f4781
Merge pull request #150 from ifupdown-ng/feature/use-ip-addr-flush
static: use ip addr|route flush instead of manually deleting matching routes
2021-04-21 23:44:30 -08:00
Ariadne Conill
aee2d45e18 static: use ip addr|route flush instead of manually deleting matching routes
This ensures we wind up with a clean slate for the interface or VRF when taking
interfaces/VRFs down.

Closes #149.
2021-04-22 01:43:12 -06:00
Ariadne Conill
5860882ffb ifupdown-ng 0.11.1. 2021-04-07 09:39:44 -06:00
Ariadne Conill
0755f7c32d tests: add regression test for #148 2021-04-07 09:38:31 -06:00
Ariadne Conill
bd319a9166 interface-file: adjust the special handling for hostname properties to use dhcp-hostname
closes #148
2021-04-07 09:30:56 -06:00
Ariadne Conill
58b1cf1021 interface: automatic dhcp-hostname determination: use dhcp-hostname instead of legacy hostname property 2021-04-07 09:30:21 -06:00
Ariadne Conill
02bc14da19 interface-file: fix mapping of leasetime to dhcp-leasetime 2021-04-07 09:21:41 -06:00
Ariadne Conill
279d3818b0 ifupdown-ng 0.11.0. 2021-04-03 14:05:13 -06:00
Maximilian Wilhelm
f3f8164ef1
Merge pull request #140 from BarbarossaTM/feature/mpls
Add support for MPLS on Linux
2021-03-30 23:37:04 +02:00
Maximilian Wilhelm
667943f46c Add support for MPLS on Linux
Closes #135

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-30 23:29:52 +02:00
Maximilian Wilhelm
5b7f5b712b doc: Add references to forward and wifi man page
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-30 23:21:31 +02:00
Ariadne Conill
381c0ed1a5
Merge pull request #147 from BarbarossaTM/feature/tunnel
Add support for gretap tunnels over IPv6 networks
2021-03-25 18:10:31 -08:00
Maximilian Wilhelm
bec5fcefce tunnel executor: Add support for ip6gretap tunnels
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-25 21:17:05 +01:00
Maximilian Wilhelm
258d397d84
Merge pull request #146 from BarbarossaTM/chore/tests
Add test for bond executor
2021-03-24 15:10:02 +01:00
Maximilian Wilhelm
cea9840651 Add tests for bond executor
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-24 15:06:22 +01:00
Maximilian Wilhelm
e88a6b7e10 bond executor: Fix bond param regex
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-24 14:42:33 +01:00
Ariadne Conill
9faa988326
Merge pull request #144 from BarbarossaTM/fix/tunnels
tunnel executor: Make sure mode/type is 1st parameter
2021-03-21 19:49:10 -08:00
Maximilian Wilhelm
5f9ba7e246
Merge pull request #145 from BarbarossaTM/fix/executors
Makefile: Install all executors, sort them alphabetically
2021-03-21 20:13:31 +01:00
Maximilian Wilhelm
cd98102e62 Makefile: Install all executors, sort them alphabetically
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-21 20:11:01 +01:00
Maximilian Wilhelm
98b23370e8 gitignore: Add ifprase
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-21 02:37:12 +01:00
Maximilian Wilhelm
876a7700d7 interface-file: Map 'key' to 'tunnel-key'
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-21 02:35:25 +01:00
Maximilian Wilhelm
2cec4ed05c tunnel executor: Make sure mode/type is 1st parameter
Closes #143

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2021-03-18 20:20:01 +01:00
Erik Kooistra
e4fa275067 command: Added mandatory permissions for O_CREAT 2020-12-21 15:58:24 -07:00
Erik Kooistra
4c64b5138b libifupdown: Fixed bug where end pointer is not updated after realloc 2020-12-21 15:58:24 -07:00
Maximilian Wilhelm
1ee485666f
Merge pull request #139 from ifupdown-ng/feature/forward-executor
Feature/forward executor
2020-12-16 14:54:22 +01:00
Ariadne Conill
7ef6be6d98 doc: interfaces-forward: document the 100% no systemd behaviour guarantee 2020-12-16 06:52:43 -07:00
Ariadne Conill
606e98d7bd docs: add interfaces-forward(5) manual page 2020-12-16 06:31:17 -07:00
Ariadne Conill
ffcf1976b8 tests: add tests for forward executor 2020-12-16 06:23:38 -07:00
Ariadne Conill
1c2fddfbaa executor-scripts: add forward executor 2020-12-16 06:13:29 -07:00
Ariadne Conill
71bc4b3dc9
Merge pull request #137 from ifupdown-ng/fix/dhclient-executor-args
dhcp executor: Pass correct arguments to dhclient
2020-12-14 18:33:36 -07:00
A. Wilcox
b9d6c190e4
dhcp executor: Pass correct arguments to dhclient
They are opt-args, not pop-tarts.
2020-12-14 11:23:59 -06:00
Ariadne Conill
31bc5c3b10 multicall: remove bug report link from multicall help text 2020-12-04 01:02:28 -07:00
Ariadne Conill
f34ad12751 multicall: a little bit of polishing 2020-12-04 00:55:04 -07:00
Ariadne Conill
27c10c57d1 ifctrstat: lowercase the applet description 2020-12-04 00:51:35 -07:00
Ariadne Conill
d1286e57f9 multicall: print applet descriptions 2020-12-04 00:50:19 -07:00
Ariadne Conill
65aa268e9d ifquery: use single printf call for graphviz header, saves 16 bytes 2020-12-04 00:37:02 -07:00
Ariadne Conill
1f7fe26dd9 version: use printf() with a static string, compiler changes this into puts() automatically, saves additional 20 bytes 2020-12-04 00:31:53 -07:00
Ariadne Conill
3734aaecbd version: use a single printf() call for the entire about text, saves 104 bytes on aarch64 2020-12-04 00:26:01 -07:00
Ariadne Conill
8a8f56dda8 version: remove newline between copyright statements 2020-12-04 00:14:20 -07:00
Maximilian Wilhelm
bed0b67583
Merge pull request #134 from ifupdown-ng/feature/wifi-executor
wifi executor
2020-12-04 07:19:21 +01:00
Maximilian Wilhelm
383ae31372 wifi executor: And some sanity checks to stopping wpa_supplicant
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-12-04 07:01:47 +01:00
Ariadne Conill
d7cc2a9917 doc: interfaces: clarify that both wireless-tools and wpa_supplicant are needed for full functionality 2020-12-03 04:58:45 -07:00
Ariadne Conill
93c8827205 wifi executor: fix up some shell nitpicks 2020-12-03 04:56:47 -07:00
Ariadne Conill
589a1f3023 docs: interfaces-wifi: add manpage xrefs for iwconfig(8) and wpa_supplicant(8). 2020-12-02 19:29:22 -07:00
Ariadne Conill
4e3a4c6cb8 wifi executor: add support for insecure wifi networks 2020-12-02 19:27:28 -07:00
Ariadne Conill
eeb40937fb doc: document the wifi executor as interfaces-wifi(5). 2020-12-02 18:48:57 -07:00
Ariadne Conill
b2f5a62c35 wifi executor: protect against unintentionally clobbering $IF_WIFI_CONFIG_PATH 2020-12-02 18:31:39 -07:00
Ariadne Conill
f77d3558f7 executors: add wifi executor 2020-12-02 18:29:05 -07:00
Ariadne Conill
cef4fafdb9
Merge pull request #129 from BarbarossaTM/feature/dhcp-config
Rename dhcp related interface options to dhcp-* and allow interface specific config file
2020-12-02 11:57:19 -07:00
Ariadne Conill
3d47b34d7a
Merge pull request #133 from ifupdown-ng/feature/smart-stanza-merging
improve stanza merging by rewriting address properties as CIDR
2020-12-02 11:50:15 -07:00
Ariadne Conill
b10094eae7 tests: ifquery: add tests to verify stanza merging works as expected 2020-12-02 11:42:45 -07:00
Ariadne Conill
c4d9d6fd06 interface: add lif_interface_finalize() which rewrites addresses as CIDR when an interface stanza ends 2020-12-02 11:38:39 -07:00
Ariadne Conill
0dd7756df2 tests: add fixture illustrating how smart stanza merging should work 2020-12-02 11:17:41 -07:00
Ariadne Conill
43159fec82
Merge pull request #132 from ifupdown-ng/bugfix/ipv6-default-netmask
fix ipv6 default netmask
2020-12-02 11:07:12 -07:00
Ariadne Conill
259851a829 interface: fix default netmask size for AF_INET6 addresses (closes #130) 2020-12-02 11:05:40 -07:00
Ariadne Conill
e1fb4c6087 tests: ifquery: add tests for checking the default netmasks 2020-12-02 10:59:40 -07:00
Maximilian Wilhelm
ff8e5d392c dhcp: Fix test
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-11-30 22:38:55 +01:00
Maximilian Wilhelm
b21cb37df0 dhcp: Pass given config file to dhclient, if present
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-11-30 22:38:32 +01:00
Maximilian Wilhelm
aada42795c DHCP: Rename options to dhcp-*
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-11-30 22:36:28 +01:00
Ariadne Conill
6730db5468
Merge pull request #127 from ifupdown-ng/bugfix/multicall-getopt
multicall: fix inappropriate use of getopt in the multicall stub
2020-11-19 03:33:19 -07:00
Ariadne Conill
7911944633 tests: add getopt regression test for multicall stub 2020-11-19 03:28:51 -07:00
Ariadne Conill
46bb0565fa multicall: do not call getopt_long() inside the stub applet 2020-11-19 03:18:23 -07:00
Maximilian Wilhelm
9715b41c28
Merge pull request #125 from ifupdown-ng/feature/ifparse
YAML output support
2020-11-14 19:10:55 +01:00
Ariadne Conill
89b96a5108 doc: ifparse: chase --yaml-raw -> -F yaml-raw change 2020-11-13 21:53:41 -07:00
Ariadne Conill
545c3870ab ifparse: use -F to select output format instead of individual output options 2020-11-13 21:53:36 -07:00
Ariadne Conill
d2a75c7bb4 yaml-writer: constify 2020-11-13 21:35:12 -07:00
Ariadne Conill
dd8064142c yaml-writer: add ability to enable/disable type annotations 2020-11-13 21:31:48 -07:00
Ariadne Conill
3bf406bf92 doc: add ifparse manual page 2020-11-12 02:28:02 -07:00
Ariadne Conill
c5520f95dd ifparse: fix usage example 2020-11-12 02:22:25 -07:00
Ariadne Conill
7ca5305063 makefile: enabling YAML costs 2KB 2020-11-11 03:25:54 -07:00
Ariadne Conill
8097d5015f ifparse: if interface marked as auto, add "auto: !!bool true" to YAML output 2020-11-11 03:13:32 -07:00
Ariadne Conill
dbfebbff87 YAML: add support for booleans 2020-11-11 03:11:48 -07:00
Ariadne Conill
5010dce3d5 ifparse: rename --yaml to --yaml-raw 2020-11-11 03:10:50 -07:00
Ariadne Conill
95f0ea4895 yaml writer: implement type hinting 2020-11-11 02:51:24 -07:00
Ariadne Conill
2ec3a39c89 ifparse: add --yaml flag 2020-11-11 02:50:29 -07:00
Ariadne Conill
cfb43e9573 add really basic yaml document graph building and writing functionality 2020-11-11 02:46:28 -07:00
Ariadne Conill
fe1664d311 list: fix LIF_LIST_FOREACH_SAFE() iteration on empty lists 2020-11-11 02:18:04 -07:00
Ariadne Conill
ec6077f26f build: add CONFIG_YAML 2020-11-10 20:08:35 -07:00
Ariadne Conill
2a9ca329ff ifparse: add skeleton 2020-11-10 20:02:07 -07:00
Ariadne Conill
f6ad65d99e cmd: split out /e/n/i pretty printer from ifquery.c 2020-11-10 19:42:09 -07:00
Ariadne Conill
d250ab213c
Merge pull request #122 from ifupdown-ng/feature/configure-auto-executor-selection
Feature/configure auto executor selection
2020-11-04 21:53:42 -07:00
Ariadne Conill
f5fc3a3c1a ADMIN-GUIDE: document auto_executor_selection 2020-11-04 21:52:27 -07:00
Maximilian Wilhelm
748a226786
Merge pull request #124 from ifupdown-ng/feature/ifquery-virtual
Feature/ifquery virtual
2020-11-04 20:40:56 +01:00
Ariadne Conill
c8dad76fe1 tests: ifquery: add testcases for ifquery -U 2020-11-04 12:23:14 -07:00
Ariadne Conill
c7eeef27a4 ifquery.8: document -U/--allow-undefined 2020-11-04 11:59:19 -07:00
Ariadne Conill
52f739c943 ifquery: implement support for -U/--allow-undefined 2020-11-04 11:55:36 -07:00
Ariadne Conill
71d832cceb
Merge pull request #123 from ifupdown-ng/chore/narrow-dhcp-hostname-configuration
interface: learn hostname from uname(2) only if an interface requests…
2020-11-04 11:47:24 -07:00
Ariadne Conill
632af7b716 interface: learn hostname from uname(2) only if an interface requests the dhcp executor
Closes #74.
2020-11-04 11:46:18 -07:00
Ariadne Conill
af25cedf33 interfaces: note automatic executor selection algorithm here too (closes #117) 2020-11-04 11:31:08 -07:00
Ariadne Conill
c292297396 interfaces: s/option/executor/ 2020-11-04 11:27:41 -07:00
Ariadne Conill
5667b6adec ifupdown-ng.5: document auto_executor_selection and selection algorithm 2020-11-04 11:24:20 -07:00
Ariadne Conill
2d2e73b8ac example config: document auto_executor_selection setting 2020-11-04 11:21:42 -07:00
Ariadne Conill
068f464e4c interface-file: if auto_executor_selection is disabled, don't guess what executors should be used 2020-11-04 11:18:40 -07:00
Ariadne Conill
0050995b64 config-file: add support for auto_executor_selection setting 2020-11-04 11:16:47 -07:00
Ariadne Conill
20d9e3fe91 executors: drop set -e, closes #121 2020-11-02 08:53:30 -07:00
Maximilian Wilhelm
b76981e832
Merge pull request #120 from ifupdown-ng/chore/document-ifstate
docs: document ifstate
2020-10-26 16:31:50 +01:00
Ariadne Conill
8cae485ca7 docs: document ifstate 2020-10-24 09:00:26 -06:00
Ariadne Conill
6cadc44183 CI: install scdoc 2020-10-24 08:43:51 -06:00
Ariadne Conill
da751ce14d CI: enable CI tests for documentation 2020-10-24 08:42:26 -06:00
Ariadne Conill
dcbb3be15b doc: ifupdown-ng.conf.scd: escape the asterisk in compat_* 2020-10-24 08:40:49 -06:00
Ariadne Conill
19a5a671eb
Merge pull request #111 from ifupdown-ng/feature/deprecate-brctl
vlan aware bridging
2020-10-22 13:42:00 -08:00
Ariadne Conill
90453f1412
Merge pull request #119 from ifupdown-ng/feature/explicit-guard
guard auto and explicitly configured interfaces from inappropriate deconfiguration
2020-10-21 07:15:03 -08:00
Ariadne Conill
b4f87cbd1e state: explicitly check for explicit keyword when loading from the ifstate file 2020-10-21 09:13:34 -06:00
Ariadne Conill
2a8a72eee7 ifupdown: upgrade dependent interfaces to explicit interfaces if explicitly requested 2020-10-21 08:46:47 -06:00
Ariadne Conill
eb9bebebc6 ifupdown: record explicitly configured interfaces as explicit in ifstate 2020-10-21 08:42:47 -06:00
Ariadne Conill
61097b1db2 ifquery: when querying state, denote presence of the explicit flag 2020-10-21 08:32:29 -06:00
Ariadne Conill
b09d622cfc state: synchronize is_explicit from state records to parsed interface collections 2020-10-21 08:30:35 -06:00
Ariadne Conill
dae7d59864 state: write and restore explicit flag from ifstate 2020-10-21 08:29:54 -06:00
Ariadne Conill
4f7063ba0f state: add lif_state_record::is_explicit 2020-10-21 08:15:52 -06:00
Ariadne Conill
817262fa33 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 08:11:45 -06:00
Ariadne Conill
b201f351ec interface: auto interfaces are always explicit 2020-10-21 08:09:21 -06:00
Ariadne Conill
9bcf914250 interface: add lif_interface::is_explicit 2020-10-21 08:06:59 -06:00
Maximilian Wilhelm
c810cd5817 doc: Add ifupdown-ng.conf(5) man page on global options.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 23:43:17 +02:00
Maximilian Wilhelm
e5d9ee25fc doc: Update bridge man page on vlan-aware options.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 23:25:42 +02:00
Maximilian Wilhelm
02a74985ab bridge: Rework vlan handling
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 21:29:50 +02:00
Maximilian Wilhelm
fb1d3181fe bridge: STP option for iproute2 has to be 0 or 1.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 21:28:10 +02:00
Maximilian Wilhelm
ab7b1f5d24 compat: Fix build failure.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 05:36:45 +02:00
Maximilian Wilhelm
480fc5eecb compat: Only create interface when configured to do so.
Add config option <compat_create_interfaces>:

  Denotes where or not to create interfaces when compat_* settings are
  active and it would be necessary to create an interface to be fully
  compliant.  This could happen when inheriting bridge VLAN settings to
  an interface within a bridges bridge-ports setting but no interface
  stanza is found.  Valid values are 0 and 1, the default is 1.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 05:14:03 +02:00
Maximilian Wilhelm
d86297f29c compat: Add glue for ifupdown2 bridge port VLAN inheritance.
Add config options <compat_ifupdown2_bridge_ports_inherit_vlans>.

  In ifupdown2 <bridge-vids> as well as the <bridge-pvid> set on a bridge
  interface will be inherited by all member ports if not set explicitly.
  When set to 1 ifupdown-ng behaves the same way and will internally copy
  both options from the bridge member ports if they are not set on the
  member port. Valid values are 0 and 1, the default is 1.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 04:58:23 +02:00
Maximilian Wilhelm
56beefdd28 ifquery: Apply compatibility glue, too.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 04:57:24 +02:00
Maximilian Wilhelm
a5761afd70 bridge: Remove fall back to IF_REQUIRES
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 03:28:20 +02:00
Ariadne Conill
05a3b1539b bridge: add support for bridge-vids (with inheritance), bridge-access and bridge-pvid 2020-10-18 03:28:20 +02:00
Ariadne Conill
03528b01ad bridge: remove hack for alpine vlan scripts (not relevant to us) 2020-10-18 03:28:20 +02:00
Ariadne Conill
3d743f512f bridge: add support for bridge-vlan-aware in iproute2 mode 2020-10-18 03:28:20 +02:00
Ariadne Conill
6c5d856ac4 bridge: remove support for gcint (noop in modern kernels), use iproute2 commands for all bridge configuration if present 2020-10-18 03:28:20 +02:00
Ariadne Conill
36eb6e3377 bridge: check if iproute2 is available and use it to configure bridge options 2020-10-18 03:28:20 +02:00
Ariadne Conill
e7ee26ac19 bridge: use iproute commands to create and assign bridge ports 2020-10-18 03:28:20 +02:00
Maximilian Wilhelm
80a590ca33
Merge pull request #114 from BarbarossaTM/feature/compatiblity-layer
Introduce compatibility layer framework
2020-10-18 03:27:46 +02:00
Maximilian Wilhelm
d96f579d7f Introduce an compatibility layer which is empty for now.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-18 03:26:40 +02:00
Ariadne Conill
509b75bc73 openrc: use --auto selector when determining which interfaces to start (closes #112) 2020-10-17 05:20:10 -06:00
Ariadne Conill
3e6129907c ifupdown-ng 0.10.0. 2020-10-15 16:03:44 -06:00
Ariadne Conill
e6de8465de
Merge pull request #110 from ifupdown-ng/feature/normalize-dhcp-hostname
dhcp hostname normalization
2020-10-14 12:50:29 -06:00
Ariadne Conill
47f74997a7 admin guide: document use_hostname_for_dhcp. 2020-10-14 05:23:22 -06:00
Ariadne Conill
4aa2749737 add a config option to disable automatically setting the dhcp hostname 2020-10-14 05:21:15 -06:00
Ariadne Conill
a210cf6a66 interface: set a default hostname property 2020-10-14 05:11:12 -06:00
Ariadne Conill
aec7dad1c1 interface-file: add special handler for hostname keyword 2020-10-14 05:04:06 -06:00
Ariadne Conill
086eca2b4e
Merge pull request #109 from ifupdown-ng/feature/include-loop-detection
refactor config file management
2020-10-14 04:54:47 -06:00
Ariadne Conill
f6fe06298f doc: interfaces: document source and source-directory options 2020-10-14 04:29:25 -06:00
Ariadne Conill
ee5e8b5702 interface-file: implement source-directory 2020-10-14 04:26:26 -06:00
Ariadne Conill
5035d2e160 interface-file: handle_generic(): drop unnecessary unused declaration for state 2020-10-14 04:12:59 -06:00
Ariadne Conill
8d4c7461af interface-file: break loops using lif_interface_file_parse_state.loaded dictionary 2020-10-14 04:07:14 -06:00
Ariadne Conill
0df3e03b68 interface-file: fully encapsulate cur_iface into lif_interface_file_parse_state 2020-10-14 04:00:39 -06:00
Ariadne Conill
dcb6ef97ef cmd: use lif_interface_file_parse_state structure as needed 2020-10-14 03:50:58 -06:00
Ariadne Conill
3bcfe91e84 interface-file: simplify state management a bit 2020-10-14 03:48:20 -06:00
Ariadne Conill
d76c2df460
Merge pull request #108 from ifupdown-ng/feature/openrc-init-script
openrc init script
2020-10-14 03:04:18 -06:00
Ariadne Conill
ef3be06b38 openrc: add networking.confd describing the config options 2020-10-14 02:52:59 -06:00
Ariadne Conill
1668d17c6b openrc: use -S to consistently define the state database location 2020-10-14 02:48:51 -06:00
Ariadne Conill
fcea23dbcd openrc: use ifquery -r to determine which interfaces to stop 2020-10-14 02:47:52 -06:00
Ariadne Conill
311ad74792 doc: document ifquery -r 2020-10-14 02:45:16 -06:00
Ariadne Conill
e300867d26 ifquery: add ifquery --running
this is like ifquery --state except it dumps only the running interface names
2020-10-14 02:44:00 -06:00
Ariadne Conill
89d84ec475 openrc: use ifquery -L for find_ifaces() 2020-10-14 02:38:05 -06:00
Ariadne Conill
3b7181cf28 import networking.initd from alpine 2020-10-14 02:32:05 -06:00
Ariadne Conill
24bfcc1737
Merge pull request #107 from BarbarossaTM/bugfix/seperate-netmask
Always convert netmasks to CIDR when configuring interfaces
2020-10-13 18:41:51 -06:00
Maximilian Wilhelm
de207a5950 lifecyle: Use lif_address_format_cidr() for IF_ADDRESSES
Previously IF_ADDRESSES would not contain a prefix length if the address
  entry in the configuration did not was in CIDR format but had a netmask
  set seperately. This commit fixes that behaviour and computes a CIDR
  netmask if necessary.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 01:47:49 +02:00
Maximilian Wilhelm
365461a6f6 interface: Consider the lif_interface as const in lif_address_format_cidr()
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 01:45:14 +02:00
Maximilian Wilhelm
a72d87df0b dict: lif_dict_find() + lif_dict_find_all() do not touch the dict, so mark it const.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 01:39:31 +02:00
Maximilian Wilhelm
02324bebd5 Move handling of address/netmask pairs from ifquery into library.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 01:24:33 +02:00
Ariadne Conill
17a410b87c
Merge pull request #106 from BarbarossaTM/doc/update
Update build documentation as well as admin guide
2020-10-13 18:14:50 -04:00
Maximilian Wilhelm
98ba7f46cf dist: Point out valid values and defaults in example config.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 00:12:47 +02:00
Maximilian Wilhelm
68c51a79e7 doc: Add /etc/network/ifupdown-ng.conf to admin guide.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 00:12:47 +02:00
Maximilian Wilhelm
f707c5c1ef doc: Update build section in README
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 00:12:24 +02:00
Maximilian Wilhelm
6423a284b8 doc: Update build section in README
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-14 00:11:37 +02:00
Ariadne Conill
172daa16a0
Merge pull request #85 from ifupdown-ng/feature/dependency-loop-breaking
lifecycle: add lif_interface.is_pending to break dependency cycles
2020-10-10 22:42:34 -04:00
Ariadne Conill
a1828c688b
Merge pull request #104 from BarbarossaTM/chore/executor-cleanup
bond executor: grep for options only once
2020-10-10 22:42:13 -04:00
Maximilian Wilhelm
2d32b1577c bond executor: grep for options only once
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-11 03:28:23 +02:00
Ariadne Conill
4c354ebf35 ifquery: implement more robust loop breaking strategy 2020-10-08 02:17:13 -06:00
Ariadne Conill
959617df88 tests: add dependency loop breaking test for ifdown 2020-10-08 02:09:30 -06:00
Ariadne Conill
0cade539f8 tests: ifup: add dependency loop breaking test 2020-10-08 02:05:43 -06:00
Ariadne Conill
122a54377d lifecycle: break dependency cycles when calculating the full dependency graph 2020-10-08 02:01:46 -06:00
Ariadne Conill
b2e657cdf0 tests: add dependency loop fixture 2020-10-08 02:00:18 -06:00
Ariadne Conill
175f002b3e lifecycle: add lif_interface.is_pending to break dependency cycles 2020-10-08 01:49:44 -06:00
Maximilian Wilhelm
953b2274f7
Merge pull request #102 from BarbarossaTM/docs/fixes
docs: Fix format of VXLAN examples
2020-10-06 21:02:11 +02:00
Maximilian Wilhelm
5db2f29891 docs: Fix format of VXLAN examples.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 21:00:14 +02:00
Maximilian Wilhelm
a67d518ea9
Merge pull request #101 from BarbarossaTM/feature/batman-options
Generalize B.A.T.M.A.N. .adv. option handling
2020-10-06 20:22:19 +02:00
Ariadne Conill
5b6cb5a128
Merge pull request #100 from BarbarossaTM/chore/addresses-and-gateways-env
Expose all adresses and gateways as environment variables
2020-10-05 23:46:15 -04:00
Maximilian Wilhelm
13a8daf96e batman executor: Work around excution order limitations for now.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 05:17:48 +02:00
Maximilian Wilhelm
27a7201b45 batman executor: Generalize option handling.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 05:05:28 +02:00
Maximilian Wilhelm
332ea7c7d3 Stick to upstream naming of hop-penalty instead of interface penalty for hardifs.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 05:05:28 +02:00
Maximilian Wilhelm
59290415a0 batman executor: Allow settings the B.A.T.M.A.N. adv. routing algorithm
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 05:05:28 +02:00
Maximilian Wilhelm
5d6c7732ed static executor: Ignore errors while removing addresses
When having multiple addresses set from the same prefix they might/will(?)
  be configured as 'secondary' and implicitly removed when removing the
  non-secondary address. This leads ip complaining about not being able
  to remove the secondaries as they are already gone. So we ignore errors
  while deconfiguring addresses as they liked occur when removing a
  vanish address anyway.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:50:02 +02:00
Maximilian Wilhelm
5302bee850 lifecycle: Don't leak allocated memory.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:50:02 +02:00
Maximilian Wilhelm
1a2298a759 lifecycle: Use snprintf() in favor of sprintf()
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:50:02 +02:00
Maximilian Wilhelm
99b0d67b8e lifecycle: Remove debugging statement.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:50:02 +02:00
Maximilian Wilhelm
4a8230f916 tests: Update static executor test to reflect latest change.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:49:46 +02:00
Maximilian Wilhelm
5557804af9 static executor: Update executor to use env vars.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:43:12 +02:00
Maximilian Wilhelm
44be0c0721 Add myself to copyright notice in version information.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:43:12 +02:00
Maximilian Wilhelm
8a58c0ae6d lifecycle: Expose all adresses and gateways via the environment.
Gather all IP addresses and gateways and expose them as IF_ADDRESSES and
  IF_GATEWAYS environment variables to executors. This eliminates the need
  to run ifquery from execuctors which would have to parse the config file
  again on every run.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:43:12 +02:00
Ariadne Conill
6f588a01d5
Merge pull request #92 from BarbarossaTM/doc/manpage-update
Man pages for B.A.T.M.A.N. adv., bridges, ethtool, VRFs
2020-10-05 22:06:43 -04:00
Ariadne Conill
df6835bd62
Merge pull request #98 from BarbarossaTM/feature/pointopoint
static executor: Add support for IPv4 'pointopoint' addresses.
2020-10-05 22:04:58 -04:00
Maximilian Wilhelm
6dc43a2bb4 Let's use "point-to-point" and map "pointopoint" to it.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:03:27 +02:00
Maximilian Wilhelm
9832865539 Makefile: Order manpages by section number
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:02:24 +02:00
Maximilian Wilhelm
63a5503185 doc: Add a man page for PPP dial-up interfaces
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:01:52 +02:00
Maximilian Wilhelm
87fa1760be doc: Add a man page for Wireguard VPN tunnel interfaces
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:01:43 +02:00
Maximilian Wilhelm
584f0634ed Add new interfaces-*(5) man pages to Makefile
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 04:01:22 +02:00
Maximilian Wilhelm
596dd8165b doc: Reference CLI tool man pages in interfaces(5)
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 03:56:20 +02:00
Maximilian Wilhelm
05711db6ce doc: Add a man page for bridge interfaces
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 03:56:09 +02:00
Maximilian Wilhelm
adf0f9dd46 doc: Add a man page for B.A.T.M.A.N. adv. interfaces
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 03:55:55 +02:00
Maximilian Wilhelm
3d1d384a6b doc: Add man page for VRF related parameters
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 03:55:09 +02:00
Maximilian Wilhelm
48c87e34c1 tests: Use tabs not spaces.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-06 03:35:42 +02:00
Ariadne Conill
37299e312f
Merge pull request #87 from BarbarossaTM/feature/veth
Add support for veth interface pairs
2020-10-05 14:34:39 -06:00
Ariadne Conill
a66e8465d1
Merge pull request #99 from BarbarossaTM/feature/bonding
Add support for bonding / LAGs.
2020-10-05 14:27:07 -06:00
Maximilian Wilhelm
9ee3a874d4 Add support for bonding / LAGs.
* Add a bond executor
  * Add mappings from ifupdown1/2
  * Add a detailed man page
  * Remove legacy compatiblity glue for setups with 'requires' only

  The current implementation has to work around the fact that member interfaces
  will be already up then the bond is created. This is simply done by downing
  them, adding them to the bundle and upping them again. This can possible be
  done in a nicer way after revisiting the ordering of plugin execution (#12).

  Closes #91

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-04 01:35:41 +02:00
Maximilian Wilhelm
67163c6561 static executor: Add support for IPv4 'pointopoint' addresses.
Closes #96

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-03 20:38:40 +02:00
Ariadne Conill
cc06712611
Merge pull request #94 from ifupdown-ng/bugfix/hetzner-ipv6-default-route-is-link-local
static: always associate default routes with $IFACE
2020-10-02 15:46:03 -06:00
Ariadne Conill
74b6f9487c static: always associate default routes with $IFACE
Hetzner uses link-local addressing for their default IPv6 route,
accordingly we should specify the device a route is associated with
so that it will use the correct interface.

Thanks to Devin Brown for reporting this issue.
2020-10-02 15:44:46 -06:00
Maximilian Wilhelm
68e08cae7e
Merge pull request #89 from BarbarossaTM/feature/vxlan
Add support for VXLAN interface
2020-10-02 22:36:22 +02:00
Maximilian Wilhelm
7cf353dd22 doc: Polish VXLAN related documentation.
Thanks-to: Blake W.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 22:29:01 +02:00
Maximilian Wilhelm
704b2feecb doc: Move VXLAN documentation into own man page.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 21:24:21 +02:00
Maximilian Wilhelm
0b23f5bc86
Merge pull request #88 from BarbarossaTM/feature/debian-packaging
Add files required for Debian packaging
2020-10-02 03:09:37 +02:00
Maximilian Wilhelm
98ac007ffe
Merge pull request #90 from BarbarossaTM/chore/link-executor
Clean up link executor
2020-10-02 03:09:14 +02:00
Maximilian Wilhelm
12b0872349 vxlan executor: Fix typo and remove code copied from link executor.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 03:06:22 +02:00
Maximilian Wilhelm
1f1d4f5bad man: Add documentation for VXLAN parameters.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 03:05:30 +02:00
Maximilian Wilhelm
fef7c55270 link exectutor: Don't re-create dummy interface when present.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 02:29:39 +02:00
Maximilian Wilhelm
c6faf452b9 link executor: Don't complain about a vanished interface when downing it
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 02:29:04 +02:00
Maximilian Wilhelm
ee582ac9b0 vxlan: Add tests
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 02:25:55 +02:00
Maximilian Wilhelm
3f67b2137d Add VXLAN support
Add a vxlan exectutor as well as mappings from ifupdown2 parameters to ours.

  Closes #75

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 02:20:10 +02:00
Maximilian Wilhelm
bd8ad91de6 Add files required for Debian packaging
* Systemd unit file for networking.service
 * Small wrapper script called by networking.service
 * Defaults file for networking.service

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-10-02 00:55:34 +02:00
Maximilian Wilhelm
3035627c93 link executor: Add dependency between veth pairs
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-25 22:07:10 +02:00
Maximilian Wilhelm
f708bb1465 link executor: Add support for veth interfaces
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-25 22:04:53 +02:00
Maximilian Wilhelm
044d8307f9
Merge pull request #86 from ifupdown-ng/feature/ifquery-skip-depgraph-for-iface-queries
ifquery: don't build the dependency tree when querying for an interface's properties
2020-09-25 20:59:54 +02:00
Ariadne Conill
5615c30fd0 ifquery: don't build the dependency tree when querying for an interface's properties 2020-09-25 08:43:26 -06:00
Ariadne Conill
9c76528fe1
Merge pull request #84 from BarbarossaTM/feature/reduce-failure.domain
ifupdown: Don't configure errornous interfaces.
2020-09-24 18:34:29 -06:00
Maximilian Wilhelm
88f25e41c6 ifupdown: More specific error message.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-25 02:28:05 +02:00
Maximilian Wilhelm
a3d11ded43 ifupdown: Be consistent in error messages.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-25 02:26:21 +02:00
Maximilian Wilhelm
8dc2295006 ifupdown: Don't configure errornous interfaces.
Do not configure interfaces which have a configuration know to be broken.
  Currently this only happens when using "inherit" to an non-existing iface
  or to an iface and not a template, and having allow_any_iface_as_template
  set to 0.

  In those cases broken ifaces will not be configured unless ifup/ifdown is
  invoked with --force.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-25 02:15:16 +02:00
Ariadne Conill
fafce2f262 config-file: add missing default for implicit_template_conversion 2020-09-24 15:36:22 -06:00
Ariadne Conill
d177bfa553
Merge pull request #83 from ifupdown-ng/feature/alias-keywords-for-iface
inherit/template system improvements
2020-09-24 15:24:06 -06:00
Ariadne Conill
a55ef85776 doc: interfaces: describe template inheritance 2020-09-24 15:19:53 -06:00
Ariadne Conill
60d0ed34b8 interface: make interface-to-template conversion a config option (defaulting to enabled) 2020-09-23 19:07:52 -06:00
Ariadne Conill
243a9b92ce ifupdown: skip over template interfaces at warning time instead of letting lif_lifecycle_run() fail 2020-09-23 11:52:22 -06:00
Ariadne Conill
f224c04804 interface: explicitly convert any inherited interface into a template 2020-09-23 11:47:13 -06:00
Ariadne Conill
f9683c2242 ifupdown: complain to the user if they try to bring up or tear down a template interface 2020-09-23 11:42:09 -06:00
Ariadne Conill
580f054d7e lifecycle: do not allow lif_lifecycle_run() on a template 2020-09-23 11:39:45 -06:00
Ariadne Conill
94f00b2fdc interface-file: do not allow templates to be set as automatic 2020-09-23 11:38:29 -06:00
Ariadne Conill
41a71173d1 ifquery: display template interfaces as templates, not as iface 2020-09-23 11:35:27 -06:00
Ariadne Conill
5c5c316ebf interface: add config setting to restrict inheritance to template interfaces 2020-09-23 11:34:35 -06:00
Ariadne Conill
a3987b46c8 interface-file: mark interface as template if template keyword is used 2020-09-23 11:29:01 -06:00
Ariadne Conill
eb70e48ece interface-file: add interface and template keywords as alias for iface 2020-09-23 11:26:28 -06:00
Maximilian Wilhelm
850b20d5a8
Merge pull request #81 from ifupdown-ng/feature/ethtool
Ethtool support
2020-09-23 01:48:17 +02:00
Maximilian Wilhelm
0a0252d218 ethtool: Fix tests
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-23 01:43:25 +02:00
Maximilian Wilhelm
2e81234df1 ethtool: Call 'env' instead of 'set' to get environment
'set' will output environment variables with ' around them which will
  confuse ethtool.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-23 01:24:56 +02:00
Ariadne Conill
abc3b13fd7 Merge remote-tracking branch 'origin/master' into feature/ethtool 2020-09-21 10:23:04 -06:00
Ariadne Conill
566732321d tests: ethtool: add tests for coalesce settings 2020-09-21 10:21:43 -06:00
Ariadne Conill
3cb635b443 ethtool: set coalesce settings from ethtool-coalesce- term namespace 2020-09-21 10:06:58 -06:00
Ariadne Conill
f96cb273a0 interface-file: remap hardware-irq-coalesce- namespace to ethtool-coalesce- namespace 2020-09-21 09:56:48 -06:00
Ariadne Conill
a69c81b6be tests: ethtool: add dma ring tests 2020-09-21 09:38:39 -06:00
Ariadne Conill
3e7125aca0 ethtool: add support for the ethtool-dma namespace 2020-09-21 09:32:37 -06:00
Ariadne Conill
2078f63e62 interface-file: remap debian hardware DMA ring terms to ethtool-dma namespace 2020-09-21 09:30:12 -06:00
Ariadne Conill
c19def3764 tests: ethtool: add offload tests 2020-09-21 09:18:08 -06:00
Ariadne Conill
e8a2aab7e1 tests: ethtool: add tests for pause parameters 2020-09-21 09:10:40 -06:00
Ariadne Conill
6ae3414329 ethtool: fix up gather_params() calls a bit 2020-09-21 09:08:17 -06:00
Ariadne Conill
4e78d7e5d5 ethtool: pause parameters are IF_ETHTOOL_PAUSE_FOO, not IF_ETHTOOL_ETHERNET_PAUSE_FOO. 2020-09-21 08:59:13 -06:00
Ariadne Conill
0a58383ed8 tests: ethtool: add tests for basic link settings, wol and autoneg 2020-09-21 08:55:20 -06:00
Ariadne Conill
e4b4d8b70a ethtool: add pause and offload settings support 2020-09-21 08:39:25 -06:00
Ariadne Conill
72277e51e2 ethtool: add support for basic ethernet link settings 2020-09-21 08:31:20 -06:00
Ariadne Conill
b0480a3343 ethtool: add pre-up settings to the executor 2020-09-21 08:20:43 -06:00
Ariadne Conill
f7e7b4be85
Merge pull request #80 from ifupdown-ng/chore/batman-executor-scripts-opt
build: add batman executor to EXECUTOR_SCRIPTS_OPT
2020-09-21 08:06:48 -06:00
Ariadne Conill
eac42c104b build: add batman executor to EXECUTOR_SCRIPTS_OPT 2020-09-21 08:05:18 -06:00
Ariadne Conill
63ec8d4b3b begin ethtool executor-script (as part of EXECUTOR_SCRIPTS_OPT). 2020-09-21 08:04:10 -06:00
Ariadne Conill
a8fcf5502f add a missing translation 2020-09-19 09:20:14 -06:00
Ariadne Conill
27e11adac6 interface-file: remap properties used by both debian and ifupdown2 ethtool addons to the ethtool- namespace 2020-09-19 09:20:14 -06:00
Ariadne Conill
7bacb2a0f7
Merge pull request #79 from ifupdown-ng/chore/docs-no-multihoming
admin guide: s/multi-homing/multiple addresses/
2020-09-19 09:19:02 -06:00
Ariadne Conill
f35242e5c7 admin guide: s/multi-homing/multiple addresses/ 2020-09-19 09:14:06 -06:00
Ariadne Conill
f10faf953c
Merge pull request #78 from ifupdown-ng/feature/preferences
config file support
2020-09-19 07:16:38 -06:00
Ariadne Conill
d814aa754f config parser: explicitly skip over comments 2020-09-15 05:48:54 -06:00
Ariadne Conill
27a383cfa7 tokenizer: split out equals-as-delimiter tokenizer to its own function 2020-09-14 17:34:44 -06:00
Ariadne Conill
4b30dc4573 lifecycle: if lif_config.allow_addon_scripts is disabled, don't run addon scripts 2020-09-14 17:26:45 -06:00
Ariadne Conill
c1c9115e5d tokenize: fix tokenization of equals 2020-09-14 17:24:44 -06:00
Ariadne Conill
b4e35c442e multicall: load config file data as early as possible 2020-09-14 17:20:20 -06:00
Ariadne Conill
5b27d8408c config file: don't print warning if config file cannot be opened 2020-09-14 17:17:03 -06:00
Ariadne Conill
02d044c391 libifupdown: add config file loader 2020-09-14 17:15:18 -06:00
Ariadne Conill
fafa5ed7f9 libifupdown: add abstract config parser implementation 2020-09-14 17:11:59 -06:00
Ariadne Conill
537a56414a tokenize: consider = to be equivalent to whitespace 2020-09-14 16:50:31 -06:00
Ariadne Conill
f9d0fbb4c3 ifupdown-ng 0.9.0. 2020-09-11 02:46:55 -06:00
Ariadne Conill
67da195f9f
Merge pull request #62 from BarbarossaTM/batman
WIP: Add executor for managing B.A.T.M.A.N. adv. interfaces
2020-09-11 02:45:11 -06:00
Ariadne Conill
aba546cf8e Merge branch 'feature/udhcpc-hostname-script-client' into master 2020-09-11 02:43:58 -06:00
Ariadne Conill
b4b0889c7a tests: add test for hostname subshell 2020-09-11 02:43:32 -06:00
Ariadne Conill
6048d65d87
Merge pull request #73 from ifupdown-ng/feature/udhcpc-hostname-script-client
dhcp: fixes for udhcpc
2020-09-11 02:40:29 -06:00
Ariadne Conill
5d4e3699b2 dhcp: evaluate IF_HOSTNAME for shell fragments 2020-09-11 02:33:54 -06:00
Ariadne Conill
a7e27ffa2c dhcp: support IF_HOSTNAME, IF_SCRIPT and IF_CLIENT for udhcpc 2020-09-11 02:31:22 -06:00
Maximilian Wilhelm
e15f3ffbaa
Merge pull request #72 from ifupdown-ng/feature/depgraph-rdepends
dependency graph: reverse-dependency coloring
2020-09-10 18:06:29 +02:00
Ariadne Conill
e754e836af tests: add tests for auto interface skipping 2020-09-09 20:46:50 -06:00
Ariadne Conill
ceb82f4fd2 ifupdown: only display skip messages in verbose mode 2020-09-09 19:38:29 -06:00
Ariadne Conill
a6e022ad99 lifecycle: re-sort the interface collection by hand 2020-09-09 19:21:41 -06:00
Ariadne Conill
258e2e8a52 lifecycle: return the maximum tree depth for the dependency tree when counting depth 2020-09-09 18:12:13 -06:00
Ariadne Conill
8c8727e30f lifecycle: drop redundant lif_lifecycle_query_dependents().
since we now precompile the full dependency tree, including depths,
we no longer need to re-query each leaf for dependency information.
2020-09-09 17:40:57 -06:00
Ariadne Conill
2569503afa lifecycle: add lif_lifecycle_count_rdepends(), which calculates reverse dependency depth 2020-09-09 17:39:26 -06:00
Ariadne Conill
a5eebda391 ifquery: include reverse dependency count in dot output 2020-09-09 17:14:58 -06:00
Ariadne Conill
67dce280d7 interface: add lif_interface.rdepends_count 2020-09-09 17:13:11 -06:00
Ariadne Conill
04a65c5c38
Merge pull request #69 from BarbarossaTM/feature/create-destroy
WIP: Update executors to implement create/destroy phase
2020-09-09 16:53:20 -06:00
Maximilian Wilhelm
0674a70c35 batman executor: Fix typo
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-10 00:50:24 +02:00
Maximilian Wilhelm
e02e495257 bridge executor: Add depend phase
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-10 00:49:32 +02:00
Maximilian Wilhelm
d9e78e14fb tests: Update tests to reflect create/destroy phase.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-10 00:49:04 +02:00
Maximilian Wilhelm
af94d760d0 Update executors to reflect new create/destroy phase.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-10 00:48:47 +02:00
Maximilian Wilhelm
35e03475e4 batman executor: Use create/destroy phases
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-10 00:40:57 +02:00
Ariadne Conill
e6c6f49143 lifecycle: remove redundant lif_state_upsert() call
this will be handled by lif_state_ref_if(), which also updates the
refcounting.
2020-09-09 16:01:04 -06:00
Ariadne Conill
be9857f66f
Merge pull request #70 from ifupdown-ng/feature/iface-executor-tracing
libifupdown: add interface being run for each executor
2020-09-09 15:52:06 -06:00
Ariadne Conill
5a0b53cd73 libifupdown: add interface being run for each executor 2020-09-09 15:50:16 -06:00
Ariadne Conill
bdc66bcba9
Merge pull request #67 from ifupdown-ng/feature/create-destroy
create/destroy phases for interfaces
2020-09-09 14:41:52 -06:00
Ariadne Conill
ce6954d628 ifupdown-executor man page: add create/destroy phases 2020-09-09 14:39:40 -06:00
Ariadne Conill
00da19a381 lifecycle: add create/destroy phases 2020-09-09 14:36:07 -06:00
Ariadne Conill
277ecaf78a
Merge pull request #66 from ifupdown-ng/feature/refcounting
refcounting
2020-09-09 14:27:18 -06:00
Ariadne Conill
885126174d ifupdown: consistently update the state file when exiting 2020-09-09 14:18:16 -06:00
Ariadne Conill
6999a125c8 state: include limits.h 2020-09-09 13:27:34 -06:00
Ariadne Conill
c021b9420f state: ensure rc is set to 1 if strtoul() fails 2020-09-09 11:56:19 -06:00
Ariadne Conill
910985cd22 tests: supply --force where appropriate 2020-09-08 19:25:50 -06:00
Ariadne Conill
3b178131bd state: sync refcounts using mapped interface name, not physical interface name 2020-09-08 19:25:00 -06:00
Ariadne Conill
b514d31c26 ifupdown: implement --force 2020-09-08 19:17:00 -06:00
Ariadne Conill
efe9b60e37 ifupdown: skip interface config/deconfig based on refcounts 2020-09-08 19:10:20 -06:00
Ariadne Conill
8de83fdd9a tests: add tests for deferred interface teardown (refcounting) 2020-09-08 16:02:24 -06:00
Ariadne Conill
f139bc2416 lifecycle: consider an interface eligible for takedown if it has only one dependent (itself) 2020-09-08 15:50:32 -06:00
Ariadne Conill
ca07082ff4 state: fix parsing interface names in ifstate files 2020-09-08 15:37:35 -06:00
Ariadne Conill
6603ff1000 lifecycle: use refcounting to defer interface teardown 2020-09-08 15:28:36 -06:00
Ariadne Conill
288976f015 libifupdown: introduce lif_state_ref_if() and lif_state_unref_if(). 2020-09-08 15:05:02 -06:00
Ariadne Conill
865bf7cac6 state: plug memory leak when upserting 2020-09-08 15:02:39 -06:00
Ariadne Conill
8e4eb5d00c state: migrate to lif_state_record from a bare dictionary containing the mapped ifs 2020-09-08 14:59:58 -06:00
Ariadne Conill
923b96fab8 state: use lif_next_token() when parsing ifstate files 2020-09-08 14:46:07 -06:00
Ariadne Conill
3b10494b40 change lif_interface.is_up to lif_interface.refcount 2020-09-08 14:41:25 -06:00
Ariadne Conill
30fa4c4a2e
Merge pull request #65 from ifupdown-ng/chore/constify
lifecycle: constify lif_interface access in several functions
2020-09-07 13:42:50 -06:00
Ariadne Conill
ae1dc41c88 lifecycle: constify lif_interface access in several functions 2020-09-07 13:41:11 -06:00
Maximilian Wilhelm
017a12760c batman executor: Only create/delete iface if (not) present.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-05 01:52:43 +02:00
Maximilian Wilhelm
be6ce3c319 Add executor for managing B.A.T.M.A.N. adv. interfaces
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-05 00:35:04 +02:00
Maximilian Wilhelm
9784392a68
Merge pull request #61 from BarbarossaTM/link
Refactor management of vlans and dummy interfaces
2020-09-05 00:29:31 +02:00
Maximilian Wilhelm
32b7dda832 tests: Add test for dummy link creation.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-05 00:28:12 +02:00
Maximilian Wilhelm
4a05010539 tests: Fix vlan_explicit_depend_body.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-05 00:24:40 +02:00
Maximilian Wilhelm
aa3e94acf8 link executor: Make link deletion MOCKable again.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-05 00:18:43 +02:00
Maximilian Wilhelm
d24b4ab3e6 link executor: Make dummy creation MOCKable, too
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-05 00:15:26 +02:00
Maximilian Wilhelm
37a7d8f097 link executor: Make vlan disposal MOCKable again
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-05 00:15:01 +02:00
Maximilian Wilhelm
58f010fe91 tests: Reflect latest link executor changes
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-04 23:54:40 +02:00
Maximilian Wilhelm
2e6b3ca1ff link executor: Better error message on conflicting link types.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-04 23:33:27 +02:00
Maximilian Wilhelm
a6b95d495c link executor: Only gather and set IFACE_OPTIONS on up.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-04 23:32:51 +02:00
Maximilian Wilhelm
0e5ec5b260 link executor: Refactor VLAN iface management
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-04 23:21:21 +02:00
Ariadne Conill
a59109cb66
Merge pull request #60 from ifupdown-ng/feature/wireguard
wireguard executor implementation
2020-09-04 12:31:13 -07:00
Ariadne Conill
acb555b75a tests: add wireguard tests 2020-09-04 13:10:57 -06:00
Ariadne Conill
f32481deb9 add wireguard executor script 2020-09-04 12:57:41 -06:00
Maximilian Wilhelm
471549db4b
Merge pull request #56 from BarbarossaTM/alias
link executor: Add support for interface aliases.
2020-09-04 09:20:15 +02:00
Maximilian Wilhelm
6805262a9b link executor: Add support for interface aliases.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-09-04 09:14:45 +02:00
Ariadne Conill
2311316bfe
Merge pull request #55 from ifupdown-ng/feature/udhcpc-opts
add support for udhcpc-opts
2020-09-03 15:15:31 -07:00
Ariadne Conill
7edb29778b tests: add udhcp-opts related tests 2020-09-03 16:11:41 -06:00
Ariadne Conill
2d7668bc01 dhcp executor: add support for udhcpc-opts property 2020-09-03 16:07:27 -06:00
Ariadne Conill
4b32a0787d
Merge pull request #51 from BarbarossaTM/whitespaces
Remove leading whitespaces around interface attribute values.
2020-08-30 15:39:01 -06:00
Ariadne Conill
ab7ff2b206
Merge pull request #54 from BarbarossaTM/dummy
Only create dummy interface when missing and remove them on post-down
2020-08-30 15:38:32 -06:00
Maximilian Wilhelm
e854819e8f link executor: Delete dummy interface in post-down.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-30 22:38:58 +02:00
Maximilian Wilhelm
7d81ceb898 link executor: Only create dummy interface when not present.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-30 22:38:38 +02:00
Maximilian Wilhelm
64b24b02ed Remove leading whitespaces before interface attribute values.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-30 22:18:07 +02:00
Ariadne Conill
375b5d46cb
Merge pull request #52 from BarbarossaTM/scripts
lifecycle: Check if script dir exists before executing run-parts.
2020-08-29 08:38:16 -06:00
Maximilian Wilhelm
570679c5bf lifecycle: Make sure to return true when script dir doesn't exist.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-27 12:10:12 +02:00
Maximilian Wilhelm
61ed18db2c lifecycle: Check if script dir exist before executing run-parts.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-27 01:15:51 +02:00
Maximilian Wilhelm
1f21d2bb45
Merge pull request #50 from BarbarossaTM/link-type
Add support for dummy interfaces
2020-08-27 00:49:47 +02:00
Maximilian Wilhelm
8f3e1f06f1 link executor: Fix indentation.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-27 00:47:51 +02:00
Ariadne Conill
583ff684df
Merge pull request #48 from BarbarossaTM/vrf-fix
static executor: Use vrf-member when setting gateway if present.
2020-08-26 15:50:29 -06:00
Maximilian Wilhelm
e51ce613e6 link executor: Add support for dummy interfaces.
closes #49

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-26 23:05:28 +02:00
Maximilian Wilhelm
36fe61d8e7 link executor: Unify code paths.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-26 22:41:32 +02:00
Maximilian Wilhelm
e52e94fe5e static executor: Use vrf-member when setting gateway if present.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-08-26 22:29:59 +02:00
Ariadne Conill
d21d83fb26
Merge pull request #44 from ifupdown-ng/feature/link-hwaddress
link executor: add support for hwaddress option
2020-08-26 03:36:06 -06:00
Ariadne Conill
14914f1251 executors: use set -e consistently 2020-08-26 03:35:18 -06:00
Ariadne Conill
68415ce71d link executor: add support for hwaddress option 2020-08-26 02:22:30 -06:00
Ariadne Conill
e7ef2d1e83
Merge pull request #43 from ifupdown-ng/feature/phase-exec-messages
execute: note the phase each executor is being invoked in
2020-08-26 02:12:57 -06:00
Ariadne Conill
4d64176ea3 execute: note the phase each executor is being invoked in 2020-08-26 02:06:36 -06:00
Ariadne Conill
b30f84fdbd
Merge pull request #41 from ifupdown-ng/bugfix/link-back-to-up-down
Bugfix/link back to up down
2020-08-25 08:19:13 -06:00
Ariadne Conill
cca3608ad7 link tests: switch back to up/down from pre-up/post-down 2020-08-25 08:17:58 -06:00
Ariadne Conill
6c7c3f570d link executor: switch back to up/down phase 2020-08-25 08:17:58 -06:00
Ariadne Conill
b311293c6b
Merge pull request #40 from ifupdown-ng/bugfix/bridge-ports-none
bridge: don't generate dependencies for bridge-ports none
2020-08-25 08:10:26 -06:00
Ariadne Conill
0ff263a02a bridge: don't generate dependencies for bridge-ports none 2020-08-25 08:09:31 -06:00
Ariadne Conill
24d092ee46
Merge pull request #39 from ifupdown-ng/bugfix/skipping-interface-message-verbose
lifecycle: only print skipping interface message if verbose
2020-08-24 11:34:38 -06:00
Ariadne Conill
aefaaa7457 lifecycle: only print skipping interface message if verbose 2020-08-24 11:33:56 -06:00
Ariadne Conill
3997b6a952
Merge pull request #38 from ifupdown-ng/bugfix/vlan-complex
vlan and assorted fixes
2020-08-24 11:14:45 -06:00
Ariadne Conill
db5d2b8afd link tests: fix phasing 2020-08-24 11:08:42 -06:00
Ariadne Conill
b378305286 link executor: don't try to modprobe in mock mode 2020-08-24 11:07:40 -06:00
Ariadne Conill
de20e5f8a9 lifecycle: handle executors in reverse order when taking an interface down 2020-08-24 11:04:48 -06:00
Ariadne Conill
dfb979d00d list: add LIF_LIST_FOREACH_REVERSE() 2020-08-24 11:03:18 -06:00
Ariadne Conill
80cdfc7f96 link executor: use slightly different strategy for taking vlans down 2020-08-24 10:56:56 -06:00
Ariadne Conill
dc52642d73 static executor: default to metric=1 if not specified
this is needed to override learned routes if a gateway is configured
2020-08-24 10:52:10 -06:00
Ariadne Conill
8fe264a120 link executor: switch to pre-up/post-down 2020-08-24 10:47:16 -06:00
Ariadne Conill
aba4b18862 lifecycle: add some helpful debug 2020-08-24 10:36:17 -06:00
Ariadne Conill
050ab77bd3 tests: add vlan-complex.interface and ifquery test for it 2020-08-24 10:06:06 -06:00
Ariadne Conill
68021bc652
Merge pull request #35 from ifupdown-ng/feature/bsearch-parsing
interface config parser rewrite
2020-08-24 04:41:06 -06:00
Ariadne Conill
f17fa09944 ifupdown-ng 0.8.1. 2020-08-24 04:00:31 -06:00
Ariadne Conill
7da13ddf83
Merge pull request #37 from ifupdown-ng/feature/unify-link-and-vlan-executors
unify link and vlan executors
2020-08-24 03:59:14 -06:00
Ariadne Conill
536612d02b build: ensure we exit 1 after running kyua report 2020-08-24 03:36:26 -06:00
Ariadne Conill
14e2fecc20 tests: add vlan-named.interfaces fixture 2020-08-24 03:33:57 -06:00
Ariadne Conill
3abe6b1bd5 github: add kyua debug 2020-08-24 03:32:56 -06:00
Ariadne Conill
1384b38c78 interface: use link executor on vlans too 2020-08-24 03:06:32 -06:00
Ariadne Conill
8e7c935cdf link executor tests: test vlan dependency learning 2020-08-24 03:01:38 -06:00
Ariadne Conill
badcccaa5b link executor: integrate physical vlan configuration 2020-08-24 02:58:39 -06:00
Ariadne Conill
72ad50213f cmd: fix regression with ifdown -f handling
Alpine #11887
2020-08-24 02:19:01 -06:00
Ariadne Conill
90ba72efe2 makefile: add missing optional executors 2020-08-20 04:46:06 -06:00
Ariadne Conill
05df95964e ifupdown-ng 0.8.0. 2020-08-20 04:42:48 -06:00
Ariadne Conill
885778a412
Merge pull request #36 from ifupdown-ng/feature/static-metric
static: add support for the metric property
2020-08-20 04:41:51 -06:00
Ariadne Conill
ce07e1ff1f static: add support for the metric property 2020-08-20 04:40:38 -06:00
Ariadne Conill
6d15f21073 interface: handle is_bridge and is_bond hacks in a single place 2020-08-20 04:07:51 -06:00
Ariadne Conill
aba140a977 interface-file: use bsearch to find parser functions 2020-08-20 04:04:34 -06:00
Ariadne Conill
4a11d4fdd8 interface-file: break out source keyword handling 2020-08-20 03:46:31 -06:00
Ariadne Conill
df9c0284b1 interface-file: let commands initialize the interface collection themselves 2020-08-20 03:45:56 -06:00
Ariadne Conill
b57aba1a97 interface-file: split out auto keyword handling 2020-08-20 03:41:07 -06:00
Ariadne Conill
d36a522470 interface-file: add and use report_error() 2020-08-20 03:34:59 -06:00
Ariadne Conill
a7dc005b8d
Merge pull request #34 from ifupdown-ng/feature/vrf-static-route
vrf static route support
2020-08-20 03:21:44 -06:00
Ariadne Conill
4fdd91beee test: add test for vrf static route support 2020-08-20 03:20:26 -06:00
Ariadne Conill
0928f07af5 interface-file: imply static executor use if gateway is specified 2020-08-20 03:20:07 -06:00
Ariadne Conill
e5c592f2c1 remove lif_interface.is_dhcp too 2020-08-20 03:14:57 -06:00
Ariadne Conill
846df3272f remove lif_interface.is_static, no longer used 2020-08-20 03:10:26 -06:00
Ariadne Conill
d463d455da simplify executor use statements 2020-08-20 03:08:29 -06:00
Ariadne Conill
9caffc01c2 static executor: if vrf-table is set, apply the static route to a specific table 2020-08-20 02:59:43 -06:00
Ariadne Conill
184526401b
Merge pull request #33 from ifupdown-ng/feature/gre-executor
gre executor
2020-08-20 02:43:53 -06:00
Ariadne Conill
c86952ce71 tests: add gre executor tests 2020-08-20 02:40:27 -06:00
Ariadne Conill
6d2ae938d1 tests: add ifquery test for gre dependency learning 2020-08-20 02:33:51 -06:00
Ariadne Conill
d31f8614a3 gre executor: fix dependency learning 2020-08-20 02:33:38 -06:00
Ariadne Conill
8f057286d3 add gre executor 2020-08-20 02:30:12 -06:00
Ariadne Conill
b08b52939f
Merge pull request #32 from ifupdown-ng/feature/tunnel-generic
tunnel: improve validation of options
2020-08-20 02:10:16 -06:00
Ariadne Conill
cd1b0d5833 tunnel: improve validation of options 2020-08-20 02:09:31 -06:00
Ariadne Conill
59ba50ba4e
Merge pull request #30 from ifupdown-ng/feature/tunnel-generic
generic tunnel executor
2020-08-19 06:25:26 -06:00
Ariadne Conill
93116920da tests: verify ttl is remapped to tunnel-ttl 2020-08-19 06:20:16 -06:00
Ariadne Conill
075352b631 interface-file: remap ttl to tunnel-ttl 2020-08-19 06:20:15 -06:00
Ariadne Conill
0238051052 tests: add tunnel executor tests 2020-08-19 06:19:55 -06:00
Ariadne Conill
5fb1b4b26c tunnel executor: support mocking 2020-08-19 06:19:41 -06:00
Ariadne Conill
953666f1fb tests: add tests for managing the tunnel executor 2020-08-19 06:19:39 -06:00
Ariadne Conill
c24143982f interface-file: map 'mode' to tunnel-mode 2020-08-19 06:19:05 -06:00
Ariadne Conill
a4f147b5d0 tests: add some tunnel configuration fixtures 2020-08-19 06:18:44 -06:00
Ariadne Conill
36271e8657 build: install tunnel executor by default 2020-08-19 06:18:44 -06:00
Ariadne Conill
9cc74b58c4 interface-file: remap legacy ifupdown & ifupdown2 tunnel parameters 2020-08-19 06:18:40 -06:00
Ariadne Conill
ac56a85074
Merge pull request #31 from ifupdown-ng/feature/ppp
ppp executor
2020-08-19 06:17:44 -06:00
Ariadne Conill
e18ab114e0 ppp: add support for ifupdown2 ppp-physdev for dependency learning 2020-08-19 06:16:37 -06:00
Ariadne Conill
c2465344d5 tests: add ppp executor tests 2020-08-19 06:15:00 -06:00
Ariadne Conill
472319b847 add ppp executor 2020-08-19 06:12:27 -06:00
Ariadne Conill
0dd8dc1b0e tests: add ifquery tests for ppp 2020-08-19 06:09:05 -06:00
Ariadne Conill
dd02502461 tests: add ppp fixtures 2020-08-19 06:05:29 -06:00
Ariadne Conill
823544aace interface-file: map ppp-provider 2020-08-19 06:03:48 -06:00
Maximilian Wilhelm
aa11601893
Merge pull request #29 from ifupdown-ng/feature/ifupdown2-keyword-rewrite
add compatibility with problematic ifupdown2 terms
2020-08-19 10:21:56 +02:00
Ariadne Conill
71290da711 tests: add ifupdown2 vrf configuration fixture, and some tests against it 2020-08-19 02:14:08 -06:00
Ariadne Conill
cb03eb1ee9 interface-file: remap problematic ifupdown2 tokens to our equivalents 2020-08-19 02:10:54 -06:00
Ariadne Conill
eeb40fc689 libifupdown: move ARRAY_SIZE() declaration from multicall 2020-08-19 01:58:41 -06:00
Ariadne Conill
56323828a9
Merge pull request #28 from ifupdown-ng/feature/vrf-executor
VRF executor implementation
2020-08-18 20:35:54 -06:00
Ariadne Conill
3e427a1327
Merge pull request #24 from ifupdown-ng/feature/admin-guide
Feature/admin guide
2020-08-18 20:35:17 -06:00
Ariadne Conill
a08df1249b admin guide: formatting fixes, discuss executors 2020-08-18 20:33:44 -06:00
Ariadne Conill
2c37b22d70 tests: add fixture & test for VRF topology learning 2020-08-18 16:52:11 -06:00
Ariadne Conill
ba98705c8f tests: add tests for VRF executor 2020-08-18 16:28:34 -06:00
Ariadne Conill
ba2ad348ff executor scripts: add vrf executor 2020-08-18 16:16:46 -06:00
Ariadne Conill
a2a532c8ba
Merge pull request #27 from ifupdown-ng/feature/link-mtu
link: support `mtu` parameter (like debian)
2020-08-18 16:01:06 -06:00
Ariadne Conill
962ef0d415 link: support mtu parameter (like debian) 2020-08-18 15:57:01 -06:00
Ariadne Conill
d6c7bdb299 README: link to admin guide 2020-08-18 15:26:14 -06:00
Ariadne Conill
fb38e22cd1 admin guide: expand on dependency relationships 2020-08-18 15:26:02 -06:00
Ariadne Conill
06e2d3b035 add admin guide 2020-08-18 15:13:53 -06:00
Ariadne Conill
f282704186 README: requires keyword is not necessarily required anymore 2020-08-18 14:42:30 -06:00
Ariadne Conill
b9efc841a3 Merge branch 'feature/refactor-opts' into master 2020-08-18 14:40:50 -06:00
Ariadne Conill
b084f1c99c ifctrstat: add manpage xref 2020-08-18 14:36:35 -06:00
Ariadne Conill
0a3f1e33a8 ifctrstat: lower-case help text for program-specific options 2020-08-18 14:36:15 -06:00
Ariadne Conill
9928beef68 doc: add ifctrstat(8) manpage 2020-08-18 14:32:07 -06:00
Ariadne Conill
99571a4c03 ifctrstat: fix style issues 2020-08-18 14:20:55 -06:00
Ariadne Conill
2c562b7be0
Merge pull request #23 from ifupdown-ng/feature/multicall-manpage-xref
add manpage xref to applet help output
2020-08-18 14:18:58 -06:00
Ariadne Conill
520b831bdf cmd: add manpage xrefs where we have manpages already 2020-08-18 14:14:33 -06:00
Ariadne Conill
e2959275b6 multicall: add ability for applets to specify a manpage reference 2020-08-18 14:12:34 -06:00
A. Wilcox
327e738a55
Refactor how options are handled 2020-08-17 14:59:19 -05:00
Ariadne Conill
9fed844352
Merge pull request #20 from ifupdown-ng/ifctrstat
Add ifctrstat applet
2020-08-17 13:16:10 -06:00
A. Wilcox
1a11bb768f
ifctrstat: Use new structure and recalculate binary size 2020-08-17 14:13:16 -05:00
A. Wilcox
7b45d3939d
ifctrstat: Add -n option to omit label printing 2020-08-14 14:27:23 -05:00
A. Wilcox
6106164d7c
ifctrstat: Add ability to output counter values 2020-08-14 11:29:04 -05:00
A. Wilcox
95d943ee44
ifctrstat: Misc style fixes 2020-08-14 11:28:14 -05:00
A. Wilcox
05e0ac139b
ifctrstat: Initial structure for new applet 2020-08-14 11:27:43 -05:00
Ariadne Conill
36f0930c27 gitignore: ignore .lock files generated by testsuite 2020-08-13 00:36:50 -06:00
Ariadne Conill
db57f7c07c readme: mention irc channel 2020-08-13 00:36:23 -06:00
Ariadne Conill
0999347dab
Merge pull request #22 from ifupdown-ng/feature/configurable-applets
build: configurable applets
2020-08-12 22:39:38 -06:00
Ariadne Conill
196c000161 build: further cleanup 2020-08-12 17:09:14 -06:00
Ariadne Conill
b30e0223d0 multicall: only enable applets actually compiled in 2020-08-12 16:57:40 -06:00
Ariadne Conill
ff4822cf05 build: allow for applets to be customized 2020-08-12 16:57:40 -06:00
Ariadne Conill
4e3d2dbbc3
Merge pull request #18 from ifupdown-ng/feature/if-option-group
Feature/if option group
2020-08-11 23:35:45 -06:00
Ariadne Conill
f7d21d4b46 ifupdown: port to use option groups 2020-08-11 23:14:55 -06:00
Ariadne Conill
fda7bfae57 ifquery: use generic_usage() instead of ifquery_usage() 2020-08-11 23:10:35 -06:00
Ariadne Conill
f6d9f65248 ifquery: port to use option groups 2020-08-11 23:03:55 -06:00
Ariadne Conill
fdfe8ac080 multicall: migrate execution options to common code 2020-08-11 22:52:15 -06:00
Ariadne Conill
52509c508c multicall: migrate match options to shared collection 2020-08-11 22:37:36 -06:00
Ariadne Conill
f871002ecc multicall: add self_applet 2020-08-11 22:37:10 -06:00
Ariadne Conill
2be5add47b multicall: split option handling routines out to multicall-options.c 2020-08-11 22:15:52 -06:00
Ariadne Conill
b817bdb999 multicall: move match_options to a common include 2020-08-11 22:12:37 -06:00
Ariadne Conill
b481db6c20 multicall: add support for unified option handling 2020-08-11 22:07:32 -06:00
Ariadne Conill
ebd04cafda commands: refactor applet_usage functions to take a status code (ref #16) 2020-08-11 20:44:04 -06:00
Ariadne Conill
f96a74f232 makefile: clarify some things 2020-08-11 20:40:22 -06:00
Ariadne Conill
41e9751ac0
Merge pull request #17 from AdelieLinux/full-clean
Makefile: Ensure built libs are removed in `clean`
2020-08-11 19:23:25 -06:00
Ariadne Conill
a547ab2825
Merge pull request #13 from AdelieLinux/bugurl
Makefile: Update bug reporting URL
2020-08-11 18:11:31 -06:00
A. Wilcox
ff65bd53b5
Makefile: Ensure built libs are removed in clean
This commit ensures that `${LIBIFUPDOWN_LIB}` is removed when `make
clean` is run.
2020-08-11 10:21:02 -05:00
A. Wilcox
b87a0df83d
Makefile: Update bug reporting URL
It should point to the GitHub organisation for ifupdown-ng.
2020-08-11 09:56:22 -05:00
Ariadne Conill
3205a89840 ifupdown-ng 0.7.0. 2020-08-04 13:20:26 -06:00
Ariadne Conill
5bf6c84464 add some helper stubs for split ifupdown1-addon setups 2020-08-04 13:18:36 -06:00
Ariadne Conill
d9fd545ad1 build: add infrastructure for installing stub executors
this enables the split setup with ifupdown1 addons and core
executors as will be implemented in Alpine 3.13.
2020-08-04 13:14:06 -06:00
Ariadne Conill
0de4a3f6f9 tests: add ifup/ifdown/ifquery tests for vlans 2020-08-04 13:08:30 -06:00
Ariadne Conill
b27caaedf6 tests: add mock vlan executor and vlan fixture 2020-08-04 12:38:05 -06:00
Ariadne Conill
072c1ea148 ifquery: build full dependency tree when in dot mode 2020-08-04 12:36:38 -06:00
Ariadne Conill
049af8b9fb interface: if an interface name contains a period, treat it as a vlan device 2020-08-04 12:29:31 -06:00
Ariadne Conill
66e6a09bbe ifupdown-ng 0.6.0. 2020-08-04 12:11:06 -06:00
Ariadne Conill
8b00727370 interfaces.5: note that we are compatible with legacy ifupdown1 syntax 2020-08-04 12:09:57 -06:00
Ariadne Conill
369f3ff3c2 interfaces.5: document inherit option 2020-08-04 12:08:58 -06:00
Ariadne Conill
49dd8942f2 tests: add inheritance tests
we only need to test ifquery, as this is just a merge
operation.
2020-08-04 12:06:39 -06:00
Ariadne Conill
8615cf62ef interface: properly sync parent-child state when inheriting 2020-08-04 12:01:06 -06:00
Ariadne Conill
df2519c1ec dict: cleanups 2020-08-04 11:59:36 -06:00
Ariadne Conill
0d6b9d5d17 build: fix "ln: file exists" that pops up sometimes 2020-08-04 11:48:55 -06:00
Ariadne Conill
6728d2febe interface: add inherit property when inheriting 2020-08-04 11:48:22 -06:00
Ariadne Conill
6c7fa2919d interface-file: add support for inherit/inherits keyword 2020-08-04 11:46:56 -06:00
Ariadne Conill
3d5ca9050e interface: add lif_interface_collection_inherit() (ref #2) 2020-08-04 11:46:10 -06:00
Ariadne Conill
0eb25f1d62 interface file: simplify scanning iface directives for relevant keywords 2020-08-04 11:32:34 -06:00
Ariadne Conill
1964a99e5d lifecycle: don't insert a requires entry if it would be empty 2020-07-31 17:12:13 -06:00
Ariadne Conill
ed56d2b33b build: actually install ifupdown-executor.7 2020-07-29 03:51:57 -06:00
Ariadne Conill
7a4c16c665 ifupdown-ng 0.5.0. 2020-07-29 03:19:19 -06:00
Ariadne Conill
58bc969bbb ifupdown: only show granular locking errors in verbose mode 2020-07-29 03:18:12 -06:00
Ariadne Conill
ef3bdd79da clean up locking-related errors further 2020-07-29 03:17:12 -06:00
Ariadne Conill
c71c01862d ifupdown: make the locking errors more useful 2020-07-29 03:13:34 -06:00
Ariadne Conill
0ca8e42ee6 refactoring locking, make it per-interface to avoid deadlocks 2020-07-29 03:10:55 -06:00
Ariadne Conill
91115edeee ifupdown: use fcntl advisory locks to serialize state changes (closes #8) 2020-07-29 03:01:49 -06:00
Ariadne Conill
2fecbfc7e9 tests: add tests for learned executors 2020-07-29 02:37:14 -06:00
Ariadne Conill
2be1fa5b0b tests: add a set of more complicated dependency learning tests 2020-07-29 02:33:00 -06:00
Ariadne Conill
edfb91c05f lifecycle: separate environment building into build_environment() 2020-07-29 02:24:10 -06:00
Ariadne Conill
e4c3c4e06e mock dependency generator: add support for mock-depends property 2020-07-29 02:14:42 -06:00
Ariadne Conill
37b5c372f3 doc: add ifupdown-executor(7) manpage (closes #9) 2020-07-29 02:10:31 -06:00
Ariadne Conill
81d0ebc3e8 remove unnecessary strcmp wrapper, use typecasting instead 2020-07-28 17:57:51 -04:00
Ariadne Conill
cfbfa07e85 list: refactor lif_list_free_nodes() a bit 2020-07-28 17:53:48 -04:00
Ariadne Conill
58816712c6
Merge pull request #7 from BarbarossaTM/deduce-use
Deduce which addons to 'use' from parameters names
2020-07-28 17:50:32 -04:00
Maximilian Wilhelm
8045081024 list: Set pointer to free()ed list to NULL.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-07-28 23:30:09 +02:00
Maximilian Wilhelm
9d958892ed Free nodes of a lif_dict_find_all() result.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-07-28 23:25:01 +02:00
Maximilian Wilhelm
73f3690432 Deduce which addons to 'use' from parameters names (closes #6)
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-07-28 22:44:49 +02:00
Maximilian Wilhelm
2420023b74 Add comparator wrapper and type cast values properly.
Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-07-28 22:44:42 +02:00
Maximilian Wilhelm
f51c976b52 dict: Add lif_dict_add_once()
Add a function to add a key/value pair to the dict only if it doesn't exist
  within the dict already.

  As the dict is type agnostic this requires a comparator function to be given.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-07-28 22:44:22 +02:00
Maximilian Wilhelm
b1dfa609f9 dict: Add lif_dict_find_all().
As a dict can have multiple values for any given key we add a function to
  query all occurences of a given key and return them as a (new) list.

Signed-off-by: Maximilian Wilhelm <max@sdn.clinic>
2020-07-28 22:41:41 +02:00
Ariadne Conill
cf59f6af4c tests: add ifup/ifdown tests for learned dependencies (close #5) 2020-07-28 14:04:27 -06:00
Ariadne Conill
21bcbe1017 execute: fix memory leak in lif_execute_fmt_with_result() 2020-07-28 13:53:21 -06:00
Ariadne Conill
c97594b89b tests: add test for mock dependency generator 2020-07-28 13:49:08 -06:00
Ariadne Conill
fb5ff4eac2 lifecycle: add support for learning dependents from executors (ref #5) 2020-07-28 13:46:25 -06:00
Ariadne Conill
3a75de9f8c execute: add lif_execute_buf_with_result() to collect a result buffer 2020-07-28 13:01:15 -06:00
Ariadne Conill
3302089d4b tests: add mock-dependency-generator (ref #5) 2020-07-28 12:07:09 -06:00
Ariadne Conill
cb9d347c8a ifupdown-ng 0.4.2. 2020-07-28 10:40:46 -06:00
Ariadne Conill
ef586a6b94 build: allow the packager to configure the executor scripts installed 2020-07-28 09:04:06 -06:00
Ariadne Conill
5b9ade9db8 executors: port @ncopa's bridge scripts to run as a native executor 2020-07-28 08:34:20 -06:00
Ariadne Conill
e1918f3fd5 ifupdown: add --force for compatibility with busybox ifupdown 2020-07-28 08:23:58 -06:00
Ariadne Conill
7af585e738 ifupdown-ng 0.4.1. 2020-07-26 09:33:43 -06:00
Ariadne Conill
8ef2815ef3 link executor: fix pre-up/post-down bug 2020-07-26 07:41:27 -06:00
Ariadne Conill
d88d9b7de4 ifupdown-ng 0.4. 2020-07-26 04:20:47 -06:00
Ariadne Conill
8fb2a0571d make sure VERBOSE is respected in executors 2020-07-26 04:18:57 -06:00
Ariadne Conill
326e27cdb4 executors: static: bring down routes before addresses 2020-07-26 04:13:25 -06:00
Ariadne Conill
83440e3f88 tests: add static executor tests 2020-07-26 04:11:17 -06:00
Ariadne Conill
e3a6e08e40 tests: add dhcp executor tests 2020-07-26 04:00:56 -06:00
Ariadne Conill
d35a3e3b38 tests: add tests for ipv6-ra executor 2020-07-26 03:54:06 -06:00
Ariadne Conill
5f078537bf tests: add tests for link executor 2020-07-26 03:48:37 -06:00
Ariadne Conill
08805f7de3 lifecycle: handle_commands_for_phase(): drop unused lifname variable 2020-07-26 03:39:58 -06:00
Ariadne Conill
be00984946 lifecycle: stop bringing up links, use link executor instead 2020-07-26 03:38:55 -06:00
Ariadne Conill
83ccb4f81d add link executor 2020-07-26 03:36:31 -06:00
Ariadne Conill
e046ea6a24 tests: migrate to mock link executor 2020-07-26 03:34:04 -06:00
Ariadne Conill
52d2bad3b8 interface: always attach "use link" executor 2020-07-26 03:29:16 -06:00
Ariadne Conill
05e8f9bbb5 make all executor scripts testable/mockable 2020-07-26 03:26:11 -06:00
Ariadne Conill
12307aeb9d lifecycle: remove static address configuration (replaced with static executor) 2020-07-26 03:20:35 -06:00
Ariadne Conill
43c147c6cf build: allow for non-linux executors 2020-07-26 03:16:52 -06:00
Ariadne Conill
c1e1d30a58 tests: convert ifup/ifdown tests to test with mock executors 2020-07-26 03:12:02 -06:00
Ariadne Conill
e29e552cc2 port iproute2 configuration from C to shell 2020-07-26 03:05:46 -06:00
Ariadne Conill
cae16c5758 simplify loopback 2020-07-26 02:59:15 -06:00
Ariadne Conill
9e4e17707f lifecycle: expose INTERFACES_FILE env var to executors 2020-07-26 02:50:01 -06:00
Ariadne Conill
0669e897fd tests: add mock static executor 2020-07-26 02:37:39 -06:00
Ariadne Conill
987e6399de libifupdown: interface: add support for automatic "use static" insertion 2020-07-26 02:32:00 -06:00
Ariadne Conill
052156ac09 ifquery: add --property (ref #4) 2020-07-26 02:28:29 -06:00
Ariadne Conill
f06ee76d41 implement ipv6-ra executor for controlling ipv6 RA setting on a per-NIC basis 2020-07-26 01:24:06 -06:00
Ariadne Conill
690190a0b9 libifupdown: interface-file: do not process use directives unless in an interface context 2020-07-25 08:49:37 -06:00
Ariadne Conill
4e452c5fec multicall: mark multicall_usage() as noreturn
this is needed to shut up coverity
2020-07-25 08:47:21 -06:00
Ariadne Conill
ea1c39ff5a ifupdown-ng 0.3. 2020-07-25 03:18:51 -06:00
Ariadne Conill
3e2d447a44 build: make distcheck work 2020-07-25 03:18:15 -06:00
Ariadne Conill
38af1ce2d2 dhcp: failing to find a supported impl should be a fatal error 2020-07-25 03:09:41 -06:00
Ariadne Conill
00c1d0f58c dhcp: react to $PHASE not legacy $MODE 2020-07-25 03:08:32 -06:00
Ariadne Conill
96392f0dd1 dhcp: add dhclient support 2020-07-25 03:06:47 -06:00
Ariadne Conill
201e00bbac dhcp: add support for dhcpcd 2020-07-25 03:04:02 -06:00
Ariadne Conill
c51c9f7103 build: install executor scripts 2020-07-25 02:55:09 -06:00
Ariadne Conill
7eeb7c98b5 libifupdown: lifecycle: remove built-in dhcp implementation 2020-07-25 02:50:45 -06:00
Ariadne Conill
cd8fbb64fb tests: use mock executors 2020-07-25 02:50:24 -06:00
Ariadne Conill
559bf2d1a4 libifupdown: execute: fix behavior of lif_file_is_executable() 2020-07-25 02:49:44 -06:00
Ariadne Conill
fa0a0d7f79 tests: add mock executor 2020-07-25 02:38:42 -06:00
Ariadne Conill
cf8ae46ca0 executors: add dhcp executor (replacing the built in) 2020-07-25 02:36:11 -06:00
Ariadne Conill
dca34ebba7 ifupdown: allow for the executor path to be changed 2020-07-25 02:22:27 -06:00
Ariadne Conill
95a72b5663 libifupdown: lifecycle: implement native executor support 2020-07-25 02:19:15 -06:00
Ariadne Conill
f8c14fc233 libifupdown: lifecycle: if in verbose mode, export VERBOSE to executors 2020-07-25 02:18:53 -06:00
Ariadne Conill
83d64b0a69 libifupdown: lifecycle: IFACE env variable should be set to the logical ifname 2020-07-25 02:18:35 -06:00
Ariadne Conill
96112d5dc9 libifupdown: execute: add lif_maybe_try_executor() 2020-07-25 02:17:27 -06:00
Ariadne Conill
4a5a37a620 build: define EXECUTOR_PATH 2020-07-25 02:02:28 -06:00
Ariadne Conill
eb8f1938dc libifupdown: add utility function to determine if a path is executable 2020-07-25 01:58:20 -06:00
Ariadne Conill
b35d985ea4 ifupdown-ng 0.2.2. 2020-07-25 01:23:14 -06:00
Ariadne Conill
1919f9b0d3 fix README formatting 2020-07-25 01:18:03 -06:00
Ariadne Conill
cbcd8e8326 allow for building against libbsd for glibc systems 2020-07-25 01:16:01 -06:00
Ariadne Conill
492e9e7038 tests: add tests for fallback netmask handling 2020-07-25 01:06:21 -06:00
Ariadne Conill
d582e405b6 lifecycle: simplify netmask fallback path 2020-07-25 01:02:05 -06:00
Ariadne Conill
60633e4acb lifecycle: add backwards compat for netmask triples 2020-07-25 00:58:32 -06:00
Ariadne Conill
03e00cbb02 ifupdown-ng 0.2.1. 2020-07-24 08:33:22 -06:00
Ariadne Conill
532aaa30de build: add install_docs target 2020-07-24 08:32:58 -06:00
Ariadne Conill
475aeb2992 add docs for ifdown/ifup/interfaces too 2020-07-24 08:28:29 -06:00
Ariadne Conill
cebe24e1ec docs: properly format see also section 2020-07-24 07:55:29 -06:00
Ariadne Conill
915f04912c update gitignore 2020-07-24 07:48:50 -06:00
Ariadne Conill
bc48534814 add ifquery manpage 2020-07-24 07:48:14 -06:00
Ariadne Conill
6d1c67ed38 README: note how to build apps, docs and run the tests 2020-07-24 07:17:18 -06:00
Ariadne Conill
713d5071d5 state: don't consider missing state file to be a fatal error 2020-07-24 07:13:04 -06:00
Ariadne Conill
464c078bbe github won't let us float elements, gross 2020-07-24 06:15:10 -06:00
Ariadne Conill
ef86d9400b more tweaking 2020-07-24 06:13:43 -06:00
Ariadne Conill
7eb81a1b3a more tweaking 2020-07-24 06:13:01 -06:00
Ariadne Conill
ef6600a91e more tweaking 2020-07-24 06:11:20 -06:00
Ariadne Conill
53b7fbd393 more readme hacking 2020-07-24 06:09:42 -06:00
Ariadne Conill
85cab33148 clean up README a bit 2020-07-24 06:07:36 -06:00
153 changed files with 9264 additions and 695 deletions

View file

@ -10,10 +10,16 @@ jobs:
- name: Update system and add dependencies
run: |
apk upgrade -Ua
apk add build-base git kyua atf
apk add build-base git kyua atf scdoc
- name: Checkout
uses: actions/checkout@v2
- name: Build and run tests
- name: Build
run: make
- name: Build documentation
run: make docs
- name: Run tests
run: make check

12
.gitignore vendored
View file

@ -1,5 +1,17 @@
*.o
*.a
*.1
*.2
*.3
*.4
*.5
*.6
*.7
*.8
ifupdown
ifquery
ifup
ifdown
ifctrstat
ifparse
*.lock

View file

@ -1,4 +1,5 @@
Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
Copyright (c) 2020-2021 Ariadne Conill <ariadne@dereferenced.org>
Copyright (c) 2020-2021 Maximilian Wilhelm <max@sdn.clinic>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above

198
Makefile
View file

@ -1,12 +1,30 @@
LAYOUT ?= linux
SCDOC := scdoc
LIBBSD_CFLAGS =
LIBBSD_LIBS =
PACKAGE_NAME := ifupdown-ng
PACKAGE_VERSION := 0.2
PACKAGE_BUGREPORT := https://github.com/kaniini/ifupdown-ng/issues/new
PACKAGE_VERSION := 0.11.3
PACKAGE_BUGREPORT := https://github.com/ifupdown-ng/ifupdown-ng/issues/new
INTERFACES_FILE := /etc/network/interfaces
STATE_FILE := /run/ifstate
CFLAGS = -ggdb3 -Os -Wall -Wextra
CPPFLAGS = -I. -DINTERFACES_FILE=\"${INTERFACES_FILE}\" -DSTATE_FILE=\"${STATE_FILE}\" -DPACKAGE_NAME=\"${PACKAGE_NAME}\" -DPACKAGE_VERSION=\"${PACKAGE_VERSION}\" -DPACKAGE_BUGREPORT=\"${PACKAGE_BUGREPORT}\"
CONFIG_FILE := /etc/network/ifupdown-ng.conf
EXECUTOR_PATH := /usr/libexec/ifupdown-ng
CFLAGS ?= -ggdb3 -Os
CFLAGS += -Wall -Wextra -Werror
CFLAGS += -Wmissing-declarations -Wmissing-prototypes -Wcast-align -Wpointer-arith -Wreturn-type
CFLAGS += ${LIBBSD_CFLAGS}
CPPFLAGS = -I.
CPPFLAGS += -DINTERFACES_FILE=\"${INTERFACES_FILE}\"
CPPFLAGS += -DSTATE_FILE=\"${STATE_FILE}\"
CPPFLAGS += -DCONFIG_FILE=\"${CONFIG_FILE}\"
CPPFLAGS += -DPACKAGE_NAME=\"${PACKAGE_NAME}\"
CPPFLAGS += -DPACKAGE_VERSION=\"${PACKAGE_VERSION}\"
CPPFLAGS += -DPACKAGE_BUGREPORT=\"${PACKAGE_BUGREPORT}\"
CPPFLAGS += -DEXECUTOR_PATH=\"${EXECUTOR_PATH}\"
LIBIFUPDOWN_SRC = \
@ -19,47 +37,183 @@ LIBIFUPDOWN_SRC = \
libifupdown/state.c \
libifupdown/environment.c \
libifupdown/execute.c \
libifupdown/lifecycle.c
libifupdown/lifecycle.c \
libifupdown/config-parser.c \
libifupdown/config-file.c \
libifupdown/compat.c
LIBIFUPDOWN_OBJ = ${LIBIFUPDOWN_SRC:.c=.o}
LIBIFUPDOWN_LIB = libifupdown.a
MULTICALL_SRC = cmd/multicall.c
MULTICALL_SRC = \
cmd/multicall.c \
cmd/multicall-options.c \
cmd/multicall-exec-options.c \
cmd/multicall-match-options.c \
cmd/pretty-print-iface.c
MULTICALL_OBJ = ${MULTICALL_SRC:.c=.o}
MULTICALL = ifupdown
IFQUERY_SRC = cmd/ifquery.c
IFQUERY_OBJ = ${IFQUERY_SRC:.c=.o}
# enable ifup/ifdown applets (+16 KB)
CONFIG_IFUPDOWN ?= Y
IFUPDOWN_SRC = cmd/ifupdown.c
IFUPDOWN_OBJ = ${IFUPDOWN_SRC:.c=.o}
MULTICALL_${CONFIG_IFUPDOWN}_OBJ += ${IFUPDOWN_SRC:.c=.o}
CMDS_${CONFIG_IFUPDOWN} += ifup ifdown
CPPFLAGS_${CONFIG_IFUPDOWN} += -DCONFIG_IFUPDOWN
CMD_OBJ = ${MULTICALL_OBJ} ${IFQUERY_OBJ} ${IFUPDOWN_OBJ}
# enable ifquery applet (+4 KB)
# [+20 KB without ifup/ifdown]
CONFIG_IFQUERY ?= Y
IFQUERY_SRC = cmd/ifquery.c
MULTICALL_${CONFIG_IFQUERY}_OBJ += ${IFQUERY_SRC:.c=.o}
CMDS_${CONFIG_IFQUERY} += ifquery
CPPFLAGS_${CONFIG_IFQUERY} += -DCONFIG_IFQUERY
CMDS = ifup ifdown ifquery
# enable ifctrstat applet (+1 KB)
CONFIG_IFCTRSTAT ?= Y
IFCTRSTAT_SRC = cmd/ifctrstat.c cmd/ifctrstat-${LAYOUT}.c
MULTICALL_${CONFIG_IFCTRSTAT}_OBJ += ${IFCTRSTAT_SRC:.c=.o}
CMDS_${CONFIG_IFCTRSTAT} += ifctrstat
CPPFLAGS_${CONFIG_IFCTRSTAT} += -DCONFIG_IFCTRSTAT
LIBS = ${LIBIFUPDOWN_LIB}
# enable ifparse applet (+1 KB)
CONFIG_IFPARSE ?= Y
IFPARSE_SRC = cmd/ifparse.c
MULTICALL_${CONFIG_IFPARSE}_OBJ += ${IFPARSE_SRC:.c=.o}
CMDS_${CONFIG_IFPARSE} += ifparse
CPPFLAGS_${CONFIG_IFPARSE} += -DCONFIG_IFPARSE
all: libifupdown.a ${MULTICALL} ${CMDS}
# enable YAML support (+2 KB)
CONFIG_YAML ?= Y
YAML_SRC = \
libifupdown/yaml-base.c \
libifupdown/yaml-writer.c
LIBIFUPDOWN_${CONFIG_YAML}_OBJ += ${YAML_SRC:.c=.o}
CPPFLAGS_${CONFIG_YAML} += -DCONFIG_YAML
LIBIFUPDOWN_OBJ += ${LIBIFUPDOWN_Y_OBJ}
MULTICALL_OBJ += ${MULTICALL_Y_OBJ}
CMDS += ${CMDS_Y}
CPPFLAGS += ${CPPFLAGS_Y}
EXECUTOR_SCRIPTS_CORE ?= \
dhcp \
ipv6-ra \
static \
link \
ppp \
forward
EXECUTOR_SCRIPTS_OPT ?= \
batman \
bond \
bridge \
ethtool \
gre \
mpls \
tunnel \
vrf \
vxlan \
wifi \
wireguard
EXECUTOR_SCRIPTS ?= ${EXECUTOR_SCRIPTS_CORE} ${EXECUTOR_SCRIPTS_OPT}
EXECUTOR_SCRIPTS_STUB ?=
EXECUTOR_SCRIPTS_NATIVE ?=
TARGET_LIBS = ${LIBIFUPDOWN_LIB}
LIBS += ${TARGET_LIBS} ${LIBBSD_LIBS}
all: ${MULTICALL} ${CMDS}
${CMDS}: ${MULTICALL}
ln -s ifupdown $@
ln -sf ifupdown $@
${MULTICALL}: ${LIBS} ${CMD_OBJ}
${CC} -o $@ ${CMD_OBJ} ${LIBS}
${MULTICALL}: ${TARGET_LIBS} ${MULTICALL_OBJ}
${CC} -o $@ ${MULTICALL_OBJ} ${LIBS}
libifupdown.a: ${LIBIFUPDOWN_OBJ}
${LIBIFUPDOWN_LIB}: ${LIBIFUPDOWN_OBJ}
${AR} -rcs $@ ${LIBIFUPDOWN_OBJ}
clean:
rm -f ${LIBIFUPDOWN_OBJ} ${CMD_OBJ}
rm -f ${LIBIFUPDOWN_OBJ} ${MULTICALL_OBJ}
rm -f ${LIBIFUPDOWN_LIB}
rm -f ${CMDS} ${MULTICALL}
rm -f ${MANPAGES}
check: libifupdown.a ${CMDS}
kyua test
check: ${LIBIFUPDOWN_LIB} ${CMDS}
kyua test || (kyua report --verbose && exit 1)
install: all
install -D -m755 ${MULTICALL} ${DESTDIR}/sbin/${MULTICALL}
for i in ${CMDS}; do \
ln -s /sbin/${MULTICALL} ${DESTDIR}/sbin/$$i; \
done
for i in ${EXECUTOR_SCRIPTS}; do \
install -D -m755 executor-scripts/${LAYOUT}/$$i ${DESTDIR}${EXECUTOR_PATH}/$$i; \
done
for i in ${EXECUTOR_SCRIPTS_STUB}; do \
install -D -m755 executor-scripts/stub/$$i ${DESTDIR}${EXECUTOR_PATH}/$$i; \
done
for i in ${EXECUTOR_SCRIPTS_NATIVE}; do \
install -D -m755 executor-scripts/${LAYOUT}-native/$$i ${DESTDIR}${EXECUTOR_PATH}/$$i; \
done
install -D -m644 dist/ifupdown-ng.conf.example ${DESTDIR}${CONFIG_FILE}.example
.scd.1 .scd.2 .scd.3 .scd.4 .scd.5 .scd.6 .scd.7 .scd.8:
${SCDOC} < $< > $@
MANPAGES_5 = \
doc/ifstate.5 \
doc/ifupdown-ng.conf.5 \
doc/interfaces.5 \
doc/interfaces-bond.5 \
doc/interfaces-batman.5 \
doc/interfaces-bridge.5 \
doc/interfaces-forward.5 \
doc/interfaces-ppp.5 \
doc/interfaces-tunnel.5 \
doc/interfaces-vrf.5 \
doc/interfaces-vxlan.5 \
doc/interfaces-wifi.5 \
doc/interfaces-wireguard.5
MANPAGES_7 = \
doc/ifupdown-executor.7
MANPAGES_8 = \
doc/ifquery.8 \
doc/ifup.8 \
doc/ifdown.8 \
doc/ifctrstat.8 \
doc/ifparse.8
MANPAGES = ${MANPAGES_5} ${MANPAGES_7} ${MANPAGES_8}
docs: ${MANPAGES}
install_docs: docs
for i in ${MANPAGES_5}; do \
target=$$(basename $$i); \
install -D -m644 $$i ${DESTDIR}/usr/share/man/man5/$$target; \
done
for i in ${MANPAGES_7}; do \
target=$$(basename $$i); \
install -D -m644 $$i ${DESTDIR}/usr/share/man/man7/$$target; \
done
for i in ${MANPAGES_8}; do \
target=$$(basename $$i); \
install -D -m644 $$i ${DESTDIR}/usr/share/man/man8/$$target; \
done
.SUFFIXES: .scd .1 .2 .3 .4 .5 .6 .7 .8
DIST_NAME = ${PACKAGE_NAME}-${PACKAGE_VERSION}
DIST_TARBALL = ${DIST_NAME}.tar.xz
distcheck: check dist
dist: ${DIST_TARBALL}
${DIST_TARBALL}:
git archive --format=tar --prefix=${DIST_NAME}/ -o ${DIST_NAME}.tar ${DIST_NAME}
xz ${DIST_NAME}.tar

View file

@ -1,18 +1,49 @@
# ifupdown-ng
This package is a work in progress implementation of the ifupdown suite. It is
intended to be largely compatible with ifupdown and ifupdown2, with some caveats:
ifupdown-ng is a network device manager that is largely compatible with Debian
ifupdown, BusyBox ifupdown and Cumulus Networks' ifupdown2.
For more information read the [admin guide](doc/ADMIN-GUIDE.md).
## Dependency Resolution
![Dependency resolution example](doc/img/dependency-resolution.png)
ifupdown-ng uses a dependency resolver to determine interface bring-up order
in a deterministic way.
This is accomplished through a combination of manual hinting using the `requires`
keyword and dependency learning using native executors.
For compatibility with some legacy ifupdown executors, we also provide the
`requires` keyword under other environment variables in some cases.
## Caveats
* ifupdown2 python plugins are not supported at this time. An executor could be
written to handle them.
* ifupdown-ng uses a SAT solver to determine interface bring-up order, like
ifupdown2. However, relationships must be explicitly defined instead of
inferred by plugins in ifupdown2. This simplifies the executors and ensures
consistent behaviour across executors.
* ifupdown-ng retains compatibility with /etc/network/if-X.d scripts, but will
prefer using executors in /usr/libexec/ifupdown-ng where appropriate.
This package is planned to replace BusyBox ifupdown in Alpine at some point in
the future.
## Building
On musl systems, simply do `make` and `make install` to build and install.
On glibc systems, you must install `libbsd-dev` or equivalent and additionally define `LIBBSD_CFLAGS` and `LIBBSD_LIBS`:
# instal packages
apt install build-essential libbsd0 libbsd-dev
# build ifupdown-ng
make LIBBSD_CFLAGS="$(pkg-config --cflags libbsd-overlay)" LIBBSD_LIBS="$(pkg-config --cflags --libs libbsd-overlay)"
make install
To run the tests, do `make check`. Running the checks requires `kyua` (`apk add kyua` / `apt install kyua`).
To build the documentation, do `make docs` and `make install_docs`. Building
the documentation requires scdoc (`apk add scdoc` / `apt install scdoc`).
## Discussion
Discuss ifupdown-ng on IRC: irc.oftc.net #ifupdown-ng

97
cmd/ifctrstat-linux.c Normal file
View file

@ -0,0 +1,97 @@
/*
* cmd/ifctrstat-linux.c
* Purpose: Implement ifctrstat system-specific routines for Linux
*
* Copyright (c) 2020 Adélie Software in the Public Benefit, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "cmd/multicall.h"
#include "cmd/ifctrstat-linux.h"
struct counter_desc {
const char *name;
const void *data;
} avail_counters[] = {
{"rx.discard", "rx_dropped"},
{"rx.errors", "rx_errors"},
{"rx.octets", "rx_bytes"},
{"rx.packets", "rx_packets"},
{"tx.discard", "tx_dropped"},
{"tx.errors", "tx_errors"},
{"tx.octets", "tx_bytes"},
{"tx.packets", "tx_packets"}
};
size_t avail_counters_count = ARRAY_SIZE(avail_counters);
static int
counter_compare(const void *key, const void *candidate)
{
return strcasecmp((const char *)key, ((struct counter_desc *)candidate)->name);
}
const char *
read_counter(const char *interface, const char *counter)
{
FILE *fp;
const char *path;
char full_path[PATH_MAX];
char buffer[1024];
size_t in_count;
struct counter_desc *ctrdata;
errno = 0;
ctrdata = bsearch(counter, avail_counters, avail_counters_count, sizeof(struct counter_desc), counter_compare);
if (ctrdata) {
path = (const char *)ctrdata->data;
} else {
errno = ENOSYS;
return NULL;
}
if (snprintf(full_path, PATH_MAX, "/sys/class/net/%s/statistics/%s", interface, path) > PATH_MAX)
{
errno = ENOMEM;
return NULL;
}
fp = fopen(full_path, "r");
if (!fp)
{
return NULL;
}
in_count = fread(buffer, 1, sizeof(buffer), fp);
if (in_count == sizeof(buffer))
{
errno = ENOMEM;
fclose(fp);
return NULL;
}
if (ferror(fp))
{
return NULL;
}
fclose(fp);
/* take away the \n, we add our own */
buffer[in_count - 1] = '\0';
return strdup(buffer);
}

22
cmd/ifctrstat-linux.h Normal file
View file

@ -0,0 +1,22 @@
/*
* cmd/ifctrstat-linux.c
* Purpose: Implement ifctrstat system-specific routines for Linux
*
* Copyright (c) 2021 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#ifndef IFUPDOWN_IFCTRSTAT_LINUX__H__GUARD
#define IFUPDOWN_IFCTRSTAT_LINUX__H__GUARD
extern const char * read_counter(const char *interface, const char *counter);
#endif

159
cmd/ifctrstat.c Normal file
View file

@ -0,0 +1,159 @@
/*
* cmd/ifctrstat.c
* Purpose: Display statistics about interfaces on the system
*
* Copyright (c) 2020 Adélie Software in the Public Benefit, Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "libifupdown/libifupdown.h"
#include "cmd/multicall.h"
#include "cmd/ifctrstat-linux.h"
extern struct counter_desc { const char *name; const void *data; } avail_counters[];
extern int avail_counters_count;
static bool show_label = true;
static bool
counter_is_valid(const char *candidate)
{
for (int i = 0; i < avail_counters_count; i++)
{
if (strcasecmp(avail_counters[i].name, candidate) == 0)
return true;
}
return false;
}
static void
print_counter(const char *iface, const char *name, const char *value)
{
(void) iface;
if (show_label)
fprintf(stdout, "%s: %s\n", name, value);
else
fprintf(stdout, "%s\n", value);
}
static int
print_all_counters(const char *iface)
{
int code = EXIT_SUCCESS;
const char *res;
for (int i = 0; i < avail_counters_count; i++)
{
const char *ctr = avail_counters[i].name;
res = read_counter(iface, ctr);
if (!res)
{
fprintf(stderr, "%s: could not determine value of %s for interface %s: %s\n", argv0, ctr, iface, strerror(errno));
code = EXIT_FAILURE;
}
else
{
print_counter(iface, ctr, res);
}
}
return code;
}
static void
ifctrstat_list_counters(const char *opt_arg)
{
(void) opt_arg;
for (int i = 0; i < avail_counters_count; i++)
{
fprintf(stdout, "%s\n", avail_counters[i].name);
}
exit(EXIT_SUCCESS);
}
static void
ifctrstat_set_nolabel(const char *opt_arg)
{
(void) opt_arg;
show_label = false;
}
static int
ifctrstat_main(int argc, char *argv[])
{
if (optind >= argc)
generic_usage(self_applet, EXIT_FAILURE);
int idx = optind;
if (argc - idx == 0)
{
fprintf(stderr, "%s: interface required\n",
argv0);
return EXIT_FAILURE;
}
const char *iface = argv[idx++];
if (argc - idx == 0)
{
return print_all_counters(iface);
}
for (; idx < argc; idx++)
{
if (!counter_is_valid(argv[idx]))
{
fprintf(stderr, "%s: counter %s is not valid or not available\n", argv0, argv[idx]);
return EXIT_FAILURE;
}
errno = 0;
const char *res = read_counter(iface, argv[idx]);
if (!res)
{
fprintf(stderr, "%s: could not determine value of %s for interface %s: %s\n", argv0, argv[idx], iface, strerror(errno));
return EXIT_FAILURE;
}
print_counter(iface, argv[idx], res);
}
return EXIT_SUCCESS;
}
static struct if_option local_options[] = {
{'L', "list", NULL, "list available counters", false, ifctrstat_list_counters},
{'n', "no-label", NULL, "print value without counter label", false, ifctrstat_set_nolabel}
};
static struct if_option_group local_option_group = {
.desc = "Program-specific options",
.group_size = ARRAY_SIZE(local_options),
.group = local_options
};
struct if_applet ifctrstat_applet = {
.name = "ifctrstat",
.desc = "display statistics about an interface",
.main = ifctrstat_main,
.usage = "ifctrstat [options] <interface> <counter>\n ifctrstat [options] --list",
.manpage = "8 ifctrstat",
.groups = { &global_option_group, &local_option_group, NULL }
};

224
cmd/ifparse.c Normal file
View file

@ -0,0 +1,224 @@
/*
* cmd/ifparse.c
* Purpose: Redisplay /e/n/i in alternative formats.
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#define _GNU_SOURCE
#include <fnmatch.h>
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include "libifupdown/libifupdown.h"
#ifdef CONFIG_YAML
# include "libifupdown/yaml-base.h"
# include "libifupdown/yaml-writer.h"
#endif
#include "cmd/multicall.h"
#include "cmd/pretty-print-iface.h"
static bool show_all = false;
static bool allow_undefined = false;
static void
set_show_all(const char *arg)
{
(void) arg;
show_all = true;
}
static void
set_allow_undefined(const char *arg)
{
(void) arg;
allow_undefined = true;
}
static const char *output_fmt = "ifupdown";
static void
set_output_fmt(const char *arg)
{
output_fmt = arg;
}
static struct if_option local_options[] = {
{'F', "format", NULL, "output format to use", true, set_output_fmt},
{'A', "all", NULL, "show all interfaces", false, set_show_all},
{'U', "allow-undefined", NULL, "allow querying undefined (virtual) interfaces", false, set_allow_undefined},
};
static struct if_option_group local_option_group = {
.desc = "Program-specific options",
.group_size = ARRAY_SIZE(local_options),
.group = local_options
};
#ifdef CONFIG_YAML
static void
prettyprint_interface_yaml(struct lif_interface *iface)
{
struct lif_yaml_node doc = {};
lif_yaml_document_init(&doc, "interfaces");
struct lif_yaml_node *iface_node = lif_yaml_node_new_list(iface->ifname);
lif_yaml_node_append_child(&doc, iface_node);
if (iface->is_auto)
{
struct lif_yaml_node *iface_entry_node = lif_yaml_node_new_boolean("auto", true);
lif_yaml_node_append_child(iface_node, iface_entry_node);
}
struct lif_node *iter;
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
const char *value = entry->data;
char addr_buf[512];
if (!strcmp(entry->key, "address"))
{
struct lif_address *addr = entry->data;
if (!lif_address_unparse(addr, addr_buf, sizeof addr_buf, true))
continue;
value = addr_buf;
}
struct lif_yaml_node *iface_entry_node = lif_yaml_node_new_string(entry->key, value);
lif_yaml_node_append_child(iface_node, iface_entry_node);
}
lif_yaml_write(iface_node, stdout, true);
lif_yaml_node_free(&doc);
}
#endif
struct prettyprint_impl_map {
const char *name;
void (*handle)(struct lif_interface *iface);
};
struct prettyprint_impl_map pp_impl_map[] = {
{"ifupdown", prettyprint_interface_eni},
#ifdef CONFIG_YAML
{"yaml-raw", prettyprint_interface_yaml},
#endif
};
static int
pp_impl_cmp(const void *a, const void *b)
{
const char *key = a;
const struct prettyprint_impl_map *impl = b;
return strcmp(key, impl->name);
}
static int
ifparse_main(int argc, char *argv[])
{
struct lif_dict state = {};
struct lif_dict collection = {};
struct lif_interface_file_parse_state parse_state = {
.collection = &collection,
};
lif_interface_collection_init(&collection);
if (!lif_state_read_path(&state, exec_opts.state_file))
{
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.state_file);
return EXIT_FAILURE;
}
if (!lif_interface_file_parse(&parse_state, exec_opts.interfaces_file))
{
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.interfaces_file);
return EXIT_FAILURE;
}
if (match_opts.property == NULL && lif_lifecycle_count_rdepends(&exec_opts, &collection) == -1)
{
fprintf(stderr, "%s: could not validate dependency tree\n", argv0);
return EXIT_FAILURE;
}
if (!lif_compat_apply(&collection))
{
fprintf(stderr, "%s: failed to apply compatibility glue\n", argv0);
return EXIT_FAILURE;
}
struct prettyprint_impl_map *m = bsearch(output_fmt, pp_impl_map, ARRAY_SIZE(pp_impl_map), sizeof(*m), pp_impl_cmp);
if (m == NULL)
{
fprintf(stderr, "%s: %s: output format not supported\n", argv0, output_fmt);
return EXIT_FAILURE;
}
if (show_all)
{
struct lif_node *n;
LIF_DICT_FOREACH(n, &collection)
{
struct lif_dict_entry *entry = n->data;
m->handle(entry->data);
}
return EXIT_SUCCESS;
}
if (optind >= argc)
generic_usage(self_applet, EXIT_FAILURE);
int idx = optind;
for (; idx < argc; idx++)
{
struct lif_dict_entry *entry = lif_dict_find(&collection, argv[idx]);
struct lif_interface *iface = NULL;
if (entry != NULL)
iface = entry->data;
if (entry == NULL && allow_undefined)
iface = lif_interface_collection_find(&collection, argv[idx]);
if (iface == NULL)
{
fprintf(stderr, "%s: unknown interface %s\n", argv0, argv[idx]);
return EXIT_FAILURE;
}
m->handle(iface);
}
return EXIT_SUCCESS;
}
struct if_applet ifparse_applet = {
.name = "ifparse",
.desc = "redisplay interface configuration",
.main = ifparse_main,
.usage = "ifparse [options] <interfaces>\n ifparse [options] --all",
.manpage = "8 ifparse",
.groups = { &global_option_group, &match_option_group, &exec_option_group, &local_option_group },
};

View file

@ -20,50 +20,18 @@
#include <getopt.h>
#include "libifupdown/libifupdown.h"
#include "cmd/multicall.h"
#include "cmd/pretty-print-iface.h"
void
print_interface(struct lif_interface *iface)
{
if (iface->is_auto)
printf("auto %s\n", iface->ifname);
printf("iface %s\n", iface->ifname);
struct lif_node *iter;
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
if (!strcmp(entry->key, "address"))
{
struct lif_address *addr = entry->data;
char addr_buf[512];
if (!lif_address_unparse(addr, addr_buf, sizeof addr_buf, true))
{
printf(" # warning: failed to unparse address\n");
continue;
}
printf(" %s %s\n", entry->key, addr_buf);
}
else
printf(" %s %s\n", entry->key, (const char *) entry->data);
}
printf("\n");
}
void
static void
print_interface_dot(struct lif_dict *collection, struct lif_interface *iface, struct lif_interface *parent)
{
if (iface->is_up)
if (!lif_lifecycle_query_dependents(&exec_opts, iface, iface->ifname))
return;
if (parent != NULL)
printf("\"%s\" -> ", parent->ifname);
printf("\"%s (%zu)\" -> ", parent->ifname, parent->rdepends_count);
printf("\"%s\"", iface->ifname);
printf("\"%s (%zu)\"", iface->ifname, iface->rdepends_count);
printf("\n");
@ -80,51 +48,51 @@ print_interface_dot(struct lif_dict *collection, struct lif_interface *iface, st
{
struct lif_interface *child_if = lif_interface_collection_find(collection, tokenp);
if (child_if->is_pending)
continue;
child_if->is_pending = true;
print_interface_dot(collection, child_if, iface);
child_if->is_up = true;
child_if->is_pending = false;
}
}
void
ifquery_usage(void)
static void
print_interface_property(struct lif_interface *iface, const char *property)
{
fprintf(stderr, "usage: ifquery [options] <interfaces>\n");
fprintf(stderr, " ifquery [options] --list\n");
struct lif_node *iter;
bool printing_address = !strcmp(property, "address");
fprintf(stderr, "\nOptions:\n");
fprintf(stderr, " -h, --help this help\n");
fprintf(stderr, " -V, --version show this program's version\n");
fprintf(stderr, " -i, --interfaces FILE use FILE for interface definitions\n");
fprintf(stderr, " -L, --list list matching interfaces\n");
fprintf(stderr, " -a, --auto only match against interfaces hinted as 'auto'\n");
fprintf(stderr, " -I, --include PATTERN only match against interfaces matching PATTERN\n");
fprintf(stderr, " -X, --exclude PATTERN never match against interfaces matching PATTERN\n");
fprintf(stderr, " -P, --pretty-print pretty print the interfaces instead of just listing\n");
fprintf(stderr, " -S, --state-file FILE use FILE for state\n");
fprintf(stderr, " -s, --state show configured state\n");
fprintf(stderr, " -D, --dot generate a dependency graph\n");
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
exit(1);
if (strcmp(entry->key, property))
continue;
if (printing_address)
{
char addr_buf[512];
if (!lif_address_format_cidr(iface, entry, addr_buf, sizeof(addr_buf)))
continue;
printf("%s\n", addr_buf);
}
else
printf("%s\n", (const char *) entry->data);
}
}
struct match_options {
bool is_auto;
char *exclude_pattern;
char *include_pattern;
bool pretty_print;
bool dot;
};
void
static void
list_interfaces(struct lif_dict *collection, struct match_options *opts)
{
struct lif_node *iter;
if (opts->dot)
{
printf("digraph interfaces {\n");
printf("edge [color=blue fontname=Sans fontsize=10]\n");
printf("node [fontname=Sans fontsize=10]\n");
printf("digraph interfaces {\n"
"edge [color=blue fontname=Sans fontsize=10]\n"
"node [fontname=Sans fontsize=10]\n");
}
LIF_DICT_FOREACH(iter, collection)
@ -144,7 +112,7 @@ list_interfaces(struct lif_dict *collection, struct match_options *opts)
continue;
if (opts->pretty_print)
print_interface(iface);
prettyprint_interface_eni(iface);
else if (opts->dot)
print_interface_dot(collection, iface, NULL);
else
@ -155,7 +123,10 @@ list_interfaces(struct lif_dict *collection, struct match_options *opts)
printf("}\n");
}
void
static bool listing = false, listing_stat = false, listing_running = false;
static bool allow_undefined = false;
static void
list_state(struct lif_dict *state, struct match_options *opts)
{
struct lif_node *iter;
@ -172,106 +143,132 @@ list_state(struct lif_dict *state, struct match_options *opts)
fnmatch(opts->include_pattern, entry->key, 0))
continue;
printf("%s=%s\n", entry->key, (const char *) entry->data);
struct lif_state_record *rec = entry->data;
if (listing_running)
printf("%s\n", entry->key);
else
printf("%s=%s %zu%s\n", entry->key, rec->mapped_if, rec->refcount,
rec->is_explicit ? " explicit" : "");
}
}
int
static void
set_listing(const char *opt_arg)
{
(void) opt_arg;
listing = true;
}
static void
set_show_state(const char *opt_arg)
{
(void) opt_arg;
listing_stat = true;
}
static void
set_show_running(const char *opt_arg)
{
(void) opt_arg;
listing_running = true;
}
static void
set_pretty_print(const char *opt_arg)
{
(void) opt_arg;
match_opts.pretty_print = true;
}
static void
set_output_dot(const char *opt_arg)
{
(void) opt_arg;
match_opts.dot = true;
}
static void
set_property(const char *opt_arg)
{
match_opts.property = opt_arg;
}
static void
set_allow_undefined(const char *opt_arg)
{
(void) opt_arg;
allow_undefined = true;
}
static struct if_option local_options[] = {
{'r', "running", NULL, "show configured (running) interfaces", false, set_show_running},
{'s', "state", NULL, "show configured state", false, set_show_state},
{'p', "property", "property PROPERTY", "print values of properties matching PROPERTY", true, set_property},
{'D', "dot", NULL, "generate a dependency graph", false, set_output_dot},
{'L', "list", NULL, "list matching interfaces", false, set_listing},
{'P', "pretty-print", NULL, "pretty print the interfaces instead of just listing", false, set_pretty_print},
{'U', "allow-undefined", NULL, "allow querying undefined (virtual) interfaces", false, set_allow_undefined},
};
static struct if_option_group local_option_group = {
.desc = "Program-specific options",
.group_size = ARRAY_SIZE(local_options),
.group = local_options
};
static int
ifquery_main(int argc, char *argv[])
{
struct lif_dict state = {};
struct lif_dict collection = {};
struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{"interfaces", required_argument, 0, 'i'},
{"list", no_argument, 0, 'L'},
{"auto", no_argument, 0, 'a'},
{"include", required_argument, 0, 'I'},
{"exclude", required_argument, 0, 'X'},
{"pretty-print", no_argument, 0, 'P'},
{"state-file", required_argument, 0, 'S'},
{"state", no_argument, 0, 's'},
{"dot", no_argument, 0, 'D'},
{NULL, 0, 0, 0}
struct lif_interface_file_parse_state parse_state = {
.collection = &collection,
};
struct match_options match_opts = {};
bool listing = false, listing_stat = false;
char *interfaces_file = INTERFACES_FILE;
char *state_file = STATE_FILE;
for (;;)
lif_interface_collection_init(&collection);
if (!lif_state_read_path(&state, exec_opts.state_file))
{
int c = getopt_long(argc, argv, "hVi:LaI:X:PS:sD", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
ifquery_usage();
break;
case 'V':
lif_common_version();
break;
case 'i':
interfaces_file = optarg;
break;
case 'L':
listing = true;
break;
case 'a':
match_opts.is_auto = true;
break;
case 'I':
match_opts.include_pattern = optarg;
break;
case 'X':
match_opts.exclude_pattern = optarg;
break;
case 'P':
match_opts.pretty_print = true;
break;
case 'S':
state_file = optarg;
break;
case 's':
listing_stat = true;
break;
case 'D':
match_opts.dot = true;
break;
}
}
if (!lif_state_read_path(&state, state_file))
{
fprintf(stderr, "%s: could not parse %s\n", argv0, state_file);
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.state_file);
return EXIT_FAILURE;
}
if (!lif_interface_file_parse(&collection, interfaces_file))
if (!lif_interface_file_parse(&parse_state, exec_opts.interfaces_file))
{
fprintf(stderr, "%s: could not parse %s\n", argv0, interfaces_file);
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.interfaces_file);
return EXIT_FAILURE;
}
if (match_opts.property == NULL && lif_lifecycle_count_rdepends(&exec_opts, &collection) == -1)
{
fprintf(stderr, "%s: could not validate dependency tree\n", argv0);
return EXIT_FAILURE;
}
if (!lif_compat_apply(&collection))
{
fprintf(stderr, "%s: failed to apply compatibility glue\n", argv0);
return EXIT_FAILURE;
}
/* --list --state is not allowed */
if (listing && listing_stat)
ifquery_usage();
if (listing && (listing_stat || listing_running))
generic_usage(self_applet, EXIT_FAILURE);
if (listing)
{
list_interfaces(&collection, &match_opts);
return EXIT_SUCCESS;
}
else if (listing_stat)
else if (listing_stat || listing_running)
{
list_state(&state, &match_opts);
return EXIT_SUCCESS;
}
if (optind >= argc)
ifquery_usage();
generic_usage(self_applet, EXIT_FAILURE);
int idx = optind;
for (; idx < argc; idx++)
@ -284,6 +281,9 @@ ifquery_main(int argc, char *argv[])
if (entry != NULL)
iface = entry->data;
if (entry == NULL && allow_undefined)
iface = lif_interface_collection_find(&collection, argv[idx]);
}
if (iface == NULL)
@ -292,7 +292,10 @@ ifquery_main(int argc, char *argv[])
return EXIT_FAILURE;
}
print_interface(iface);
if (match_opts.property != NULL)
print_interface_property(iface, match_opts.property);
else
prettyprint_interface_eni(iface);
}
return EXIT_SUCCESS;
@ -300,6 +303,9 @@ ifquery_main(int argc, char *argv[])
struct if_applet ifquery_applet = {
.name = "ifquery",
.desc = "query interface configuration",
.main = ifquery_main,
.usage = ifquery_usage
.usage = "ifquery [options] <interfaces>\n ifquery [options] --list",
.manpage = "8 ifquery",
.groups = { &global_option_group, &match_option_group, &exec_option_group, &local_option_group },
};

View file

@ -3,6 +3,7 @@
* Purpose: bring interfaces up or down
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -18,38 +19,15 @@
#include <stdio.h>
#include <string.h>
#include <getopt.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include "libifupdown/libifupdown.h"
#include "cmd/multicall.h"
struct match_options {
bool is_auto;
char *exclude_pattern;
char *include_pattern;
};
static bool up;
static struct lif_execute_opts exec_opts = {};
void
ifupdown_usage(void)
{
fprintf(stderr, "usage: %s [options] <interfaces>\n", argv0);
fprintf(stderr, "\nOptions:\n");
fprintf(stderr, " -h, --help this help\n");
fprintf(stderr, " -V, --version show this program's version\n");
fprintf(stderr, " -i, --interfaces FILE use FILE for interface definitions\n");
fprintf(stderr, " -S, --state-file FILE use FILE for state\n");
fprintf(stderr, " -a, --auto only match against interfaces hinted as 'auto'\n");
fprintf(stderr, " -I, --include PATTERN only match against interfaces matching PATTERN\n");
fprintf(stderr, " -X, --exclude PATTERN never match against interfaces matching PATTERN\n");
fprintf(stderr, " -n, --no-act do not actually run any commands\n");
fprintf(stderr, " -v, --verbose show what commands are being run\n");
exit(1);
}
bool
static bool
is_ifdown()
{
if (strstr(argv0, "ifdown") != NULL)
@ -58,9 +36,135 @@ is_ifdown()
return false;
}
bool
change_interface(struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *ifname)
static int
acquire_state_lock(const char *state_path, const char *lifname)
{
if (exec_opts.mock || exec_opts.no_lock)
return -1;
char lockpath[4096] = {};
snprintf(lockpath, sizeof lockpath, "%s.%s.lock", state_path, lifname);
int fd = open(lockpath, O_CREAT | O_WRONLY | O_TRUNC, 0600);
if (fd < 0)
{
if (exec_opts.verbose)
fprintf(stderr, "%s: while opening lockfile %s: %s\n", argv0, lockpath, strerror(errno));
return -2;
}
int flags = fcntl(fd, F_GETFD);
if (flags < 0)
{
close(fd);
if (exec_opts.verbose)
fprintf(stderr, "%s: while getting flags for lockfile: %s\n", argv0, strerror(errno));
return -2;
}
flags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1)
{
close(fd);
if (exec_opts.verbose)
fprintf(stderr, "%s: while setting lockfile close-on-exec: %s\n", argv0, strerror(errno));
return -2;
}
struct flock fl = {
.l_type = F_WRLCK,
.l_whence = SEEK_SET
};
if (exec_opts.verbose)
fprintf(stderr, "%s: acquiring lock on %s\n", argv0, lockpath);
if (fcntl(fd, F_SETLK, &fl) == -1)
{
close(fd);
if (exec_opts.verbose)
fprintf(stderr, "%s: while locking lockfile: %s\n", argv0, strerror(errno));
return -2;
}
return fd;
}
static bool
skip_interface(struct lif_interface *iface, const char *ifname, struct lif_dict *state, bool update_state)
{
if (iface->is_template)
{
fprintf(stderr, "%s: cannot change state on %s (template interface)\n", argv0, ifname);
return false;
}
if (iface->has_config_error)
{
if (exec_opts.force)
{
fprintf(stderr, "%s: (de)configuring interface %s despite config errors\n", argv0, ifname);
return false;
}
else
{
fprintf(stderr, "%s: skipping interface %s due to config errors\n", argv0, ifname);
return true;
}
}
if (exec_opts.force)
return false;
if (up && iface->refcount > 0)
{
if (exec_opts.verbose)
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 %sinterface %s (already deconfigured), use --force to force deconfiguration\n",
argv0, iface->is_auto ? "auto " : "", ifname);
return true;
}
return false;
}
static bool
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);
if (lockfd == -2)
{
fprintf(stderr, "%s: could not acquire exclusive lock for %s: %s\n", argv0, ifname, strerror(errno));
return false;
}
if (skip_interface(iface, ifname, state, update_state))
{
if (lockfd != -1)
close(lockfd);
return true;
}
if (exec_opts.verbose)
{
fprintf(stderr, "%s: changing state of interface %s to '%s'\n",
@ -71,13 +175,26 @@ change_interface(struct lif_interface *iface, struct lif_dict *collection, struc
{
fprintf(stderr, "%s: failed to change interface %s state to '%s'\n",
argv0, ifname, up ? "up" : "down");
if (lockfd != -1)
close(lockfd);
return false;
}
if (lockfd != -1)
close(lockfd);
if (up && update_state)
{
iface->is_explicit = true;
lif_state_upsert(state, ifname, iface);
}
return true;
}
bool
static bool
change_auto_interfaces(struct lif_dict *collection, struct lif_dict *state, struct match_options *opts)
{
struct lif_node *iter;
@ -98,83 +215,68 @@ 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;
}
return true;
}
int
static int
update_state_file_and_exit(int rc, struct lif_dict *state)
{
if (exec_opts.mock)
{
exit(rc);
return rc;
}
if (!lif_state_write_path(state, exec_opts.state_file))
{
fprintf(stderr, "%s: could not update %s\n", argv0, exec_opts.state_file);
exit(EXIT_FAILURE);
return EXIT_FAILURE;
}
exit(rc);
return rc;
}
static int
ifupdown_main(int argc, char *argv[])
{
up = !is_ifdown();
struct lif_dict state = {};
struct lif_dict collection = {};
struct option long_options[] = {
{"help", no_argument, 0, 'h'},
{"version", no_argument, 0, 'V'},
{"interfaces", required_argument, 0, 'i'},
{"auto", no_argument, 0, 'a'},
{"include", required_argument, 0, 'I'},
{"exclude", required_argument, 0, 'X'},
{"state-file", required_argument, 0, 'S'},
{"no-act", no_argument, 0, 'n'},
{"verbose", no_argument, 0, 'v'},
{NULL, 0, 0, 0}
struct lif_interface_file_parse_state parse_state = {
.collection = &collection,
};
struct match_options match_opts = {};
char *interfaces_file = INTERFACES_FILE;
char *state_file = STATE_FILE;
for (;;)
lif_interface_collection_init(&collection);
if (!lif_state_read_path(&state, exec_opts.state_file))
{
int c = getopt_long(argc, argv, "hVi:aI:X:S:nv", long_options, NULL);
if (c == -1)
break;
switch (c) {
case 'h':
ifupdown_usage();
break;
case 'V':
lif_common_version();
break;
case 'i':
interfaces_file = optarg;
break;
case 'a':
match_opts.is_auto = true;
break;
case 'I':
match_opts.include_pattern = optarg;
break;
case 'X':
match_opts.exclude_pattern = optarg;
break;
case 'S':
state_file = optarg;
break;
case 'n':
exec_opts.mock = true;
exec_opts.verbose = true;
break;
case 'v':
exec_opts.verbose = true;
break;
}
}
if (!lif_state_read_path(&state, state_file))
{
fprintf(stderr, "%s: could not parse %s\n", argv0, state_file);
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.state_file);
return EXIT_FAILURE;
}
if (!lif_interface_file_parse(&collection, interfaces_file))
if (!lif_interface_file_parse(&parse_state, exec_opts.interfaces_file))
{
fprintf(stderr, "%s: could not parse %s\n", argv0, interfaces_file);
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.interfaces_file);
return EXIT_FAILURE;
}
if (lif_lifecycle_count_rdepends(&exec_opts, &collection) == -1)
{
fprintf(stderr, "%s: could not validate dependency tree\n", argv0);
return EXIT_FAILURE;
}
if(!lif_compat_apply(&collection))
{
fprintf(stderr, "%s: failed to apply compatibility glue\n", argv0);
return EXIT_FAILURE;
}
@ -187,12 +289,12 @@ ifupdown_main(int argc, char *argv[])
if (match_opts.is_auto)
{
if (!change_auto_interfaces(&collection, &state, &match_opts))
return EXIT_FAILURE;
return update_state_file_and_exit(EXIT_FAILURE, &state);
return EXIT_SUCCESS;
return update_state_file_and_exit(EXIT_SUCCESS, &state);
}
else if (optind >= argc)
ifupdown_usage();
generic_usage(self_applet, EXIT_FAILURE);
int idx = optind;
for (; idx < argc; idx++)
@ -218,33 +320,33 @@ ifupdown_main(int argc, char *argv[])
if (entry == NULL)
{
fprintf(stderr, "%s: unknown interface %s\n", argv0, argv[idx]);
return EXIT_FAILURE;
return update_state_file_and_exit(EXIT_FAILURE, &state);
}
iface = entry->data;
}
if (!change_interface(iface, &collection, &state, ifname))
return EXIT_FAILURE;
if (!change_interface(iface, &collection, &state, ifname, true))
return update_state_file_and_exit(EXIT_FAILURE, &state);
}
if (!exec_opts.mock && !lif_state_write_path(&state, state_file))
{
fprintf(stderr, "%s: could not update %s\n", argv0, state_file);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
return update_state_file_and_exit(EXIT_SUCCESS, &state);
}
struct if_applet ifup_applet = {
.name = "ifup",
.desc = "bring interfaces up",
.main = ifupdown_main,
.usage = ifupdown_usage
.usage = "ifup [options] <interfaces>",
.manpage = "8 ifup",
.groups = { &global_option_group, &match_option_group, &exec_option_group, },
};
struct if_applet ifdown_applet = {
.name = "ifdown",
.desc = "take interfaces down",
.main = ifupdown_main,
.usage = ifupdown_usage
.usage = "ifdown [options] <interfaces>",
.manpage = "8 ifdown",
.groups = { &global_option_group, &match_option_group, &exec_option_group, },
};

View file

@ -0,0 +1,103 @@
/*
* cmd/multicall-exec-options.c
* Purpose: multi-call binary frontend -- option handling
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#define _GNU_SOURCE
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "cmd/multicall.h"
#define DEFAULT_TIMEOUT 300
struct lif_execute_opts exec_opts = {
.interfaces_file = INTERFACES_FILE,
.executor_path = EXECUTOR_PATH,
.state_file = STATE_FILE,
.timeout = DEFAULT_TIMEOUT,
};
static void
set_interfaces_file(const char *opt_arg)
{
exec_opts.interfaces_file = opt_arg;
}
static void
set_state_file(const char *opt_arg)
{
exec_opts.state_file = opt_arg;
}
static void
set_no_act(const char *opt_arg)
{
(void) opt_arg;
exec_opts.mock = true;
exec_opts.verbose = true;
}
static void
set_verbose(const char *opt_arg)
{
(void) opt_arg;
exec_opts.verbose = true;
}
static void
set_executor_path(const char *opt_arg)
{
exec_opts.executor_path = opt_arg;
}
static void
set_no_lock(const char *opt_arg)
{
(void) opt_arg;
exec_opts.no_lock = true;
}
static void
set_force(const char *opt_arg)
{
(void) opt_arg;
exec_opts.force = true;
}
static void
set_timeout(const char *opt_arg)
{
exec_opts.timeout = atoi(opt_arg);
if (exec_opts.timeout < 0)
exec_opts.timeout = DEFAULT_TIMEOUT;
}
static struct if_option exec_options[] = {
{'f', "force", NULL, "force (de)configuration", false, set_force},
{'i', "interfaces", "interfaces FILE", "use FILE for interface definitions", true, set_interfaces_file},
{'l', "no-lock", NULL, "do not use a lockfile to serialize state changes", false, set_no_lock},
{'n', "no-act", NULL, "do not actually run any commands", false, set_no_act},
{'v', "verbose", NULL, "show what commands are being run", false, set_verbose},
{'E', "executor-path", "executor-path PATH", "use PATH for executor directory", true, set_executor_path},
{'S', "state-file", "state-file FILE", "use FILE for state", true, set_state_file},
{'T', "timeout", "timeout TIMEOUT", "wait TIMEOUT seconds for executors to complete", true, set_timeout},
};
struct if_option_group exec_option_group = {
.desc = "Execution",
.group_size = ARRAY_SIZE(exec_options),
.group = exec_options
};

View file

@ -0,0 +1,55 @@
/*
* cmd/multicall-match-options.c
* Purpose: multi-call binary frontend -- option handling
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#define _GNU_SOURCE
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "cmd/multicall.h"
struct match_options match_opts = {};
static void
set_auto(const char *opt_arg)
{
(void) opt_arg;
match_opts.is_auto = true;
}
static void
set_include_pattern(const char *opt_arg)
{
match_opts.include_pattern = opt_arg;
}
static void
set_exclude_pattern(const char *opt_arg)
{
match_opts.exclude_pattern = opt_arg;
}
static struct if_option match_options[] = {
{'a', "auto", NULL, "only match against interfaces hinted as 'auto'", false, set_auto},
{'I', "include", "include PATTERN", "only match against interfaces matching PATTERN", true, set_include_pattern},
{'X', "exclude", "exclude PATTERN", "never match against interfaces matching PATTERN", true, set_exclude_pattern},
};
struct if_option_group match_option_group = {
.desc = "Matching interfaces",
.group_size = ARRAY_SIZE(match_options),
.group = match_options
};

162
cmd/multicall-options.c Normal file
View file

@ -0,0 +1,162 @@
/*
* cmd/multicall-options.c
* Purpose: multi-call binary frontend -- option handling
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#define _GNU_SOURCE
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "cmd/multicall.h"
extern const struct if_applet *self_applet;
void
generic_usage(const struct if_applet *applet, int result)
{
fprintf(stderr, "%s", applet->name);
if (applet->desc != NULL)
fprintf(stderr, " - %s", applet->desc);
fprintf(stderr, "\n");
if (applet->usage != NULL)
fprintf(stderr, "\nUsage:\n %s\n", applet->usage);
size_t iter;
for (iter = 0; applet->groups[iter] != NULL; iter++)
{
const struct if_option_group *group = applet->groups[iter];
fprintf(stderr, "\n%s:\n", group->desc);
size_t group_iter;
for (group_iter = 0; group_iter < group->group_size; group_iter++)
{
const struct if_option *opt = &group->group[group_iter];
fprintf(stderr, " ");
if (opt->short_opt)
fprintf(stderr, "-%c", opt->short_opt);
else
fprintf(stderr, " ");
if (opt->long_opt)
fprintf(stderr, "%c --%-30s", opt->short_opt ? ',' : ' ',
opt->long_opt_desc ? opt->long_opt_desc : opt->long_opt);
else
fprintf(stderr, "%34s", "");
fprintf(stderr, "%s\n", opt->desc);
}
}
if (applet->manpage != NULL)
fprintf(stderr, "\nFor more information: man %s\n", applet->manpage);
exit(result);
}
static void
generic_usage_request(const char *opt_arg)
{
(void) opt_arg;
generic_usage(self_applet, EXIT_SUCCESS);
}
static struct if_option global_options[] = {
{'h', "help", NULL, "this help", false, generic_usage_request},
{'V', "version", NULL, "show this program's version", false, (void *) lif_common_version},
};
struct if_option_group global_option_group = {
.desc = "Global options",
.group_size = ARRAY_SIZE(global_options),
.group = global_options
};
const struct if_option *
lookup_option(const struct if_applet *applet, int opt)
{
size_t iter;
for (iter = 0; applet->groups[iter] != NULL; iter++)
{
const struct if_option_group *group = applet->groups[iter];
size_t group_iter;
for (group_iter = 0; group_iter < group->group_size; group_iter++)
{
const struct if_option *option = &group->group[group_iter];
if (option->short_opt == opt)
return option;
}
}
return NULL;
}
void
process_options(const struct if_applet *applet, int argc, char *argv[])
{
char short_opts[256] = {}, *p = short_opts;
struct option long_opts[256] = {};
size_t iter, long_opt_iter = 0;
for (iter = 0; applet->groups[iter] != NULL; iter++)
{
const struct if_option_group *group = applet->groups[iter];
size_t group_iter;
for (group_iter = 0; group_iter < group->group_size; group_iter++)
{
const struct if_option *opt = &group->group[group_iter];
if (opt->short_opt)
{
*p++ = opt->short_opt;
if (opt->require_argument)
*p++ = ':';
}
if (opt->long_opt)
{
/* XXX: handle long-opts without short-opts */
long_opts[long_opt_iter] = (struct option) {
.name = opt->long_opt,
.has_arg = opt->require_argument ? required_argument : no_argument,
.val = opt->short_opt
};
long_opt_iter++;
}
}
}
for (;;)
{
int c = getopt_long(argc, argv, short_opts, long_opts, NULL);
if (c == -1)
break;
const struct if_option *opt = lookup_option(applet, c);
if (opt == NULL)
break;
opt->handle(optarg);
}
}

View file

@ -13,29 +13,56 @@
* from the use of this software.
*/
#define _GNU_SOURCE
#include <libgen.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include "cmd/multicall.h"
char *argv0;
#ifdef CONFIG_IFQUERY
extern struct if_applet ifquery_applet;
#endif
#ifdef CONFIG_IFUPDOWN
extern struct if_applet ifup_applet;
extern struct if_applet ifdown_applet;
#endif
#ifdef CONFIG_IFCTRSTAT
extern struct if_applet ifctrstat_applet;
#endif
#ifdef CONFIG_IFPARSE
extern struct if_applet ifparse_applet;
#endif
struct if_applet ifupdown_applet;
const struct if_applet *self_applet = NULL;
struct if_applet *applet_table[] = {
#ifdef CONFIG_IFCTRSTAT
&ifctrstat_applet,
#endif
#ifdef CONFIG_IFUPDOWN
&ifdown_applet,
#endif
#ifdef CONFIG_IFPARSE
&ifparse_applet,
#endif
#ifdef CONFIG_IFQUERY
&ifquery_applet,
#endif
#ifdef CONFIG_IFUPDOWN
&ifup_applet,
#endif
&ifupdown_applet,
};
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
int
static int
applet_cmp(const void *a, const void *b)
{
const char *key = a;
@ -44,13 +71,17 @@ applet_cmp(const void *a, const void *b)
return strcmp(key, applet->name);
}
void multicall_usage(void);
void multicall_usage(int status) __attribute__((noreturn));
struct if_applet ifupdown_applet;
int
main(int argc, char *argv[])
{
argv0 = basename(argv[0]);
struct if_applet **app;
const struct if_applet **app;
lif_config_load(CONFIG_FILE);
app = bsearch(argv0, applet_table,
ARRAY_SIZE(applet_table), sizeof(*applet_table),
@ -59,42 +90,48 @@ main(int argc, char *argv[])
if (app == NULL)
{
fprintf(stderr, "%s: applet not found\n", argv0);
multicall_usage();
multicall_usage(EXIT_FAILURE);
}
return (*app)->main(argc, argv);
self_applet = *app;
if (self_applet != &ifupdown_applet)
process_options(*app, argc, argv);
return self_applet->main(argc, argv);
}
int
static int
multicall_main(int argc, char *argv[])
{
if (argc < 2)
multicall_usage();
multicall_usage(EXIT_FAILURE);
return main(argc - 1, argv + 1);
}
void
multicall_usage(void)
multicall_usage(int status)
{
fprintf(stderr, "usage: ifupdown <applet> [options]\n");
fprintf(stderr,
PACKAGE_NAME " " PACKAGE_VERSION "\n"
"usage: ifupdown <applet> [options]\n"
"\n"
"Built-in applets:\n");
fprintf(stderr, "\nBuilt-in applets:\n\t");
for (size_t i = 0; i < ARRAY_SIZE(applet_table); i++)
{
if (i != 0)
fprintf(stderr, ", ");
if (applet_table[i] == &ifupdown_applet)
continue;
fprintf(stderr, "%s", applet_table[i]->name);
fprintf(stderr, " %-10s %s\n", applet_table[i]->name, applet_table[i]->desc);
}
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
exit(status);
}
struct if_applet ifupdown_applet = {
.name = "ifupdown",
.main = multicall_main,
.usage = multicall_usage,
.groups = { &global_option_group, NULL }
};

View file

@ -13,17 +13,62 @@
* from the use of this software.
*/
#include <stdbool.h>
#ifndef IFUPDOWN_CMD_MULTICALL_H__GUARD
#define IFUPDOWN_CMD_MULTICALL_H__GUARD
#include "libifupdown/libifupdown.h"
struct if_applet;
struct if_option {
char short_opt;
const char *long_opt;
const char *long_opt_desc;
const char *desc;
bool require_argument;
void (*const handle)(const char *opt_arg);
};
struct if_option_group {
const char *desc;
size_t group_size;
const struct if_option *group;
};
struct if_applet {
const char *name;
const char *desc;
const char *usage;
const char *manpage;
int (*const main)(int argc, char *argv[]);
void (*const usage)(void);
const struct if_option_group *groups[16];
};
extern char *argv0;
extern const struct if_applet *self_applet;
extern struct if_option_group global_option_group;
struct match_options {
bool is_auto;
const char *exclude_pattern;
const char *include_pattern;
bool pretty_print;
bool dot;
const char *property;
};
extern struct match_options match_opts;
extern void process_options(const struct if_applet *applet, int argc, char *argv[]);
extern const struct if_option *lookup_option(const struct if_applet *applet, int opt);
extern struct if_option_group match_option_group;
extern struct lif_execute_opts exec_opts;
extern struct if_option_group exec_option_group;
void generic_usage(const struct if_applet *applet, int result);
#endif

56
cmd/pretty-print-iface.c Normal file
View file

@ -0,0 +1,56 @@
/*
* cmd/pretty-print-iface.c
* Purpose: interface pretty-printer (/e/n/i style)
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <stdio.h>
#include <string.h>
#include "libifupdown/libifupdown.h"
#include "cmd/multicall.h"
#include "cmd/pretty-print-iface.h"
void
prettyprint_interface_eni(struct lif_interface *iface)
{
if (!lif_lifecycle_query_dependents(&exec_opts, iface, iface->ifname))
return;
if (iface->is_auto)
printf("auto %s\n", iface->ifname);
printf("%s %s\n", iface->is_template ? "template" : "iface", iface->ifname);
struct lif_node *iter;
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
if (!strcmp(entry->key, "address"))
{
struct lif_address *addr = entry->data;
char addr_buf[512];
if (!lif_address_unparse(addr, addr_buf, sizeof addr_buf, true))
{
printf(" # warning: failed to unparse address\n");
continue;
}
printf(" %s %s\n", entry->key, addr_buf);
}
else
printf(" %s %s\n", entry->key, (const char *) entry->data);
}
printf("\n");
}

23
cmd/pretty-print-iface.h Normal file
View file

@ -0,0 +1,23 @@
/*
* cmd/pretty-print-iface.h
* Purpose: interface pretty-printer (/e/n/i style)
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#ifndef IFUPDOWN_CMD_PRETTY_PRINT_IFACE_H__GUARD
#define IFUPDOWN_CMD_PRETTY_PRINT_IFACE_H__GUARD
#include "libifupdown/libifupdown.h"
extern void prettyprint_interface_eni(struct lif_interface *iface);
#endif

View file

@ -0,0 +1,15 @@
[Unit]
Description=ifupdown-ng networking initialization
Documentation=man:interfaces(5) man:ifup(8) man:ifdown(8)
[Service]
Type=oneshot
RemainAfterExit=yes
SyslogIdentifier=networking
TimeoutStopSec=30s
ExecStart=/usr/share/ifupdown-ng/sbin/networking start
ExecStop=/usr/share/ifupdown-ng/sbin/networking stop
ExecRestart=/usr/share/ifupdown-ng/sbin/networking restart
[Install]
WantedBy=basic.target network.target multi-user.target network-online.target

68
dist/debian/networking vendored Normal file
View file

@ -0,0 +1,68 @@
#!/bin/sh
#
# Wrapper script for networking set up and teardown via unit file
#
# Thu, 01 Oct 2020 22:47:43 +0200
# -- Maximilian Wilhelm <max@sdn.clinic>
#
STATE_DIR="/run/ifsate"
# Make sure the state dir is present
if [ ! -d "${STATE_DIR}" ]; then
mkdir "${STATE_DIR}"
fi
# Check for require binaries
if [ ! -x /sbin/ifup -o ! -x /sbin/ifdown ]; then
echo "ifup and/or ifdown not found!" >&2
exit 1
fi
# Apply defaults if present (verbose mode, kill switch, etc.)
CONFIGURE_INTERFACES=yes
if [ -f /etc/default/networking ]; then
. /etc/default/networking
fi
ARGS=""
if [ "${VERBOSE}" = yes ]; then
ARGS="-v"
fi
# Let's go
case "$1" in
start)
if [ "${CONFIGURE_INTERFACES}" = no ]; then
echo "Not configuring network interfaces, see /etc/default/networking"
exit 0
fi
ifup -a ${ARGS}
;;
stop)
if [ "${SKIP_DOWN_AT_SYSRESET}" = "yes" ] && systemctl list-jobs | egrep -q '(shutdown|reboot|halt|poweroff)\.target'; then
echo ${NAME}':' "Skipping deconfiguring network interfaces"
exit 0
fi
ifdown -a ${ARGS}
;;
restart)
ifupdown_init
ifdown -a ${ARGS}
ifup -a ${ARGS}
;;
# reload missing here!
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
exit 0

13
dist/debian/networking.default vendored Normal file
View file

@ -0,0 +1,13 @@
#
# Defaults for ifupdown-ng networking service
#
# Change the below to "yes" if you want ifup/ifdown and it's executors to be
# verbose about what's going on
VERBOSE="no"
# Set to "yes" if you want to skip deconfiguring all interfaces during system
# reboot and shutdown. This might be of interest in large scale deployments,
# where you might not want to wait for interface deconfiguration to speed up
# shutdown/reboot.
SKIP_DOWN_AT_SYSRESET="yes"

56
dist/ifupdown-ng.conf.example vendored Normal file
View file

@ -0,0 +1,56 @@
# This is an example configuration file for ifupdown-ng, which allows
# the system administrator to configure the behaviour of ifupdown-ng.
#
# The settings specified here are the defaults of ifupdown-ng.
# allow_addon_scripts:
# Enable support for /etc/if-X.d addon scripts. These are used for
# compatibility with legacy setups, and may be disabled for performance
# improvements in setups where only ifupdown-ng executors are used.
# Valid values are 0 and 1, the default is 1.
allow_addon_scripts = 1
# allow_any_iface_as_template:
# Enable any interface to act as a template for another interface.
# This is presently the default, but is deprecated. An admin may choose
# to disable this setting in order to require inheritance from specified
# templates. Valid values are 0 and 1, the default is 1.
allow_any_iface_as_template = 1
# auto_executor_selection:
# Automatically determine which executors are needed to bring up an
# interface. An admin may choose to disable this setting and explicitly
# define which executors to use with `use` statements.
# Valid values are 0 and 1, the default is 1.
auto_executor_selection = 1
# compat_create_interfaces:
# Denotes where or not to create interfaces when compat_* settings are
# active and it would be necessary to create an interface to be fully
# compliant. This could happen when inheriting bridge VLAN settings to
# an interface within a bridges bridge-ports setting but no interface
# stanza is found. Valid values are 0 and 1, the default is 1.
compat_create_interfaces = 1
# compat_ifupdown2_bridge_ports_inherit_vlans:
# In ifupdown2 <bridge-vids> as well as the <bridge-pvid> set on a bridge
# interface will be inherited by all member ports if not set explicitly.
# When set to 1 ifupdown-ng behaves the same way and will internally copy
# both options from the bridge member ports if they are not set on the
# member port. Valid values are 0 and 1, the default is 1.
compat_ifupdown2_bridge_ports_inherit_vlans = 1
# implicit_template_conversion:
# In some legacy configs, a template may be declared as an iface, and
# ifupdown-ng automatically converts those declarations to a proper
# template. If this setting is disabled, inheritance will continue to
# work against non-template interfaces without converting them to a
# template. Valid values are 0 and 1, the default is 1.
implicit_template_conversion = 1
# use_hostname_for_dhcp:
# Automatically learn the hostname property, used for DHCP configuration
# by querying the system hostname using uname(2). This is basically
# equivalent to `hostname $(hostname)` without having to specify any
# configuration. Valid values are 0 and 1, the default is 1.
use_hostname_for_dhcp = 1

8
dist/openrc/networking.confd vendored Normal file
View file

@ -0,0 +1,8 @@
# Sets the path used for the interface definitions.
#cfgfile="/etc/network/interfaces"
# Sets the path used for the state database.
#ifstate="/run/ifstate"
# Skip taking down networking while shutting down.
#keep_network="YES"

70
dist/openrc/networking.initd vendored Normal file
View file

@ -0,0 +1,70 @@
#!/sbin/openrc-run
: ${cfgfile:="/etc/network/interfaces"}
ifstate=/run/ifstate
single_iface="${RC_SVCNAME#*.}"
if [ "$single_iface" = "$RC_SVCNAME" ]; then
single_iface=
fi
depend() {
need localmount
want dev-settle
after bootmisc hwdrivers modules
provide net
keyword -jail -prefix -vserver -docker
}
# find interfaces we want to start
find_ifaces() {
if [ -n "$single_iface" ]; then
echo $single_iface
else
ifquery -L -a -i "$cfgfile"
fi
}
# return the list of interfaces we should try stop
find_running_ifaces() {
if [ -n "$single_iface" ]; then
echo $single_iface
else
ifquery -r -i "$cfgfile" -S "$ifstate"
fi
}
start() {
local iface= ret=1
ebegin "Starting networking"
eindent
for iface in $(find_ifaces); do
local r=0
ebegin "$iface"
if ! ifup -i "$cfgfile" -S "$ifstate" $iface >/dev/null; then
ifdown -f -i "$cfgfile" -S "$ifstate" $iface >/dev/null 2>&1
r=1
fi
# atleast one interface needs to be started for action
# to be success
eend $r && ret=0
done
eoutdent
return $ret
}
stop() {
local iface=
# Don't stop the network at shutdown.
yesno ${keep_network:-YES} && yesno $RC_GOINGDOWN && return 0
ebegin "Stopping networking"
eindent
for iface in $(find_running_ifaces); do
ebegin "$iface"
ifdown -i "$cfgfile" -S "$ifstate" -f $iface >/dev/null
eend $?
done
eoutdent
return 0
}

275
doc/ADMIN-GUIDE.md Normal file
View file

@ -0,0 +1,275 @@
# ifupdown-ng for system administrators
ifupdown-ng is a network device manager which is backwards
compatible with traditional ifup and ifdown as used on Debian
and Alpine systems, while solving many design deficits with
the original approach through robust error handling and the
use of a dependency-solver to determine interface bring-up
order.
This guide is intended to walk users through using the
ifupdown-ng system without any assumption of familiarity
with the legacy ifupdown system.
## Important Filesystem Paths
The ifupdown-ng system uses the following paths, ranked
in order of importance:
* `/etc/network/interfaces`: the interface configuration
database, which contains information about what
interfaces should be configured.
* `/etc/network/ifupdown-ng.conf`: the main configuration
file which controls ifupdown-ng's behaviour. See the
*ifupdown-ng Configuration* section below.
* `/run/ifstate`: the interface state file, which denotes
what physical interfaces are configured, and what
interface definition they are configured as.
* `/usr/libexec/ifupdown-ng`: this directory contains the
native ifupdown-ng executors, which are run as necessary
to configure an interface. See the ifupdown-executor(7)
manual page for more information on how these programs
are written.
* `/etc/network/if-{up|down|pre-up|post-down}.d`:
these directories contain scripts that are run when an
interface is brought up or down. In general, they follow
the same contract described in ifupdown-executor(7).
All configuration examples in this guide concern the
`/etc/network/interfaces` file.
## ifupdown-ng Configuration
ifupdown-ng allows to configure some parts of it's behaviour.
Currently the following settings are supported in
`/etc/network/ifupdown-ng.conf`:
* `allow_addon_scripts`: Enable support for /etc/if-X.d addon scripts.
These are used for compatibility with legacy setups, and may be
disabled for performance improvements in setups where only
ifupdown-ng executors are used. Valid values are `0` and `1`,
default is `1`.
* `allow_any_iface_as_template`: Enable any interface to act as a
template for another interface. This is presently the default,
but is deprecated. An admin may choose to disable this setting
in order to require inheritance from specified templates.
Valid values are `0` and `1`, the default is `1`.
* `auto_executor_selection`: Automatically select executors based
on the presence of their config options. An admin may choose to
disable this setting in order to require explicitly enabling
executors through `use` statements. Valid values are `0` and `1`,
the default is `1`.
* `compat_create_interfaces`:
Denotes where or not to create interfaces when compat\_* settings are
active and it would be necessary to create an interface to be fully
compliant. This could happen when inheriting bridge VLAN settings to
an interface within a bridges bridge-ports setting but no interface
stanza is found. Valid values are `0` and `1`, the default is `1`.
* `compat_ifupdown2_bridge_ports_inherit_vlans`: In ifupdown2 `bridge-vids`
as well as the <bridge-pvid> set on a bridge interface will be inherited
by all member ports if not set explicitly. When set to `1` ifupdown-ng
behaves the same way and will internally copy both options from the
bridge member ports if they are not set on the member port.
Valid values are `0` and `1`, the default is `1`.
* `implicit_template_conversion`: In some legacy configs, a template
may be declared as an iface, and ifupdown-ng automatically converts
those declarations to a proper template. If this setting is
disabled, inheritance will continue to work against non-template
interfaces without converting them to a template. Valid values
are `0` and `1`, the default is `1`.
* `use_hostname_for_dhcp`: A common configuration pattern with DHCP
interfaces is to use `hostname $(hostname)`. If this setting is
enabled, the `hostname` property will default to the system
hostname. Valid values are `0` and `1`, the default is `1`.
## Interface Configuration
### Basic Configuration
To begin with, lets look at a basic configuration for a
desktop computer. This scenario involves using the DHCP
helper to learn an IPv4 address dynamically.
In this case, the `/etc/network/interfaces` file would
look like:
```
auto eth0
iface eth0
use dhcp
```
These configuration statements do two things: designate
that `eth0` should be started automatically with the `auto`
keyword, and designate that the `dhcp` executor should be
used to configure the interface.
As a more detailed explanation, here is a commented version:
```
# Start eth0 automatically.
auto eth0
# Begin an interface definition for eth0.
iface eth0
# Use the dhcp executor to configure eth0.
use dhcp
```
### IPv6 RA Configuration
With IPv6, stateless auto-configuration is typically used to
configure network interfaces. If you are not interested in
using IPv4 at all, you can simply use the `ipv6-ra` executor
to ensure that an interface is configured to accept IPv6 RA
advertisements:
```
auto eth0
iface eth0
use ipv6-ra
```
### Static Configuration
We can use the `static` executor to configure static IPv4 and
IPv6 addresses. If you use the `address` keyword, the `static`
executor will automatically be used to configure the interface:
```
auto eth0
iface eth0
address 203.0.113.2/24
gateway 203.0.113.1
```
#### Multiple Addresses
A typical scenario on servers is where a server has multiple
IP addresses on a single interface. In this case you simply
add additional `address` lines like this:
```
auto eth0
iface eth0
address 203.0.113.2/24
address 203.0.113.3/24
address 203.0.113.4/24
gateway 203.0.113.1
```
#### Dual-stack configurations
Another typical scenario for servers is to run a dual-stack
configuration, where interfaces have both an IPv4 and an IPv6
address. This is accomplished in a similar way as multi-homing.
You specify the IPv4 and IPv6 addresses you want, followed by
gateways for each:
```
auto eth0
iface eth0
address 203.0.113.2/24
address 203.0.113.3/24
address 203.0.113.4/24
gateway 203.0.113.1
address 2001:db8:1000:2::2/64
address 2001:db8:1000:2::3/64
address 2001:db8:1000:2::4/64
gateway 2001:db8:1000:2::1
```
## Relationships
As previously mentioned, ifupdown-ng features a dependency
resolver that allows for determining the interface configuration
order.
![Dependency resolution example](img/dependency-resolution.png)
In order to make use of this, dependencies can be managed in one
of two ways:
### Explicit dependency management using `requires`
The `requires` keyword can be used to manage explicit
dependencies:
```
auto eth0
iface eth0
use dhcp
auto gre0
iface gre0
requires eth0
use gre
gre-endpoint 203.0.113.2
gre-ttl 255
gre-flags ignore-df
address 203.0.113.194/30
gateway 203.0.113.193
```
### Implicit dependency management using executors
Executors can declare implicit dependencies which work the same
way as explicit dependencies, but are learned at run-time, for
example:
```
auto bond0
iface bond0
use bond
bond-members eth0 eth1
[...]
```
Is with respect to dependency equivalent to:
```
auto bond0
iface bond0
use bond
requires eth0 eth1
[...]
```
## Executors
The ifupdown-ng system is expanded with additional features via
executors. Executors are selected on a per-interface basis using
`use` statements, for example:
```
auto eth0
iface eth0
use dhcp
```
Executors are run in the order specified by the `use` statements.
Some executors are automatically added based on other statements
in an interface definition. To see the full list of executors
used for an interface, use the ifquery(8) command.
## Questions
If you have further questions about how to use ifupdown-ng to
configure a specific scenario, drop by the
[ifupdown-ng IRC channel](irc://irc.oftc.net/#ifupdown-ng).

39
doc/ifctrstat.scd Normal file
View file

@ -0,0 +1,39 @@
ifctrstat(8)
# NAME
ifctrstat - display interface statistics
# SYNOPSIS
*ifctrstat* [<_options_>...] <_interface_> <_counter_>
*ifctrstat* [<_options_>...] -L|--list
# DESCRIPTION
*ifctrstat* is used to query interface statistic counters in
a kernel-agnostic way. This is useful for heterogenous
environments where multiple kernels may be in use.
# OPTIONS
*-h, --help*
Display supported options to ifctrstat.
*-n, --no-label*
Display the requested counter without its label.
*-L, --list*
List available counters on this system.
*-V, --version*
Print the ifupdown-ng version and exit.
# SEE ALSO
*ifquery*(8)
# AUTHORS
A. Wilcox <awilfox@adelielinux.org>

68
doc/ifdown.scd Normal file
View file

@ -0,0 +1,68 @@
ifdown(8)
# NAME
ifdown - take interfaces down
# SYNOPSIS
ifdown [<_options_>...] <_interfaces_>
# DESCRIPTION
*ifdown* is used to deconfigure interfaces according to how they are
configured in the configuration database.
# OPTIONS
*-a, --auto*
Only match interfaces that are marked as _auto_.
*-f, --force*
Force deconfiguration of the interface.
This option exists for compatibility with other implementations.
*-h, --help*
Display supported options to ifquery.
*-i, --interfaces* _FILE_
Use _FILE_ as the config database.
*-n, --no-act*
Show what commands would be run instead of actually running
them. Useful for testing configuration changes.
*-v, --verbose*
Show what commands are being run as they are executed.
*-E, --executor-path* _PATH_
Look for executors in the given _PATH_.
*-I, --include* _PATTERN_
Include _PATTERN_ when matching against the config or state
database.
*-S, --state-file* _FILE_
Use _FILE_ as the state database.
*-T, --timeout* _TIMEOUT_
Wait up to _TIMEOUT_ seconds for executors to complete before
raising an error.
*-V, --version*
Print the ifupdown-ng version and exit.
*-X, --exclude* _PATTERN_
Exclude _PATTERN_ when matching against the config or state
database.
# SEE ALSO
*ifupdown-ng.conf*(5)
*ifup*(8)
*ifquery*(8)
*interfaces*(5)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

66
doc/ifparse.scd Normal file
View file

@ -0,0 +1,66 @@
ifparse(8)
# NAME
ifparse - redisplay interface configuration in different formats
# SYNOPSIS
*ifparse* [<_options_>...] <_interfaces_...>
*ifparse* -A|--all
# DESCRIPTION
*ifparse* is used to extract information from the interface configuration
file. It is intended to be used to translate the interface configuration
stanzas between different formats.
# OPTIONS
*-a, --auto*
Only match interfaces that are marked as _auto_.
*-h, --help*
Display supported options to ifquery.
*-i, --interfaces* _FILE_
Use _FILE_ as the config database.
*-F, --format* _FORMAT_
Use _FORMAT_ to determine what format to use. *ifupdown* and
*yaml-raw* formats are available.
*-I, --include* _PATTERN_
Include _PATTERN_ when matching against the config or state
database.
*-U, --allow-undefined*
Create virtual interfaces for any interfaces not explicitly
defined in the configuration file. This is primarily useful
for property queries.
*-S, --state-file* _FILE_
Use _FILE_ as the state database.
*-T, --timeout* _TIMEOUT_
Wait up to _TIMEOUT_ seconds for executors to complete before
raising an error.
*-V, --version*
Print the ifupdown-ng version and exit.
*-X, --exclude* _PATTERN_
Exclude _PATTERN_ when matching against the config or state
database.
# SEE ALSO
*ifup*(8)++
*ifdown*(8)++
*ifquery*(8)++
*interfaces*(5)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

84
doc/ifquery.scd Normal file
View file

@ -0,0 +1,84 @@
ifquery(8)
# NAME
ifquery - query interface configuration and state
# SYNOPSIS
*ifquery* [<_options_>...] <_interfaces_...>
*ifquery* -L|--list
*ifquery* -s|--state
# DESCRIPTION
*ifquery* is used to extract information from the interface configuration
file. It can also be used to convert from old versions of the interface
configuration file to the current format.
# OPTIONS
*-a, --auto*
Only match interfaces that are marked as _auto_.
*-h, --help*
Display supported options to ifquery.
*-i, --interfaces* _FILE_
Use _FILE_ as the config database.
*-p, --property* _PROPERTY_
Print the values of matching properties for an interface.
*-r, --running*
Print the interface names that are marked as running in
the state database.
*-s, --state*
Query the state database instead of the config database.
*-D, --dot*
Generate a dependency graph that can be used with GraphViz
*dot*(1). Used with *--list*.
*-I, --include* _PATTERN_
Include _PATTERN_ when matching against the config or state
database.
*-L, --list*
List interfaces which exist in the configuration database.
*-P, --pretty-print*
When listing interfaces, print their configuration in a format
that is compatible with *interfaces*(5) files.
*-U, --allow-undefined*
Create virtual interfaces for any interfaces not explicitly
defined in the configuration file. This is primarily useful
for property queries.
*-S, --state-file* _FILE_
Use _FILE_ as the state database.
*-T, --timeout* _TIMEOUT_
Wait up to _TIMEOUT_ seconds for executors to complete before
raising an error.
*-V, --version*
Print the ifupdown-ng version and exit.
*-X, --exclude* _PATTERN_
Exclude _PATTERN_ when matching against the config or state
database.
# SEE ALSO
*ifup*(8)++
*ifdown*(8)++
*interfaces*(5)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

57
doc/ifstate.scd Normal file
View file

@ -0,0 +1,57 @@
ifstate(5)
# NAME
*/run/ifstate* - interface state database
# DESCRIPTION
The */run/ifstate* file describes the present state of the interface
configuration -- namely, how many interfaces are up, how many
dependencies each interface has which are up (the refcount), and
whether or not an interface was explicitly brought up due to request
or configuration.
# FILE SYNTAX
At a minimum, the */run/ifstate* file contains at least one column,
the physical to logical interface mapping. This column is formatted
as such:
```
lo=lo
eth0=eth0
wlan0=work
```
The left side of the mapping is the physical interface, while the right
side is the logical interface. This field is required to be present.
The next field is the reference count. This is a number that reflects
the number of *active* dependencies an interface has. As interfaces
are brought up and down, the refcount may change. This field is
optional.
The final field denotes whether or not an interface was brought up
explicitly -- either by being marked as _auto_ or brought up manually
using *ifup*(8). The contents of this field if present is the
_explicit_ keyword.
# EXAMPLES
An example from a typical system with localhost, eth0 and a wireguard
VPN:
```
lo=lo 1 explicit
eth0=eth0 2 explicit
wg0=wg0 1 explicit
```
# SEE ALSO
*interfaces*(5)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

71
doc/ifup.scd Normal file
View file

@ -0,0 +1,71 @@
ifup(8)
# NAME
ifup - bring interfaces up
# SYNOPSIS
ifup [<_options_>...] <_interfaces_>
# DESCRIPTION
*ifup* is used to configure interfaces according to how they are
configured in the configuration database.
# OPTIONS
*-a, --auto*
Only match interfaces that are marked as _auto_.
*-f, --force*
Force configuration of the interface.
This option exists for compatibility with other implementations.
*-h, --help*
Display supported options to ifquery.
*-i, --interfaces* _FILE_
Use _FILE_ as the config database.
*-n, --no-act*
Show what commands would be run instead of actually running
them. Useful for testing configuration changes.
*-v, --verbose*
Show what commands are being run as they are executed.
*-E, --executor-path* _PATH_
Look for executors in the given _PATH_.
*-I, --include* _PATTERN_
Include _PATTERN_ when matching against the config or state
database.
*-L, --no-lock*
Do not use a lockfile to serialize state changes.
*-S, --state-file* _FILE_
Use _FILE_ as the state database.
*-T, --timeout* _TIMEOUT_
Wait up to _TIMEOUT_ seconds for executors to complete before
raising an error.
*-V, --version*
Print the ifupdown-ng version and exit.
*-X, --exclude* _PATTERN_
Exclude _PATTERN_ when matching against the config or state
database.
# SEE ALSO
*ifupdown-ng.conf*(5)
*ifdown*(8)
*ifquery*(8)
*interfaces*(5)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

92
doc/ifupdown-executor.scd Normal file
View file

@ -0,0 +1,92 @@
ifupdown-executor(7)
# NAME
*/usr/libexec/ifupdown-ng/program* - ifupdown executor protocol
# DESCRIPTION
The ifupdown executors are programs that are typically installed
into the ifupdown-ng executor path. They follow a specific
protocol documented in this man page.
# PHASES
Executors are run to react to nine different phases and are not
required to take any specific action. These phases are:
*depend*
Called to determine if the executor wishes to change
the dependency graph. The executor should write a
space-delimited list of interface names it is dependent
upon to _stdout_. Those interface names will be merged
into the dependency graph. If an executor does not have
any dependencies, it may simply exit 0 without doing
anything.
*create*
Called before *pre-up*, to explicitly allow for interface
creation if necessary.
*pre-up*
Called before the interface is going to be brought up.
*up*
Called when the interface is being brought up.
*post-up*
Called after the interface was successfully brought up.
*pre-down*
Called before the interface is going to be taken down.
*down*
Called when the interface is being taken down.
*post-down*
Called after the interface was successfully taken down.
*destroy*
Called after *post-down* to allow for explicitly
destroying an interface if necessary.
# ENVIRONMENT
Executors are guaranteed to run with a core set of environment
variables:
*IFACE*
The name of the interface being configured.
*INTERFACES_FILE*
The path to the interfaces database file being used.
*MODE*
Either _start_, _stop_ or _depend_ depending on phase.
This environment variable is present for compatibility
with legacy ifupdown scripts and should not be used in
ifupdown-ng executors.
*PHASE*
The phase being executed. See the phases section for
more information about phases.
*VERBOSE*
If present, verbose output is expected from the
executor.
Additionally, the properties associated with an interface are
provided to executors. The keys are rewritten to begin with
IF_ and are capitalized with dashes converted to underscores.
For example, the property _bridge-ports_ will be rewritten as
_IF_BRIDGE_PORTS_.
# SEE ALSO
ifup(8)++
ifdown(8)++
interfaces(5)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

76
doc/ifupdown-ng.conf.scd Normal file
View file

@ -0,0 +1,76 @@
ifupdown-ng.conf(5)
# NAME
*ifupdown-ng.conf* - Global configuration file for ifupdown-ng
# DESCRIPTION
ifupdown-ng allows to configure some parts of it's behaviour via global
configuration options.
# GENERAL CONFIGURATION OPTIONS
*allow_addon_scripts* _bool_
Enable support for /etc/if-X.d addon scripts. These are used for
compatibility with legacy setups, and may be disabled for
performance improvements in setups where only ifupdown-ng executors
are used. Valid values are _0_ and _1_, the default is _1_.
*auto_executor_selection* _bool_
Automatically determine which executors to use. At present, this
is done by inserting `use` statements for the namespace a config
option has. The namespace is separated from the config option with
a dash (`-`). Valid values are _0_ and _1_, the default is _1_.
*use_hostname_for_dhcp* _bool_
Automatically learn the hostname property, used for DHCP
configuration by querying the system hostname using uname(2).
This is basically equivalent to `hostname $(hostname)` without
having to specify any configuration. Valid values are _0_ and
_1_, the default is _1_.
# TEMPLATE RELATED OPTIONS
*allow_any_iface_as_template* _bool_
Enable any interface to act as a template for another interface.
This is presently the default, but is deprecated. An admin may
choose to disable this setting in order to require inheritance
from specified templates. Valid values are _0_ and _1_, the
default is _1_.
*implicit_template_conversion* _bool_
In some legacy configs, a template may be declared as an iface, and
ifupdown-ng automatically converts those declarations to a proper
template. If this setting is disabled, inheritance will continue
to work against non-template interfaces without converting them to
a template. Valid values are _0_ and _1_, the default is _1_.
# COMPATIBILITY RELATED OPTIONS
*compat_create_interfaces* _bool_
Denotes where or not to create interfaces when compat_\* settings are
active and it would be necessary to create an interface to be fully
compliant. This could happen when inheriting bridge VLAN settings to
an interface within a bridges bridge-ports setting but no interface
stanza is found. Valid values are _0_ and _1_, the default is _1_.
compat_ifupdown2_bridge_ports_inherit_vlans _bool_
In ifupdown2 <bridge-vids> as well as the <bridge-pvid> set on a
bridge interface will be inherited by all member ports if not set
explicitly. When set to 1 ifupdown-ng behaves the same way and will
internally copy both options from the bridge member ports if they
are not set on the member port. Valid values are _0_ and _1_, the
default is _1_.
# FILES
/etc/network/ifupdown-ng.conf
# SEE ALSO
*interfaces*(5)
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

87
doc/interfaces-batman.scd Normal file
View file

@ -0,0 +1,87 @@
interfaces-batman(5)
# NAME
*interfaces-batman* - B.A.T.M.A.N. adv. extensions for the interfaces(5)
file format
# DESCRIPTION
Better Approach To Mobile Ad-Hoc Networking (B.A.T.M.A.N.) advanced is
a mesh protocol which provides an Ethernet overlay network over an
Ethernet underlay. The overlay interface is called _meshif_ whereas
underlay interfaces are called _hardif_.
It's supported in the Linux kernel and thus available in many Linux
environments. The ifupdown-ng exectuor relies on the *batctl* tool
being installed. Support for setting interface based hop-penalties
required Linux Kernel 5.8 or later.
B.A.T.M.A.N. adv. adds 30-60 bytes of encapsulation overhead depending
on wether netword coding is activated or not. This should be taken into
consideration when setting up overlay networks, particularly on underlay
networks with a conventional 1500 byte MTU.
See https://www.open-mesh.org/projects/open-mesh/wiki for more details
and updates.
The following options allow to set up B.A.T.M.A.N. adv. interfaces.
# BATMAN-RELATED OPTIONS
*batman-ifaces* _list of interfaces_
Specifies the underlay interfaces (hardifs) which should be
configured for the B.A.T.M.A.N. adv. meshif defined within
the iface stanza.
*batman-hop-penalty* _hop-penalty_
The _hop-penalty_ defines the cost of traversing a node or an
interface. The _hop-penalty_ is a numeric value between 0 and
255. Historically a _hop-penalty_ could only be set on a meshif,
since B.A.T.M.A.N adv. v2020.3 (included in Kernel 5.8) it can
also be set on a per-interfaces (hardif) basis.
*batman-gw-mode* _gw-mode_
Denotes the gateway mode which controls the role this node will
play within this B.A.T.M.A.N. adv. instance. The mode can be
_off_, _client_, or _server_.
*batman-distributed-arp-table* _mode_
Activates or deactivates the Distributed ARP table (DAT) within
this B.A.T.M.A.N. adv. instance. Valid values are _enable_ and
_disable_.
*batman-multicast-mode* _mode_
Activates or deactivates the multicast mode of this B.A.T.M.A.N.
adv. instance. Valid values are _enable_ and _disable_.
# EXAMPLES
A B.A.T.M.A.N. adv. _meshif_:
```
auto bat-pad-cty
iface bat-pad-cty
batman-ifaces dummy-pad-cty vlan1234
batman-hop-penalty 5
#
hwaddress f2:00:c1:01:00:00
mtu 1500
```
A B.A.T.M.A.N. adv. member interfaces (_hardif_):
```
auto vlan1234
iface vlan1234
mtu 1560
batman-hop-penalty 10
```
# SEE ALSO
*batctl*(8)
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

204
doc/interfaces-bond.scd Normal file
View file

@ -0,0 +1,204 @@
interfaces-bond(5)
# NAME
*interfaces-bond* - Bonding/LAG extensions for the interfaces(5) file format
# DESCRIPTION
The Linux implementation for Ling Aggregation Groups (LAGs) is called
_bonding_, whereas a LAG interface is called _bond_. The Linux bonding
implementation supports active/passive setups, classical EtherChannels
as well as LACP (802.3ad).
The following options set up bonding/LAG interfaces with ifupdown-ng.
See https://www.kernel.org/doc/Documentation/networking/bonding.rst and
for more information.
# BOND-RELATED OPTIONS
A bond interface must have at least one member port set. All other
options are optional.
*bond-members* _list of interfaces_
Denotes the physical member interfaces to form this LAG. For
compatiblity to ifupdown1 and ifupdown2 _slaves_ as well as
_bond-slaves_ are an alias for this option. This option is
required.
*bond-mode* _mode_
Denotes the mode for this LAG. The _mode_ can be given as string
or as numerical value. Valid values are _balance-rr_ (0),
_active-backup_ (1), _balance-xor_ (2), _broadcast_ (3),
_802.3ad_ (4), _balance-tlb_ (5), _balance-alb_ (6).
The default is _balance-rr_.
*bond-xmit-hash-policy* _policy_
Denotes the hash policy/algorithm used to distribute packets
across the physical links. This only applies for modes
_balance-alb_, _balance-tlb_, _balance-xor_, and _802.3ad_.
The _policy_ can be given as string or as numerical value.
Valid values are _layer2_ (0), _layer3+4_ (1), _layer2+3_ (2),
_encap2+3_ (3), and _encap3+4_ (4). The default is _layer2_.
*bond-min-links* _number_
Denotes the minimum number of available links before turning on
carrier.
*bond-miimon* _interval_
Denotes the MII link monitoring frequency in milliseconds.
This determines how often the link state of each slave is
inspected for link failures. A value of zero disables MII
link monitoring. The default is 0.
*bond-use-carrier* _bool_
Denotes wether miimon uses MII or ethtool ioctls vs. the
netif_carrier_ok() call to determine member link status.
A value of 1 enables the use of netif_carrier_ok(), a value of
0 will use the deprecated MII / ETHTOOL ioctls. The default
is 1.
*bond-updelay* _delay_
Denotes the delay in milliseconds before considering link up,
in milliseconds. The default is 0.
*bond-downdelay* _delay_
Denotes the delay in milliseconds before considering link down,
in milliseconds. The default is 0.
*bond-all-slaves-active* _bool_
Denotes wether duplicate frames (received on inactive ports)
should be dropped (0) or delivered (1). The default is 0.
*bond-packets-per-slave* _num_packets_
Denotes the number of packets to transmit through a member
before moving to the next one. When set to 0 then a slave is
chosen at random. The valid range is 0 - 65535; the default
value is 1. This option has effect only in balance-rr mode.
*bond-lp-interval* _interval_
Denotes the interval in seconds between sending learning packets
to each members peer switch. The valid range is 1 - 0x7fffffff;
the default value is 1. This option has effect only in modes
balance-tlb and balance-alb.
*bond-resend-igmp* _number_
Denotes the number of IGMP membership reports to send after a
link failover happend. The valid range is 0 - 255; a value of
0 prevents the IGMP membership report from being issued in
response to the failover event. The default is 1.
This option is useful for bonding modes balance-rr, active-backup
balance-tlb and balance-alb, in which a failover can switch the
IGMP traffic from one slave to another.
# LACP-RELATED OPTIONS
The following options are only valid in LACP (802.3ad) mode.
*bond-lacp-rate* _rate_
Denotes the _rate_ of LACPDU requested from the peer. The _rate_
can be given as string or as numerical value. Valid values are
slow (0) and fast (1). The default is slow.
*bond-ad-select* _mode_
Denotes the 802.3ad aggregation selection logic. The _mode_ can
be given as string or as numerical value. Valid values are
_stable_ (0), _bandwidth_ (1) and _cound_ (2). The default is
_stable_.
*bond-ad-actor-sys-prio* _priority_
Denotes the LACP system priority. The allowed range is 1 - 65535.
The default value is 65535.
*bond-ad-user-port-key* _key_
Denotes the upper 10 bits of the port-key. he values can be from
0 - 1023. The default is 0.
# ACTIVE/BACKUP-RELATED OPTIONS
The following options are only valid in active/passive setups.
*bond-primary* _interface_
Denotes the primary member interface The specified device will
always be the active slave while it is available. The primary
option is only valid for active-backup, balance-tlb and
balance-alb mode.
*bond-primary-reselect* _policy_
Denotes the reselection policy for the primary member interface.
Valid values are _always_ (0), _better_ (1), and _failure_ (2).
The default is _always_.
*bond-fail-over-mac* _mode_
Denotes whether active-backup mode should set all member
interfaces to the same MAC address at enslavement (the
traditional behavior), or, when enabled, perform special
handling of the bond's MAC address in accordance with the
selected policy. Valid values are _none_ (0), _active_ (1),
_follow_ (2). The default is _none_.
*bond-num-grat-arp* _count_
Denotes the number of peer notifications (gratuitous ARPs and
unsolicited IPv6 Neighbor Advertisements) to be issued after a
failover event. The valid range is 0 - 255; the default is 1.
*bond-num-unsol-na* _count_
This is an alias for _bond-num-grat-arp_
*bond-peer-notif-delay* _interval_
Denotes the interval in milliseconds, between each peer
notification (gratuitous ARP and unsolicited IPv6 Neighbor
Advertisement) issued after a failover event. The default
is 0 which means to match the value of the link monitor
interval.
# ARP-RELATED OPTIONS
The following options configure ARP link monitoring.
The ARP monitor works by periodically checking the slave
devices to determine whether they have sent or received
traffic recently. Regular traffic is generated via ARP
probes issued for the addresses specified by the
_bond-arp-ip-target_ option.
*bond-arp-interval* _interval_
Denotes the frequency in milliseconds to send ARP probes.
*bond-arp-ip-target* _IPv4 address_
Denotes the IP addresses to use as ARP monitoring peers when
_bond-arp-interval_ is > 0.
*bond-arp-validate* _mode_
Specifies whether or not ARP probes and replies should be
validated in any mode that supports arp monitoring, or whether
non-ARP traffic should be filtered (disregarded) for link
monitoring purposes. Valid values are _none_ (0), _active_ (1),
_backup_ (2), _all_ (3), _filter_ (4), _filter_active_ (5), and
_filter_backup_ (6). The default is _none_.
*bond-arp-all-targets* _mode_
Denotes the number of _bond-arp-ip-targets_ that have to be
reachable to consider the member interface to be up. Valid
options are _any_ (0) and _all_ (1). The default is _any_.
# EXAMPLES
A bond using two links and LACP (802.3ad):
```
auto bond0
iface bond0
bond-members eth0 eth1
bond-mode 802.3ad
bond-xmit-hash-policy layer3+4
bond-min-links 1
#
address 192.0.2.42/24
address 2001:db8::42/64
```
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

173
doc/interfaces-bridge.scd Normal file
View file

@ -0,0 +1,173 @@
interfaces-bridge(5)
# NAME
*interfaces-bridge* - Bridge extensions for the interfaces(5) file format
# DESCRIPTION
Linux has support for Ethernet bridging interfaces which act like an
Ethernet switch within the Linux Kernel. The following options allow
to set up Ethernet bridges and adding configured interfaces to bridges.
See *ip-link*(8) for more details about the options listed below.
# BRIDGE-RELATED OPTIONS
*bridge-ports* _list of interfaces_
A space separated list of interfaces which should be configured
as member interfaces of this bridge. This option must be set
for the bridge to be configured.
*bridge-hw* _MAC address_
Denotes the _MAC address_ the bridge should use.
*bridge-ageing* _seconds_
Denotes the time in seconds after which a MAC address will be
removed from the Forwarding DataBase (FDB) after not having
seen a frame with this source address.
*bridge-vlan-aware* _bool_
Denotes wether or not the bridge should be aware of 802.1q VLANs.
_bool_ can be given as _yes_/_no_ or _0_/_1_. The defaul is _no_.
See related options for configuring vlan-aware bridges, below.
# SPANNING TREE RELATED BRIDGE OPTIONS
*bridge-stp* _state_
Activates or deactivates IEEE 802.1d Spanning Tree Protocol
(STP) support of the bridge. Valid values are _on_/_off_.
*bridge-bridgeprio* _priority_
Sets the bridge's priority to _priority_. The priority value is
a number between 0 and 65535. Lower priority values are better.
The bridge with the lowest priority will be elected _root
bridge_.
*bridge-fd* _seconds_
Denotes the bridge forward delay in seconds. Valid values are
between 2 and 30.
*bridge-hello* _seconds_
Denotes the bridge hello time in seconds. Valid values are
between 1 and 10.
*bridge-maxage* _seconds_
Denotes the seconds until another bridge is considerd dead
after reception of its last STP hello message. Valid values
are between 6 and 40.
# OPTIONS FOR VLAN-AWARE-BRIDGES
The following options only have an effect on vlan-aware bridges and
their ports.
All settings can be applied on the bridge interface itself and all member
port iface stanzas. If applied on the bridge interface they take effect
for the bridge interface itself and might be inherited to _bridge-ports_
depending on the compatibility settings configured in *ifupdown-ng.conf*(5).
Configuring VLAN options on the bridge interface might be required for
setting up a VLAN interface to one of the VLANs carried within the bridge.
See the EXAMPLES section for an example for this scenario.
See *ifupdown-ng.conf*(5) for more information about compatiblity settings
mentioned below.
*bridge-access* _vlan ID_
Configure the given _vlan ID_ for untagged ingress and egress
on this interface. The common description for this kind of
configuration is called "access port".
*bridge-pvid* _vlan ID_
Denotes the _vlan ID_ to considered a PVID at ingress.
Any untagged frames received on this interface will be
assigned to this _vlan ID_. The default PVID is _1_.
If compatibility to ifupdown2 bridge port inheritance is active
a _bridge-pvid_ set on the bridge will be inherited to any
interface configured in _bridge-ports_ without a _bridge-pvid_ set.
*bridge-vids* _list of vlan IDs_
Denotes the space separated list of VLANs to be allowed tagged
ingress/egress on this interface.
If compatibility to ifupdown2 bridge port inheritance is active
a _bridge-vids_ set on the bridge will be inherited to any
interface configured in _bridge-ports_ without _bridge-vids_ set.
*bridge-allow-untagged* _bool_
Denotes wether or not the bridge should allow untagged frames on
ingress as well as egress. If set to _no_ untagged frames will be
droppped on ingress and none will be sent. _bool_ can be given as
_yes_/_no_ or _0_/_1_. The defaul is _yes_.
# EXAMPLES
A simple layer 2 only bridge:
```
auto br0
iface br0
bridge-ports eth0 veth-vm1 tap0
bridge-fd 0
bridge-stp off
```
A bridge with layer 3 configuration:
```
auto br0
iface br0
bridge-ports eth0 veth-vm1 tap0
bridge-fd 0
bridge-stp off
#
address 192.0.2.42/24
address 2001:db8::42/64
```
A layer 2 only vlan-aware bridge:
```
auto bond0
iface bond0
bond-members eth0 eth1
bridge-vids 23 42 84 1337
auto br0
iface br0
bridge-ports bond0
```
A vlan-aware bridge with a VLAN interface on top:
```
auto eth0
iface eth0
bridge-vids 23 42 84 1337
auto br0
iface br0
bridge-ports eth0
bridge-vlan-aware yes
bridge-vids 42
auto vlan42
iface vlan42
vlan-raw-device br0
#
address 192.0.2.42/24
address 2001:db8::42/64
```
# SEE ALSO
*interfaces*(5)
*ifupdown-ng.conf*(5)
*ip-link*(8)
*bridge*(8)
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

View file

@ -0,0 +1,48 @@
interfaces-forward(5)
# NAME
*interfaces-forward* - forwarding vocabulary for the interfaces(5) file format
# DESCRIPTION
Linux allows for configuration of IP packet forwarding behavior on a protocol
and interface basis. The following options allow for this configuration.
# FORWARDING-RELATED OPTIONS
The forward executor will only modify the sysctl configuration if these options
are provided, otherwise other mechanisms such as /etc/sysctl.conf may be used.
*forward-ipv4* _yes|no_
Whether the interface should forward unicast IPv4 packets.
*forward-ipv6* _yes|no_
Whether the interface should forward unicast IPv6 packets.
*forward-ipv4-mc* _yes|no_
Whether the interface should forward multicast IPv4 packets.
*forward-ipv6-mc* _yes|no_
Whether the interface should forward multicast IPv6 packets.
# EXAMPLES
The typical home router scenario will want to forward both IPv4 and IPv6
packets:
```
iface WAN
use dhcp
forward-ipv4 yes
forward-ipv6 yes
iface LAN
address 192.168.0.1/24
forward-ipv4 yes
forward-ipv6 yes
```
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

40
doc/interfaces-mpls.scd Normal file
View file

@ -0,0 +1,40 @@
interfaces-mpls(5)
# NAME
*interfaces-mpls* - MPLS vocabulary for the interfaces(5) file format
# DESCRIPTION
Linux allows has support for MultiProtocol Label Switching (MPLS) for a while
now. The following options allow for this configuration.
# MPLS-RELATED OPTIONS
The MPLS executor will only modify the sysctl configuration if these options
are provided, otherwise other mechanisms such as /etc/sysctl.conf may be used.
If MPLS is enabled on (at least) one interface the executor will load the
_mpls_iptunnel_ kernel module.
Be aware that you have to set the _platform_labels_ sysctl to make MPLS work.
See https://www.kernel.org/doc/Documentation/networking/mpls-sysctl.rst for
more details on the MPLS related knobs in the Linux kernel.
*mpls-enable* _yes|no_
Control whether packets can be input on this interface. If disabled,
packets carrying an MPLS label will be discarded without further
processing.
# EXAMPLES
```
iface eth0
address 2001:db8:08:15::42/64
#
mpls-enable yes
```
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

37
doc/interfaces-ppp.scd Normal file
View file

@ -0,0 +1,37 @@
interfaces-ppp(5)
# NAME
*interfaces-ppp* - PPP extensions for the interfaces(5) file format
# DESCRIPTION
The Point-to-Point Protocol (PPP) usually is used for dial-up lines,
most common for over Digital Subscriber Lines (DSL) to connect to an
Internet Service Provider (ISP). The following options allow to set
up PPP dial-up connections.
# PPP-RELATED OPTIONS
*ppp-provider* _provider_
Denotes the file name of the _provider_ configuration file
within the _/etc/ppp/peers/_ directory which should be used
to set up the PPP connection.
*ppp-physdev* _interfaces_
Denotes the physical (underlay) interface which is used to
set up the PPP connection.
# EXAMPLES
A PPP connection to _local-ISP_:
```
auto ppp0
iface ppp0
ppp-provider local-ISP
```
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

161
doc/interfaces-tunnel.scd Normal file
View file

@ -0,0 +1,161 @@
interfaces-tunnel(5)
# NAME
*interfaces-tunnel* - Tunnel extensions for the interfaces(5) file format
# DESCRIPTION
The following options set up tunneling interfaces with ifupdown-ng.
# TUNNEL-RELATED OPTIONS
A tunnel interface must have a mode, remote IP and a local IP or device
set, all other options are optional.
*tunnel-mode* _mode_
Denotes the mode for this tunnel. Basically all tunnel modes supported
by Linux / iproute2 are supported as well. This includes but is not
limited to _gre_/_gretap_, _ip6gre_/_ip6gretap_, _ipip_/_ip6ip_/_sit_.
*tunnel-local* _IP_
Denotes the IP address used as the local tunnel endpoint. According
to the _tunnel-mode_ an IPv4 or IPv6 address has to be given.
For compatiblity to ifupdown1 _local_ is an alias for this option.
*tunnel-local-dev* _interface_
When the local IP address the tunnel should be established from isn't
static and therefore might change (e.g. configured by DHCP or PPP) it
might be desireable to just use the address configured on _interface_.
When _tunnel-local-dev_ is given instead of _tunnel-local_ ifupdown-ng
will try to determine the IP address set on the given _interface_ with
respect to the address family required to set up a tunnel of the given
_mode_ and use this to set up the tunnel.
*tunnel-remote* _IP_
Denotes the IP address used as the remote tunnel endpoint. According
to the _tunnel-mode_ an IPv4 or IPv6 address has to be given.
For compatiblity to ifupdown1 _endpoint_ is an alias for this option.
*tunnel-physdev* _interface_
Denotes the _interface_ the encapsulated packets should be sent out by.
This comes in handy when using VRFs to denote that the local tunnel
endpoint should be terminated in VRF _interface_ or the VRF associated
with _interface_.
Note: Depending on the _mode_ of the tunnel either the VRF interface
or the real underlay interface may have to given as _interface_.
*tunnel-ttl* _ttl_
Denotes the TTL value to use in outgoing packets. _ttl_ is a number in the
range 1 - 255 whereas 0 is a special value meaning that packets inherit the
TTL value. The default for IPv4 tunnels is to inherit the TTL, for IPv6
tunnels it's 64. For compatiblity to ifupdown1 _ttl_ is an alias for this option.
# IPIP/SIT-RELATED OPTIONS
*tunnel-encap* _encap_
Denotes the type of secondary UDP encapsulation to use for this tunnel
if any. Supported _encap_ values are _fou_, _gue_, and _none_.
_fou_ indicates Foo-Over-UDP, _gue_ indicates Generic UDP Encapsulation.
# GRE-RELATED OPTIONS
*tunnel-encap* _encap_
Denotes the type of secondary UDP encapsulation to use for this tunnel
if any. Supported _encap_ values are _fou_, _gue_, and _none_.
_fou_ indicates Foo-Over-UDP, _gue_ indicates Generic UDP Encapsulation.
*tunnel-key* _key_
Denotes the_key to used for keyed GRE to allow multiple tunnels between
the same two endpoints. _key_ is either a number or an IPv4 address-
like dotted quad. The key parameter specifies the same key to use in both
directions. The _tunnel-ikey_ and _tunnel-okey_ parameters specify different
keys for input and output. For compatiblity to ifupdown1 _key_ is an alias
for this option.
*tunnel-hoplimit* _ttl_
Denotes the Hop Limit value to use in outgoing packets for _ip6gre_/_ip6gretap_
tunnels.
*tunnel-ignore-df* _bool_
Denotes wether to enable/disable IPv4 DF suppression on this tunnel. Normally
datagrams that exceed the MTU will be fragmented; the presence of the DF flag
inhibits this, resulting instead in an ICMP Unreachable (Fragmentation Required)
message. Enabling this attribute causes the DF flag to be ignored.
*tunnel-ikey* _key_
Denotes the key to used for keyed GRE for packets received. See _tunnel-key_
for details.
*tunnel-okey* _key_
Denotes the key to used for keyed GRE for packets sent out. See _tunnel-key_
for details.
*tunnel-pmtudisc* _bool_
Denotes wether to enable/disable Path MTU Discovery on this tunnel. It is
enabled by default. Note that a fixed ttl is incompatible with this option:
tunneling with a fixed ttl always makes pmtu discovery.
*tunnel-tos* _tos_
Denotes the TOS value to use in outgoing packets.
# EXAMPLES
A simple GRE tunnel
```
auto gre0
iface gre0
tunnel-mode gre
tunnel-remote 198.51.100.1
tunnel-local 203.0.113.2
#
address 192.0.2.42/24
address 2001:db8::42/64
```
A GRE tunnel where the local IP is learned from _eth0_
```
auto gre1
iface gre1
tunnel-mode gre
tunnel-remote 198.51.100.1
tunnel-local-dev eth0
#
address 192.0.2.42/24
address 2001:db8::42/64
```
A GRE tunnel which transfers encapasulated packets via _eth0_ which is part
of a VRF.
```
auto eth0
iface eth0
address 203.0.113.2/24
gateway 203.0.113.1
vrf vrf_external
auto tun-vrf
iface tun-vrf
tunnel-mode gre
tunnel-remote 198.51.100.1
tunnel-local 203.0.113.2
tunnel-physdev eth0
#
address 192.0.2.42/24
address 2001:db8::42/64
auto vrf_external
iface vrf_external
vrf-table 1023
```
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

58
doc/interfaces-vrf.scd Normal file
View file

@ -0,0 +1,58 @@
interfaces-vrf(5)
# NAME
*interfaces-vrf* - VRF extensions for the interfaces(5) file format
# DESCRIPTION
Linux has support for Virtual Routing and Forwarding (VRF) instances
since Kernel >= 4.4. The following options allow to set up VRFs and
adding configured interfaces to VRFs.
Note that in the Linux Kernel VRFs are represented as network interfaces,
too. See https://www.kernel.org/doc/Documentation/networking/vrf.rst for
more details.
# VRF-RELATED OPTIONS
*vrf-table* _table id_
The _id_ of the kernel routing table associated with this
VRF interface. This parameter indicates that the interface
where it is specified shall be a VRF.
*vrf* _vrf interface_
The _vrf_ the interface should be assigned to. This parameter
is specified on regular interfaces which should be within the
given _vrf_.
# EXAMPLES
A VRF interface:
```
auto vrf_external
iface vrf_external
vrf-table 1023
```
A regular interface which should be within a VRF:
```
auto eth0
iface eth0
address 192.2.0.42/24
address 2001:db8::42/64
gateway 192.2.0.1
gateway 2001:db::1
vrf vrf_external
```
# SEE ALSO
*ip-vrf*(8)
*ip-link*(8)
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

131
doc/interfaces-vxlan.scd Normal file
View file

@ -0,0 +1,131 @@
interfaces-vxlan(5)
# NAME
*interfaces-vxlan* - VXLAN extensions for the interfaces(5) file format
# DESCRIPTION
Virtual eXtensible LAN (VXLAN) is an overlay network to carry Layer 2 over
an IP network while accommodating a very large number of tenants. It is
defined in RFC7348.
Be aware that VXLAN encapsulation adds 50 bytes of overhead to the IP packet
header (inner Ethernet header + VXLAN + UDP + IP). This should be taken into
consideration when setting up overlay networks, particularly on underlay
networks with a conventional 1500 byte MTU.
The following options set up VXLAN Tunnel EndPoints (VTEP) interfaces with
ifupdown-ng.
See https://www.kernel.org/doc/Documentation/networking/vxlan.rst and
https://vincent.bernat.ch/en/blog/2017-vxlan-linux for more information.
# VXLAN-RELATED OPTIONS
A VXLAN Virtual Tunnel Endpoint (VTEP) interface must an ID set. All
other options are optional.
*vxlan-id* _VNI ID_
Denotes the VXLAN Network Identifier (VNI) ID for this interface.
This parameter is required for VTEP interfaces.
*vxlan-physdev* _interface_
Specifies the physical ("underlay") device to use for tunnel
endpoint communication. This is required for setups using
multicast.
*vxlan-local-ip* _address_
Specifies the source IP address to use in outgoing packets.
For compatiblity with ifupdown2 _vxlan-local-tunnelip_ is an
alias for this parameter.
*vxlan-peer-ips* _list of IP addresses_
Specifies the unicast destination IP address(es) to use in outgoing
packets when the destination link layer address is not known in
the VXLAN device forwarding database. This option can be used to
form Point-to-Point as well as Point-to-Multipoint VXLAN tunnels/
overlays depending on how many peer IPs are given. If more than one
IP address is given a Point-to-Multipoint overlay is being set up
and ingress / head-end replication will be used by the Linux Kernel.
This option cannot be used together with _vxlan-peer-group_ option.
For compatiblity with ifupdown2 _vxlan-remoteip_ is an alias for this option
and for compatibility with previos versions of ifupdown-ng _vxlan-remote-ip_
is an alias for this option, too.
*vxlan-peer-group* _multicast group_
Specifies the multicast group address to join, requires _vxlan-phsydev_
to be set as well. This parameter cannot be specified in combination
with the _vxlan-peer-ips_ parameter. For compatibility with ifupdown2
_vxlan-svcnodeip_ is an alias for this option and for compatibility
with previos version of ifupdown-ng _vxlan-remote-group_ is an alias, too.
*vxlan-learning* _on/off_
Specifies if unknown source link layer addresses and IP addresses
are entered into the VXLAN device forwarding database.
*vxlan-ageing* _seconds_
Specifies the lifetime in seconds of FDB entries learnt by the kernel.
*vxlan-dstport* _port_
Specifies the UDP destination port of the remote VXLAN tunnel endpoint.
The default is 4789.
# EXAMPLES
A VTEP with multiple peers addressed via a multicast group:
```
auto vx_v1001_padcty
iface vx_v1001_padcty
vxlan-id 655617
vxlan-physdev vlan1001
vxlan-remote-group 225.10.1.1
#
hwaddress f2:00:c1:01:10:01
mtu 1560
```
The same works just fine with IPv6 in the underlay:
```
auto vx_v1400_padcty
iface vx_v1400_padcty
vxlan-id 917505
vxlan-physdev vlan1400
vxlan-peer-group ff42:1400::1
#
hwaddress f2:00:0d:01:14:00
mtu 1560
```
Note that the underlay must have an MTU of at least 1610 to
carry the encapsulated packets of the two VTEPs above.
A VTEP with one peer (unicast point-to-point configuration):
```
auto vx_ptp1
iface vx_ptp1
vxlan-id 2342
vxlan-local-ip 192.0.2.42
vxlan-peer-ips 198.51.100.23
#
hwaddress f2:00:c1:01:10:01
```
A VTEP with multiple peers (unicast point-to-multipoint with ingress / head-end replication):
```
auto vx_her
iface vx_her
vxlan-id 1337
vxlan-local-ip 2001:db8:1::1
vxlan-peer-ips 2001:db8:2::23 2001:db8:3::42 2001:db8:4::84
```
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

63
doc/interfaces-wifi.scd Normal file
View file

@ -0,0 +1,63 @@
interfaces-wifi(5)
# NAME
*interfaces-wifi* - WiFi vocabulary for the interfaces(5) file format
# DESCRIPTION
Wi-Fi (the IEEE 802.11 family of protocols) is a commonly used wireless
networking standard. The following options allow for configuration of
Wi-Fi client interfaces.
WPA-secured networks are managed using *wpa_supplicant*(8), while insecure
networks are managed directly with *iwconfig*(8).
# WIFI-RELATED OPTIONS
*wifi-config-path* _path_
Denotes the absolute _path_ to a *wpa_supplicant* configuration file.
If no path is given, _/run/wpa_supplicant.<interface>.conf_ will be
used for a temporary configuration file. This option may not be used
with other configuration options.
*wifi-ssid* _ssid_
The SSID the Wi-Fi client should connect to.
*wifi-psk* _psk_
The passphrase for connecting to the Wi-Fi network. If unset, the
client will connect without WPA2 encryption.
# EXAMPLES
A typical setup may involve connecting to a home and work network. To
achieve this, we can define a pair of virtual interfaces called *wifi-home*
and *wifi-work*, which connect to their respective wifi networks:
```
iface wifi-home
use dhcp
wifi-ssid HomeNetwork
wifi-psk ExamplePassphrase
iface wifi-work
use dhcp
wifi-config-path /etc/network/wpa-work.conf
```
The virtual interfaces can be used with *ifup* and *ifdown*:
```
# ifup wlan0=wifi-home
# ifdown wlan0
# ifup wlan0=wifi-work
```
# SEE ALSO
*iwconfig*(8)++
*wpa_supplicant*(8)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>

View file

@ -0,0 +1,56 @@
interfaces-wireguard(5)
# NAME
*interfaces-wireguard* - Wireguard extensions for the interfaces(5) file format
# DESCRIPTION
Wireguard is a comtemporary in-Kernel layer 3 VPN protocol implementation
which aims to provide fast and secure tunnels. The following options
allow to set up Wireguard VPN tunnels.
# WIREGUARD-RELATED OPTIONS
*wireguard-config-path* _path_
Denotes the absolute _path_ to the Wireguard configuration file.
If no path is given, _/etc/wireguard/<interface>.conf_ will be
used. In the latter case _use wireguard_ has to be explicitly
set to the interface configuration.
Be aware that the given configuration file will be loaded using
*wg setconf* and not with *wg-quick*. The file format for both
tools isn't compatible so you have to make sure you provide a
valid configuration file for the *wg* tool. If you already have
a configuration file for *wg-quick* you can set up the tunnel
manually once and then dump the configuration using *wg showconf*
and save this to _path_.
# EXAMPLES
A Wireguard VPN tunnel with explicit configuration file specified
```
auto wg-foo
iface wg-foo
wireguard-config-path /etc/wireguard/foo.conf
#
address 192.0.2.23/42
address 2001:db8::23/64
```
A Wireguard VPN tunnel with implicit configuration file:
```
auto wg-bar
iface wg-bar
use wireguard
#
address 192.0.2.23/42
address 2001:db8::23/64
```
# AUTHORS
Maximilian Wilhelm <max@sdn.clinic>

262
doc/interfaces.scd Normal file
View file

@ -0,0 +1,262 @@
interfaces(5)
# NAME
*/etc/network/interfaces* - interface configuration database
# DESCRIPTION
The */etc/network/interfaces* file is used to specify how network
interfaces are configured. The file is processed by *ifquery*(8),
*ifup*(8) and *ifdown*(8) to introspect and change system state.
In most cases, syntax from legacy implementations is supported as
well, but that syntax is not discussed in detail here.
# FILE SYNTAX
The interface configuration database is composed of a series of
stanzas. Hash symbols designate comments, which are ignored by
the system.
A stanza is a collection of triples, where a triple is a key and
value combination that is related to an *object*. Triples which
are not associated with an *object* are considered to be part
of the root of the configuration tree.
All keywords are case-sensitive and are expected to be lower-case.
The following is a simple example of a stanza:
```
auto eth0
iface eth0
address 203.0.113.2/24
gateway 203.0.113.1
```
This stanza defines an interface named *eth0* which is configured
with an address of *203.0.113.2* and gateway of *203.0.113.1*.
# SUPPORTED KEYWORDS FOR UNASSOCIATED TRIPLES
*auto* _object_
Designates that _object_ should be automatically configured
by the system when appropriate.
*iface* _object_
Begins a new declaration for _object_. Any child keyword
associated with the declaration will be stored inside
_object_.
*source* _filename_
Includes the file _filename_ as configuration data.
*source-directory* _directory_
Includes the files in _directory_ as configuration data.
*template* _object_
Begins a new declaration for _object_, like *iface*, except
that _object_ is defined as a *template*.
# SUPPORTED KEYWORDS FOR OBJECT TRIPLES
Any keyword may be used inside an interface declaration block, but
the system will only respond to certain keywords by default:
*address* _address_
Associates an IPv4 or IPv6 address in CIDR notation with
the parent interface. If an IP address without a prefix
length is given a given _netmask_ attribute is used if present.
If neither a prefix length nor a _netmask_ are given a /24 or /64
prefix length is presumed for IPv4 / IPv6 as of compatibility
reasons to classic ifupdown.
*netmask* _netmask_
Associates a fallback netmask with the parent interface for
addresses which do not have a CIDR length set. This option
is for backwards compatibility and should not be used in new
deployments.
*point-to-point* _address_
Sets the given IPv4 _address_ as the peer address on the
interface. This setting only takes effect for the IPv4 address
familiy and only makes sense in combination with a /32 netmask.
For compatiblity with ifupdown and ifupdown2, _pointopoint_ is
an alias for this parameter.
*gateway* _address_
Associates an IPv4 or IPv6 address with the parent interface
for use as a default route (gateway). This usually is given
once for IPv4 and once for IPv6 (in a Dual-Stack setup).
*link-type* _link-type_
Denotes the link-type of the interface. When set to _dummy_,
the interface is created as a virtual dummy interfaces.
When set to _veth_ the interface is created as virtual veth
interface (pair).
*veth-peer-name* _peer-name_
Denotes the name of the veth peer interfaces. If not set
the kernel will name the veth peer interface as _vethN_
with N being an integer number.
*alias* _alias_
Sets the given alias on the interface.
*requires* _interfaces_...
Designates one or more required interfaces that must be
brought up before configuration of the parent interface.
Interfaces associated with the parent are taken down at
the same time as the parent.
*inherit* _object_
Designates that the configured interface should inherit
configuration data from _object_. Normally _object_
must be a *template*.
*use* _executor_
Designates that an executor should be used. See _EXECUTORS_
section for more information on executors.
*pre-down* _command_
Runs _command_ before taking the interface down.
*down* _command_
Runs _command_ when the interface is taken down.
*post-down* _command_
Runs _command_ after taking the interface down.
*pre-up* _command_
Runs _command_ before bringing the interface up.
*up* _command_
Runs _command_ when the interface is brought up.
*post-up* _command_
Runs _command_ after bringing the interface up.
Additional packages such as *bonding*, *bridge*, *tunnel*, *vrf* and
*vxlan* add additional keywords to this vocabulary.
# EXECUTORS
The *use* keyword designates that an _executor_ should be used.
This system is extendable by additional packages, but the
most common executors are:
*batman*
The interface is a B.A.T.M.A.N. adv. mesh interface.
Configuration of B.A.T.M.A.N. adv. interfaces requires the
*batctl* untiliy to be installed.
*bond*
The interface is a bonded interface. Configuration
of bonded interfaces requires the *bonding* package
to be installed.
*bridge*
The interface is an ethernet bridge. Configuration
of ethernet bridges requires the *bridge* package
to be installed.
*dhcp*
Use a DHCP client to learn the IPv4 address of an
interface.
*forward*
Configures forwarding settings on the interface.
*loopback*
Designates the interface as a loopback device.
*ppp*
Designates the interface as a PPP device. Configuration
of PPP interfaces require the *ppp* and probably the *pppoe*
packages to be installed.
*tunnel*
The interface is a tunnel. Configuration of tunnels
requires the *tunnel* package to be installed on Alpine
Linux.
*vrf*
The interface is a VRF. Configuration of VRFs requires
the *vrf* package to be installed.
*vxlan*
The interface is a Virtual Extensible LAN (VXLAN) tunnel
endpoint.
*wifi*
The interface is a Wi-Fi (IEEE 802.11) client interface.
Configuration of the WiFi client interface requires the
*wireless-tools* package to be installed.
The *wpa_supplicant* package must also be installed to
connect to hotspots using WPA-based security.
*wireguard*
The interface is a Wireguard VPN tunnel endpoint.
Check *interfaces-<executor>(5)* for further informaton about a given
executor and available configuration parameters.
If the _auto\_executor\_selection_ ifupdown-ng.conf option is enabled,
*use* statements will automatically be added for executors when their
configuration statements are present in the interfaces file.
# EXAMPLES
Configure a bridge interface *br0* with *bond0* attached to it,
which is a failover between *eth0* and *eth1*. This requires
the *bonding* and *bridge* packages to be installed:
```
auto br0
iface br0
use bridge
requires bond0
address 203.0.113.2/24
gateway 203.0.113.1
iface bond0
use bond
requires eth0 eth1
bond-mode 802.3ad
bond-xmit-hash-policy layer2+3
```
Configure a network interface to use DHCP to learn its IPv4
address:
```
auto eth0
iface eth0
use dhcp
```
# SEE ALSO
*ifstate*(5)
*ifupdown-ng.conf*(5)
*ifup*(8)
*ifdown*(8)
*ifquery*(8)
*ifctrstat*(8)
*interfaces-batman*(5)
*interfaces-bond*(5)
*interfaces-bridge*(5)
*interfaces-forward*(5)
*interfaces-mpls*(5)
*interfaces-ppp*(5)
*interfaces-tunnel*(5)
*interfaces-vrf*(5)
*interfaces-vxlan*(5)
*interfaces-wifi*(5)
*interfaces-wireguard*(5)
# AUTHORS
Ariadne Conill <ariadne@dereferenced.org>++
Maximilian Wilhelm <max@sdn.clinic>

View file

@ -0,0 +1,126 @@
#!/bin/sh
#
# Maximilian Wilhelm <max@sdn.clinic>
# -- Wed 26 Aug 2020 08:15:58 PM CEST
#
# This executor is responsible for setting up the main B.A.T.M.A.N. adv. interface (eg. bat0)
# as well as managing settings of the underlying interfaces (hardifs).
#
# See interfaces-batman(5) for a list of supported options for hardifs as well as meshifs.
#
if [ "$VERBOSE" ]; then
set -x
fi
if ! which batctl >/dev/null 2>&1; then
echo "Error: batctl utility not found!" >&2
exit 1
fi
#
# Compatiblity glue: Newer versions of batctl print a deprecation
# warning when called with -m <batif>. Avoid spamming the log and
# producting SPAM by silently handling this here.
mesh_if_param="-m"
if batctl -h 2>&1 | grep -q "meshif"; then
mesh_if_param="meshif"
fi
#
# Functions to manage main B.A.T.M.A.N. adv. interface
batctl_if () {
for iface in ${IF_BATMAN_IFACES}; do
${MOCK} batctl "${mesh_if_param}" "${IFACE}" interface "$1" "${iface}"
done
}
set_meshif_options () {
# We only care for options of format IF_BATMAN_<OPTION_NAME>
env | grep '^IF_BATMAN_[A-Z0-9_]\+' | while IFS="=" read opt value; do
# Members, ignore-regex, routing-algo, and throughput_override are handled seperately
if [ "${opt}" = "IF_BATMAN_IFACES" -o \
"${opt}" = "IF_BATMAN_IFACES_IGNORE_REGEX" -o \
"${opt}" = "IF_BATMAN_ROUTING_ALGO" -o \
"${opt}" = "IF_BATMAN_THROUGHPUT_OVERRIDE" ]; then
continue
fi
# Convert options for the actual name
real_opt=$(echo "${opt}" | sed -e 's/^IF_BATMAN_\([A-Z0-9_]\+\)/\1/' | tr '[A-Z]' '[a-z]')
${MOCK} batctl "${mesh_if_param}" "${IFACE}" "${real_opt}" "${value}"
done
}
set_routing_algo () {
if [ "${IF_BATMAN_ROUTING_ALGO}" != "BATMAN_IV" -a "${IF_BATMAN_ROUTING_ALGO}" != "BATMAN_V" ]; then
echo "Invalid routing algorithm \"$1\"."
return
fi
batctl ra "${IF_BATMAN_ROUTING_ALGO}"
}
#
# Functions to manage B.A.T.M.A.N. adv. underlay interfaces (hardifs)
set_hardif_options () {
# Query hardif parameter manually for now
for hardif in ${IF_BATMAN_IFACES}; do
penalty=$(ifquery -p "batman-hop-penalty" "${hardif}")
if [ "${penalty}" ]; then
${MOCK} batctl hardif "${hardif}" hop_penalty "${penalty}"
fi
throughput=$(ifquery -p "batman-throughput-override" "${hardif}")
if [ "${throughput}" ]; then
${MOCK} batctl hardif "${hardif}" throughput_override "${througput}"
fi
done
}
case "${PHASE}" in
depend)
if [ "${IF_BATMAN_IFACES}" ]; then
echo "${IF_BATMAN_IFACES}"
fi
;;
create)
# Main B.A.T.M.A.N. adv. interface
if [ "${IF_BATMAN_IFACES}" ]; then
if [ "${IF_BATMAN_ROUTING_ALGO}" ]; then
set_routing_algo
fi
if [ ! -d "/sys/class/net/${IFACE}" ]; then
batctl "${mesh_if_param}" "${IFACE}" interface create || true
fi
fi
;;
pre-up)
# Main B.A.T.M.A.N. adv. interface (meshif)
if [ "${IF_BATMAN_IFACES}" ]; then
batctl_if add
set_meshif_options
set_hardif_options
fi
;;
destroy)
if [ "${IF_BATMAN_IFACES}" -a -d "/sys/class/net/${IFACE}" ]; then
# Newer versions of batctl provide the "interface destroy" API, try to use it
if ! batctl "${mesh_if_param}" "${IFACE}" interface destroy 2>/dev/null; then
# Fall back to old style member interface removal
batctl_if del
fi
fi
;;
*)
exit 0
;;
esac

57
executor-scripts/linux/bond Executable file
View file

@ -0,0 +1,57 @@
#!/bin/sh
#
# This executor is responsible for setting up bond/LAG interfaces.
#
# Sat, 03 Oct 2020 20:42:19 +0200
# -- Maximilian Wilhelm <max@sdn.clinic>
#
[ -n "$VERBOSE" ] && set -x
get_bond_options() {
# We only care for options of format IF_BOND_<OPTION_NAME>
env | grep '^IF_BOND_[A-Z0-9_]\+' | while IFS="=" read opt value; do
# Members are handled seperately
if [ "${opt}" = "IF_BOND_MEMBERS" ]; then
continue
fi
# Convert options for the actual name
real_opt=$(echo "${opt}" | sed -e 's/^IF_BOND_\([A-Z0-9_]\+\)/\1/' | tr '[A-Z]' '[a-z]')
echo -n " ${real_opt} ${value}"
done
}
case "$PHASE" in
depend)
echo "${IF_BOND_MEMBERS}"
;;
create)
if [ -d "/sys/class/net/${IFACE}" ]; then
exit 0
fi
# Gather bonding options for this interface
options=$(get_bond_options)
# Create bond
${MOCK} ip link add "${IFACE}" type bond ${options}
# Add members to bundle
for member_iface in ${IF_BOND_MEMBERS}; do
# Work around the current execution order
${MOCK} ip link set "${member_iface}" down
${MOCK} ip link set master "${IFACE}" "${member_iface}"
${MOCK} ip link set "${member_iface}" up
done
;;
destroy)
if [ -z "${MOCK}" -a ! -d "/sys/class/net/${IFACE}" ]; then
exit 0
fi
${MOCK} ip link del "${IFACE}"
;;
esac

302
executor-scripts/linux/bridge Executable file
View file

@ -0,0 +1,302 @@
#!/bin/sh
[ -n "$VERBOSE" ] && set -x
# Copyright (C) 2012, 2020 Natanael Copa <ncopa@alpinelinux.org>
# Copyright (C) 2020 Ariadne Conill <ariadne@dereferenced.org>
# Copyright (C) 2020 Maximilian Wilhelm <max@sdn.clinic>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# This software is provided 'as is' and without any warranty, express or
# implied. In no event shall the authors be liable for any damages arising
# from the use of this software.
################################################################################
# Bridge management functions #
################################################################################
all_ports_exist() {
local i=
for i in "$@"; do
[ -d /sys/class/net/$i ] || return 1
done
return 0
}
wait_ports() {
local timeout= waitports=
[ -z "$IF_BRIDGE_WAITPORT" ] && return 0
set -- $IF_BRIDGE_WAITPORT
timeout="$1"
shift
waitports="$@"
[ -z "$waitports" ] && waitports="$PORTS"
while ! all_ports_exist $waitports; do
[ "$timeout" -eq 0 ] && return 0
timeout=$(($timeout - 1))
sleep 1
done
}
all_ports() {
local i=
for i in /sys/class/net/*/ifindex; do
i=${i%/*}
i=${i##*/}
case "$i" in
lo|$IFACE) continue;;
*) echo $i;;
esac
done
}
add_ports() {
local port=
for port in $PORTS; do
if [ -n "$IF_BRIDGE_HW" ]; then
ip link set dev $port addr $IF_BRIDGE_HW
fi
ip link set dev $port master $IFACE && ip link set dev $port up
done
}
del_ports() {
local port=
for port in $PORTS; do
ip link set dev $port down
ip link set dev $port nomaster
done
}
set_bridge_opts_brctl() {
[ -n "$IF_BRIDGE_AGEING" ] \
&& brctl setageing $IFACE $IF_BRIDGE_AGEING
[ -n "$IF_BRIDGE_BRIDGEPRIO" ] \
&& brctl setbridgeprio $IFACE $IF_BRIDGE_BRIDGEPRIO
[ -n "$IF_BRIDGE_FD" ] \
&& brctl setfd $IFACE $IF_BRIDGE_FD
[ -n "$IF_BRIDGE_HELLO" ] \
&& brctl sethello $IFACE $IF_BRIDGE_HELLO
[ -n "$IF_BRIDGE_MAXAGE" ] \
&& brctl setmaxage $IFACE $IF_BRIDGE_MAXAGE
[ -n "$IF_BRIDGE_PATHCOST" ] \
&& brctl setpathcost $IFACE $IF_BRIDGE_PATHCOST
[ -n "$IF_BRIDGE_PORTPRIO" ] \
&& brctl setportprio $IFACE $IF_BRIDGE_PORTPRIO
[ -n "$IF_BRIDGE_STP" ] \
&& brctl stp $IFACE $IF_BRIDGE_STP
}
yesno() {
case "$1" in
yes|YES|true|TRUE|1)
echo 1
;;
*)
echo 0
;;
esac
}
set_bridge_opts_iproute2() {
[ -n "$IF_BRIDGE_AGEING" ] \
&& ip link set dev $IFACE type bridge ageing_time $IF_BRIDGE_AGEING
[ -n "$IF_BRIDGE_BRIDGEPRIO" ] \
&& ip link set dev $IFACE type bridge priority $IF_BRIDGE_BRIDGEPRIO
[ -n "$IF_BRIDGE_FD" ] \
&& ip link set dev $IFACE type bridge forward_delay $IF_BRIDGE_FD
[ -n "$IF_BRIDGE_HELLO" ] \
&& ip link set dev $IFACE type bridge hello_time $IF_BRIDGE_HELLO
[ -n "$IF_BRIDGE_MAXAGE" ] \
&& ip link set dev $IFACE type bridge max_age $IF_BRIDGE_MAXAGE
[ -n "$IF_BRIDGE_PATHCOST" ] \
&& bridge link set dev $IFACE cost $IF_BRIDGE_PATHCOST
[ -n "$IF_BRIDGE_PORTPRIO" ] \
&& bridge link set dev $IFACE priority $IF_BRIDGE_PORTPRIO
[ -n "$IF_BRIDGE_STP" ] \
&& ip link set dev $IFACE type bridge stp $(yesno $IF_BRIDGE_STP)
[ -n "$IF_BRIDGE_VLAN_AWARE" ] \
&& ip link set dev $IFACE type bridge vlan_filtering $(yesno $IF_BRIDGE_VLAN_AWARE)
}
set_bridge_opts() {
[ -x /sbin/bridge ] && set_bridge_opts_iproute2 && return 0
[ -x /sbin/brctl ] && set_bridge_opts_brctl && return 0
}
all_ports_ready() {
local port=
for port in $PORTS; do
case $(cat /sys/class/net/$IFACE/brif/$port/state) in
""|0|3) ;; # 0 = disabled, 3 = forwarding
[12]) return 1;;
esac
done
return 0
}
find_maxwait() {
awk '{printf("%.f\n", 2 * $0 / 100); }' \
/sys/class/net/$IFACE/bridge/forward_delay
}
wait_bridge() {
local timeout=$IF_BRIDGE_MAXWAIT
if [ -z "$timeout" ]; then
timeout=$(find_maxwait)
fi
ip link set dev $IFACE up
while ! all_ports_ready; do
[ $timeout -eq 0 ] && break
timeout=$(($timeout - 1))
sleep 1
done
}
################################################################################
# Bridge port management functions #
################################################################################
configure_access_port() {
port="$1"
vlan="$2"
self="$3"
# Cleans all existing VLANs (probably at least VLAN 1)
bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | while read vid flags; do
bridge vlan del vid "${vid}" dev "${port}" ${self}
done
bridge vlan add vid "${vlan}" pvid untagged dev "${port}" ${self}
}
configure_trunk_port() {
port="$1"
self="$2"
# Working on the bridge itself?
if [ "${self}" ]; then
allow_untagged="${IF_BRIDGE_ALLOW_UNTAGGED}"
pvid="${IF_BRIDGE_PVID}"
vids="${IF_BRIDGE_VIDS}"
else
allow_untagged=$(ifquery -p bridge-allow-untagged ${port} 2>/dev/null || true)
pvid=$(ifquery -p bridge-pvid ${port} 2>/dev/null || true)
vids=$(ifquery -p bridge-vids ${port} 2>/dev/null || true)
fi
# If bridge-allow-untagged if set to off, remove untagged VLAN. If it's
# one of our bridge-vids, it will be set again later.
if [ "${allow_untagged}" -a "$(yesno ${allow_untagged})" = 0 ]; then
untagged_vid=$(bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | awk '/Untagged/ { print $1 }')
if [ "${untagged_vid}" ]; then
bridge vlan del vid "${untagged_vid}" dev "${port}" ${self}
fi
fi
# The vlan specified is to be considered a PVID at ingress.
# Any untagged frames will be assigned to this VLAN.
if [ "${pvid}" ]; then
cur_pvid=$(bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | awk '/PVID/ { print $1 }')
if [ "${cur_pvid}" ]; then
bridge vlan del vid ${cur_pvid} dev "${port}" ${self}
fi
bridge vlan add vid "${pvid}" dev "${port}" pvid untagged ${self}
fi
# Add regular tagged VLANs
for vid in ${vids}; do
bridge vlan add vid $vid dev "${port}" ${self}
done
}
# Configure VLANs on the bridge interface itself
set_bridge_vlans() {
# Shall the bridge interface be an untagged port?
if [ "${IF_BRIDGE_ACCESS}" ]; then
configure_access_port "${IFACE}" "${IF_BRIDGE_ACCESS}" "self"
# Configure bridge interface as trunk port
else
configure_trunk_port "${IFACE}" "self"
fi
}
# Configure VLANs on the bridge-ports
set_bridge_port_vlans() {
for port in ${PORTS}; do
access_vlan=$(ifquery -p bridge-access ${port} 2>/dev/null || true)
# Shall this prot interface be an untagged port?
if [ "${access_vlan}" ]; then
configure_access_port "${port}" "${access_vlan}"
# Configure port as trunk
else
configure_trunk_port "${port}"
fi
done
}
case "$IF_BRIDGE_PORTS" in
"") ;;
none) PORTS="";;
all) PORTS=$(all_ports);;
*) PORTS="$IF_BRIDGE_PORTS";;
esac
case "$PHASE" in
depend)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" ]; then
echo "$PORTS"
fi
;;
create)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" -a ! -d "/sys/class/net/${IFACE}" ]; then
ip link add "${IFACE}" type bridge
fi
;;
pre-up)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" ]; then
wait_ports
set_bridge_opts
set_bridge_vlans
add_ports
set_bridge_port_vlans
wait_bridge
# Called for a bridge member port
elif [ "${IF_BRIDGE_VIDS}" -o "${IF_BRIDGE_PVID}" -o "${IF_BRIDGE_ACCESS}" -o "${IF_BRIDGE_ALLOW_UNTAGGED}" ]; then
# Eventually we want to configure VLAN settings of member ports here.
# The current execution model does not allow this, so this is a no-op
# for now and we work around this by configuring ports while configuring
# the bridge.
true
fi
;;
post-down)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" ]; then
del_ports
ip link set dev $IFACE down
fi
;;
destroy)
# Called for the bridge interface
if [ "${IF_BRIDGE_PORTS}" -a -d "/sys/class/net/${IFACE}" ]; then
ip link del "${IFACE}"
fi
;;
esac

67
executor-scripts/linux/dhcp Executable file
View file

@ -0,0 +1,67 @@
#!/bin/sh
# some users provide a shell fragment for the hostname property.
[ -n "$IF_DHCP_HOSTNAME" ] && IF_DHCP_HOSTNAME=$(eval echo $IF_DHCP_HOSTNAME)
determine_implementation() {
[ -n "$IF_DHCP_PROGRAM" ] && echo "$IF_DHCP_PROGRAM" && return
[ -x /sbin/dhcpcd ] && echo "dhcpcd" && return
[ -x /usr/sbin/dhclient ] && echo "dhclient" && return
[ -x /sbin/udhcpc ] && echo "udhcpc" && return
echo "could not find a supported DHCP implementation"
exit 1
}
start() {
case "$1" in
dhcpcd)
[ -n "$IF_DHCP_HOSTNAME" ] && optargs="$optargs -h $IF_DHCP_HOSTNAME"
[ -n "$IF_DHCP_VENDOR" ] && optargs="$optargs -i $IF_DHCP_VENDOR"
[ -n "$IF_DHCP_CLIENT_ID" ] && optargs="$optargs -i $IF_DHCP_CLIENT_ID"
[ -n "$IF_DHCP_LEASETIME" ] && optargs="$optargs -l $IF_DHCP_LEASETIME"
${MOCK} /sbin/dhcpcd $optargs $IFACE
;;
dhclient)
# Specific config file given?
if [ -n "$IF_DHCP_CONFIG" ]; then
optargs="$optargs -cf $IF_DHCP_CONFIG"
fi
${MOCK} /usr/sbin/dhclient -pf /var/run/dhclient.$IFACE.pid $optargs $IFACE
;;
udhcpc)
optargs=$(eval echo $IF_UDHCPC_OPTS)
[ -n "$IF_DHCP_HOSTNAME" ] && optargs="$optargs -x hostname:$IF_DHCP_HOSTNAME"
[ -n "$IF_DHCP_CLIENT_ID" ] && optargs="$optargs -c $IF_DHCP_CLIENT_ID"
[ -n "$IF_DHCP_SCRIPT" ] && optargs="$optargs -s $IF_DHCP_SCRIPT"
${MOCK} /sbin/udhcpc -b -R -p /var/run/udhcpc.$IFACE.pid -i $IFACE $optargs
;;
*)
;;
esac
}
stop() {
case "$1" in
dhcpcd)
${MOCK} /sbin/dhcpcd -k $IFACE
;;
dhclient)
${MOCK} kill -9 $(cat /var/run/dhclient.$IFACE.pid) 2>/dev/null
;;
udhcpc)
${MOCK} kill $(cat /var/run/udhcpc.$IFACE.pid)
;;
*)
;;
esac
}
impl=$(determine_implementation)
[ -z "$VERBOSE" ] || set -x
case "$PHASE" in
up) start $impl ;;
down) stop $impl ;;
*) ;;
esac

58
executor-scripts/linux/ethtool Executable file
View file

@ -0,0 +1,58 @@
#!/bin/sh
# gather params for a given prefix, based on executor-scripts/linux/tunnel.
gather_params() {
env | sed -E "
s/^IF_${1}_([A-Z0-9_]+)=(.+)/\1\n\2/
ta
d
:a
h
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
P
g
s/.*\n//" | sed -E "s/_/-/g"
}
case "$PHASE" in
pre-up)
settings="\
${IF_ETHTOOL_ETHERNET_PORT:+ port $IF_ETHTOOL_ETHERNET_PORT}
${IF_ETHTOOL_MSGLVL:+ msglvl $IF_ETHTOOL_MSGLVL}
"
[ -z "$settings" ] || $MOCK ethtool --change "$IFACE" $settings
;;
up)
# first do the link settings.
link_settings="${IF_ETHTOOL_LINK_SPEED:+ speed $IF_ETHTOOL_LINK_SPEED}${IF_ETHTOOL_LINK_DUPLEX:+ duplex $IF_ETHTOOL_LINK_DUPLEX}"
# ethernet-wol can have a second arg (key), split into $1 and $2
set -- $IF_ETHTOOL_ETHERNET_WOL
link_settings="$link_settings${1:+ wol $1}${2:+ sopass $2}"
# handle ethtool-ethernet-autoneg like Debian would
case "$IF_ETHTOOL_ETHERNET_AUTONEG" in
'')
;;
on|off)
link_settings="$link_settings autoneg $IF_ETHTOOL_ETHERNET_AUTONEG"
;;
*)
link_settings="$link_settings autoneg on advertise $IF_ETHTOOL_ETHERNET_AUTONEG"
;;
esac
[ -z "$link_settings" ] || $MOCK ethtool --change "$IFACE" $link_settings
pause_settings=$(gather_params ETHTOOL_PAUSE)
[ -z "$pause_settings" ] || $MOCK ethtool --pause "$IFACE" $pause_settings
offload_settings=$(gather_params ETHTOOL_OFFLOAD)
[ -z "$offload_settings" ] || $MOCK ethtool --offload "$IFACE" $offload_settings
dma_settings=$(gather_params ETHTOOL_DMA_RING)
[ -z "$dma_settings" ] || $MOCK ethtool --set-ring "$IFACE" $dma_settings
coalesce_settings=$(gather_params ETHTOOL_COALESCE)
[ -z "$coalesce_settings" ] || $MOCK ethtool --coalesce "$IFACE" $coalesce_settings
;;
esac

19
executor-scripts/linux/forward Executable file
View file

@ -0,0 +1,19 @@
#!/bin/sh
yesno() {
case "$1" in
yes|1) echo 1 ;;
*) echo 0 ;;
esac
}
[ "$PHASE" != "up" ] && exit 0
[ -z "$VERBOSE" ] || set -x
[ -n "$IF_FORWARD_IPV4" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV4) > /proc/sys/net/ipv4/conf/$IFACE/forwarding"
[ -n "$IF_FORWARD_IPV6" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV6) > /proc/sys/net/ipv6/conf/$IFACE/forwarding"
[ -n "$IF_FORWARD_IPV4_MC" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV4_MC) > /proc/sys/net/ipv4/conf/$IFACE/mc_forwarding"
[ -n "$IF_FORWARD_IPV6_MC" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV6_MC) > /proc/sys/net/ipv6/conf/$IFACE/mc_forwarding"
exit 0

28
executor-scripts/linux/gre Executable file
View file

@ -0,0 +1,28 @@
#!/bin/sh
# Executor for advanced GRE tunnel management.
[ -z "$IF_GRE_LOCAL" ] && exit 1
[ -z "$IF_GRE_REMOTE" ] && exit 1
[ -z "$IF_GRE_MODE" ] && IF_GRE_MODE="gre"
COMMAND="link"
FAMILY="-4"
[ "$IF_GRE_MODE" = "ip6gre" ] && FAMILY="-6"
PARAMS="mode $IF_GRE_MODE local '$IF_GRE_LOCAL' remote '$IF_GRE_REMOTE'"
[ -n "$IF_GRE_TTL" ] && PARAMS="$PARAMS ttl '$IF_GRE_TTL'"
[ -n "$IF_GRE_FLAGS" ] && PARAMS="$PARAMS $IF_GRE_FLAGS"
[ -n "$PARAMS" ] || exit 0
case "$PHASE" in
create)
${MOCK} eval ip $FAMILY $COMMAND add $IFACE $PARAMS
;;
destroy)
${MOCK} ip $FAMILY $COMMAND del $IFACE
;;
depend)
echo "$IF_GRE_DEV"
;;
esac

16
executor-scripts/linux/ipv6-ra Executable file
View file

@ -0,0 +1,16 @@
#!/bin/sh
start() {
${MOCK} /bin/sh -c "echo 1 > /proc/sys/net/ipv6/conf/$IFACE/accept_ra"
}
stop() {
${MOCK} /bin/sh -c "echo 0 > /proc/sys/net/ipv6/conf/$IFACE/accept_ra"
}
[ -z "$VERBOSE" ] || set -x
case "$PHASE" in
up) start $impl ;;
down) stop $impl ;;
*) ;;
esac

112
executor-scripts/linux/link Executable file
View file

@ -0,0 +1,112 @@
#!/bin/sh
[ -n "$VERBOSE" ] && set -x
is_vlan() {
case "$IFACE" in
*#*) return 1 ;;
*:*) return 1 ;;
vlan*.*) return 1 ;;
vlan*)
IF_VLAN_ID="${IFACE#vlan}"
[ -n "${IF_VLAN_RAW_DEVICE:-}" ] && return 0
return 1
;;
*.*)
IF_VLAN_RAW_DEVICE="${IFACE%.*}"
IF_VLAN_ID="${IFACE##*.}"
return 0
;;
*)
[ -z "${IF_VLAN_ID:-}" ] && return 1
[ -z "${IF_VLAN_RAW_DEVICE:-}" ] && return 1
return 0
;;
esac
}
case "$PHASE" in
depend)
# vlan-raw-device
if is_vlan; then
echo "$IF_VLAN_RAW_DEVICE"
# veth-peer-name
elif [ "${IF_LINK_TYPE}" = "veth" -a "${IF_VETH_PEER_NAME}" ]; then
echo "${IF_VETH_PEER_NAME}"
fi
;;
create)
if [ "${IF_LINK_TYPE}" = "dummy" ]; then
if [ -d "/sys/class/net/${IFACE}" ]; then
iface_type=$(ip -d link show dev "${IFACE}" | head -n3 | tail -n1 | awk '{ print $1 }')
if [ "${iface_type}" != 'dummy' ]; then
echo "Interface ${IFACE} exists but is of type ${iface_type} instead of dummy"
exit 1
fi
exit 0
fi
${MOCK} ip link add "${IFACE}" type dummy
elif [ "${IF_LINK_TYPE}" = "veth" ]; then
if [ ! -d "/sys/class/net/${IFACE}" ]; then
ARGS=""
if [ "${IF_VETH_PEER_NAME}" ]; then
ARGS="peer ${IF_VETH_PEER_NAME}"
fi
${MOCK} ip link add "${IFACE}" type veth ${ARGS}
fi
elif is_vlan; then
if [ -d "/sys/class/net/${IFACE}" ]; then
exit 0
fi
if [ -z "${MOCK}" ]; then
if [ ! -d "/sys/class/net/${IF_VLAN_RAW_DEVICE}" ]; then
echo "Underlay device ${IF_VLAN_RAW_DEVICE} for ${IFACE} does not exist"
exit 1
fi
if ! [ -d /proc/net/vlan ]; then
echo "Loading 8021q kernel module for VLAN support"
${MOCK} modprobe 8021q
fi
fi
${MOCK} ip link add link "${IF_VLAN_RAW_DEVICE}" name "${IFACE}" type vlan id "${IF_VLAN_ID}"
fi
;;
up)
IF_LINK_OPTIONS="$IF_LINK_OPTIONS"
[ -n "$IF_MTU" ] && IF_LINK_OPTIONS="$IF_LINK_OPTIONS mtu $IF_MTU"
[ -n "$IF_HWADDRESS" ] && IF_LINK_OPTIONS="$IF_LINK_OPTIONS address $IF_HWADDRESS"
${MOCK} ip link set up dev "${IFACE}" ${IF_LINK_OPTIONS}
# Set alias is configured
if [ "${IF_ALIAS}" ]; then
${MOCK} ip link set alias "${IF_ALIAS}" dev "${IFACE}"
fi
;;
down)
# Don't complain about a vanished interface when downing it
if [ -z "${MOCK}" -a ! -d "/sys/class/net/${IFACE}" ]; then
exit 0
fi
${MOCK} ip link set down dev "${IFACE}"
;;
destroy)
if [ "${IF_LINK_TYPE}" = "dummy" ] || [ "${IF_LINK_TYPE}" = "veth" ] || is_vlan; then
if [ -z "${MOCK}" -a ! -d "/sys/class/net/${IFACE}" ]; then
exit 0
fi
${MOCK} ip link del "${IFACE}"
fi
;;
esac

36
executor-scripts/linux/mpls Executable file
View file

@ -0,0 +1,36 @@
#!/bin/sh
#
# Maximilian Wilhelm <max@sdn.clinic>
# -- Thu, 17 Dec 2020 03:02:10 +0100
#
# This executor is responsible for setting up MPLS decapsulation on a given interface.
#
# See interfaces-mpls(5) for a list of supported options.
#
yesno() {
case "$1" in
yes|1) echo 1 ;;
*) echo 0 ;;
esac
}
[ -z "$VERBOSE" ] || set -x
# We only operate in pre-up phase
[ "$PHASE" != "pre-up" ] && exit 0
if [ "$IF_MPLS_ENABLE" ]; then
value=$(yesno $IF_MPLS_ENABLE)
# Load mpls module if we should enable MPLS decap on (at least) one interface
if [ "${value}" = 1 ]; then
${MOCK} modprobe mpls_iptunnel
fi
# If MPLS support isn't loaded and we are not MOCKing, carry on
if [ -f "/proc/sys/net/mpls/conf/$IFACE/input" -o "${MOCK}" ]; then
${MOCK} /bin/sh -c "echo ${value} > /proc/sys/net/mpls/conf/$IFACE/input"
fi
fi

14
executor-scripts/linux/ppp Executable file
View file

@ -0,0 +1,14 @@
#!/bin/sh
[ -z "$IF_PPP_PROVIDER" ] && exit 0
case "$PHASE" in
create)
${MOCK} pon $IF_PPP_PROVIDER
;;
destroy)
${MOCK} poff $IF_PPP_PROVIDER
;;
depend)
echo "$IF_PPP_PHYSDEV"
;;
esac

56
executor-scripts/linux/static Executable file
View file

@ -0,0 +1,56 @@
#!/bin/sh
[ -z "${VERBOSE}" ] || set -x
[ -z "${IF_METRIC}" ] && IF_METRIC="1"
[ -n "${IF_VRF_TABLE}" ] && VRF_TABLE="table ${IF_VRF_TABLE}"
[ -n "${IF_VRF_MEMBER}" ] && VRF_TABLE="vrf ${IF_VRF_MEMBER}"
[ -n "${IF_METRIC}" ] && METRIC="metric ${IF_METRIC}"
addr_family() {
if [ "$1" != "${1#*[0-9].[0-9]}" ]; then
echo "-4"
elif [ "$1" != "${1#*:[0-9a-fA-F]}" ]; then
echo "-6"
else
exit 1
fi
}
configure_addresses() {
for addr in ${IF_ADDRESSES}; do
addrfam=$(addr_family ${addr})
if [ "${IF_POINT_TO_POINT}" -a "${addrfam}" = "-4" ]; then
PEER="peer ${IF_POINT_TO_POINT}"
else
PEER=""
fi
${MOCK} ip "${addrfam}" addr add "${addr}" ${PEER} dev "${IFACE}"
done
}
configure_gateways() {
for gw in ${IF_GATEWAYS}; do
addrfam=$(addr_family ${gw})
${MOCK} ip "${addrfam}" route add default via "${gw}" ${VRF_TABLE} ${METRIC} dev "${IFACE}"
done
}
flush() {
cmd="addr"
arg="dev ${IFACE}"
${MOCK} ip ${cmd} flush ${arg}
}
case "$PHASE" in
up)
configure_addresses add
configure_gateways add
;;
down)
flush
;;
*) exit 0 ;;
esac

128
executor-scripts/linux/tunnel Executable file
View file

@ -0,0 +1,128 @@
#!/bin/sh
# Based on alpine's tunnel configuration script.
# Copyright (c) 2017 Kaarle Ritvanen
# Copyright (c) 2020 Ariadne Conill (extended for ifupdown-ng)
# Copyright (c) 2021 Maximilian Wilhelm (make sure mode/type is 1st parameter, add more options)
[ -z "$IF_TUNNEL_LOCAL" -a -z "$IF_TUNNEL_LOCAL_DEV" ] && exit 1
[ -z "$IF_TUNNEL_REMOTE" ] && exit 1
[ -z "$IF_TUNNEL_MODE" ] && exit 1
[ -n "$VERBOSE" ] && set -x
yesno() {
case "$1" in
yes|1) echo 1 ;;
*) echo 0 ;;
esac
}
# Figure out address familiy
FAMILY="4"
case "$IF_TUNNEL_MODE" in
vti6|ipip6|ip6*)
FAMILY="6"
;;
esac
# Figure out object type - gretap tunnels have to create using ip link
# and therefor require 'type' keyword instead of 'mode'
OBJECT="tunnel"
TYPE_KW="mode"
case "${IF_TUNNEL_MODE}" in
*gretap)
OBJECT="link"
TYPE_KW="type"
# Strip possible "ip6" from tunnel mode
TUNNEL_MODE="gretap"
;;
*)
# Store tunnel type/mode separaltely and unset input variable to exclude it
# from PARAMS below
TUNNEL_MODE="$IF_TUNNEL_MODE"
unset IF_TUNNEL_MODE
;;
esac
# If 'tunnel-local <IP>' was not given but 'tunnel-local-dev <iface>' is given try
# to figure out the IP of the underlay device (wrt the address family used for this
# tunnel) and use this to set up the tunnel
if [ ${PHASE} = "create" -a ! "${IF_TUNNEL_LOCAL}" -a "${IF_TUNNEL_LOCAL_DEV}" ]; then
if [ "${FAMILY}" = "4" ]; then
ip=$(ip -4 -brief addr show dev "${IF_TUNNEL_LOCAL_DEV}" 2>/dev/null | awk '{ print $3 }' | cut -d/ -f1)
# For IPv6 we try to use a non-temporary address (-> privacy extensions)
else
# Get all IPv6 addres configured on $IF_TUNNEL_LOCAL_DEV which are not
# temporary (due to privacy extensions). We do not filter for "mgmtaddr"
# "scope global" etc. as we don't want to make further assumptions on
# whether a user wants to use a link local address configured to this interface.
#
# The assumption that a temporary address configured by PE isn't suited
# to terminate a tunnel should hold in nearly all setups, I hope.
ip=$(ip -6 addr show dev "${IF_TUNNEL_LOCAL_DEV}" -temporary | grep inet6 | head -n1 | awk '{ print $2 }' | cut -d/ -f1)
fi
if [ ! "${ip}" ]; then
echo "Unable to determine the IPv${FAMILIY} address of tunnel-local-dev ${IF_TUNNEL_LOCAL_DEV}!"
exit 1
fi
unset IF_TUNNEL_LOCAL_DEV
export IF_TUNNEL_LOCAL="${ip}"
fi
# Handle boolean switches
MORE_PARAMS=""
if [ "${IF_TUNNEL_IGNORE_DF}" ]; then
if $(yesno "${IF_TUNNEL_IGNORE_DF}"); then
MORE_PARAMS="ignore-df"
else
MORE_PARAMS="noignore-df"
fi
unset IF_TUNNEL_IGNORE_DF
fi
if [ "${IF_TUNNEL_PMTUDISC}" ]; then
if $(yesno "${IF_TUNNEL_PMTUDISC}"); then
MORE_PARAMS="pmtudisc"
else
MORE_PARAMS="nopmtudisc"
fi
unset IF_TUNNEL_PMTUDISC
fi
# Mangle residual IF_TUNNEL_* params into single string
PARAMS=$(set | sed -E '
s/^IF_TUNNEL_([A-Z0-9_]+)=(.+)/\1\n\2/
ta
d
:a
h
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
P
g
s/.*\n//
')
[ "$PARAMS" ] || exit 0
case "$PHASE" in
create)
${MOCK} eval ip -$FAMILY $OBJECT add $IFACE $TYPE_KW $TUNNEL_MODE $PARAMS $MORE_PARAMS
;;
destroy)
${MOCK} ip -$FAMILY $OBJECT del $IFACE
;;
depend)
echo "${IF_TUNNEL_DEV}" "${IF_TUNNEL_LOCAL_DEV}"
;;
esac

30
executor-scripts/linux/vrf Executable file
View file

@ -0,0 +1,30 @@
#!/bin/sh
handle_init() {
${MOCK} /sbin/ip link $1 $IFACE type vrf table $IF_VRF_TABLE
${MOCK} /sbin/ip rule $1 iif $IFACE table $IF_VRF_TABLE
${MOCK} /sbin/ip rule $1 oif $IFACE table $IF_VRF_TABLE
}
handle_member() {
${MOCK} /sbin/ip link set $IFACE master $IF_VRF_MEMBER
}
[ -n "$VERBOSE" ] && set -x
case "$PHASE" in
create)
[ -n "$IF_VRF_TABLE" ] && handle_init "add"
;;
pre-up)
[ -n "$IF_VRF_MEMBER" ] && handle_member
;;
destroy)
[ -n "$IF_VRF_TABLE" ] && handle_init "del"
;;
depend)
echo "$IF_VRF_MEMBER"
;;
*)
exit 0
;;
esac

96
executor-scripts/linux/vxlan Executable file
View file

@ -0,0 +1,96 @@
#!/bin/sh
#
# This executor is responsible for setting up the Virtual Extensible LAN (VXLAN) overlay interfaces.
#
# Fri, 02 Oct 2020 01:10:29 +0200
# -- Maximilian Wilhelm <max@sdn.clinic>
#
# Known options for the main interface are:
#
# IF_VXLAN_ID The VXLAN Network Identifier (VNI)
# IF_VXLAN_PHYSDEV Specifies the physical device to use for tunnel endpoint communication
# IF_VXLAN_LOCAL_IP Specifies the source IP address to use in outgoing packets
# IF_VXLAN_PEER_IPS Space separated list of IPs of the remote VTEP endpoint (for ptp/ptmp mode with ingress replication)
# IF_VXLAN_PEER_GROUP Multicast group to use for this VNI (for ptmp mode with multicast)
# IF_VXLAN_LEARNING Wether to activate MAC learning on this instance (on/off)
# IF_VXLAN_AGEING Specifies the lifetime in seconds of FDB entries learnt by the kernel
# IF_VXLAN_DSTPORT UDP destination port to communicate to the remote VXLAN tunnel endpoint (default 4789)
#
[ -n "$VERBOSE" ] && set -x
# No VNI, nuthin' to do for us
if [ ! "${IF_VXLAN_ID}" ]; then
exit 0
fi
case "$PHASE" in
depend)
if [ "${IF_VXLAN_PHYSDEV}" ]; then
echo "${IF_VXLAN_PHYSDEV}"
fi
;;
create)
if [ -d "/sys/class/net/${IFACE}" ]; then
exit 0
fi
# Input validation
if [ "${IF_VXLAN_PEER_IPS}" -a "${IF_VXLAN_PEER_GROUP}" ]; then
echo "Error on ${IFACE} (vxlan): Only one of 'vxlan-peer-ips' and 'vxlan-peer-group' can be used!" >&2
exit 1
fi
# Check if we should operate in unicast ptp or ptmp mode
if [ "${IF_VXLAN_PEER_IPS}" ]; then
# If it's only one thing which looks like an IPv4/IPv6 address we assume it's ptp
if echo "${IF_VXLAN_PEER_IPS}" | grep -q '^[[:space:]]*[[:xdigit:].:]\+[[:space:]]*$'; then
UCAST_MODE="ptp"
else
UCAST_MODE="ptmp"
fi
fi
# Gather arguments
ARGS=""
[ "${IF_VXLAN_PHYSDEV}" ] && ARGS="${ARGS} dev ${IF_VXLAN_PHYSDEV}"
[ "${IF_VXLAN_LOCAL_IP}" ] && ARGS="${ARGS} local ${IF_VXLAN_LOCAL_IP}"
[ "${UCAST_MODE}" = "ptp" ] && ARGS="${ARGS} remote ${IF_VXLAN_PEER_IPS}"
[ "${IF_VXLAN_PEER_GROUP}" ] && ARGS="${ARGS} group ${IF_VXLAN_PEER_GROUP}"
[ "${IF_VXLAN_AGEING}" ] && ARGS="${ARGS} ageing ${IF_VXLAN_AGEING}"
# Linux uses non-standard default port - WTF?
if [ "${IF_VXLAN_DSTPORT}" ]; then
ARGS="${ARGS} dstport ${IF_VXLAN_DSTPORT}"
else
ARGS="${ARGS} dstport 4789"
fi
case "${IF_VXLAN_LEARNING}" in
on|yes)
ARGS="${ARGS} learning"
;;
off|no)
ARGS="${ARGS} nolearning"
;;
esac
${MOCK} ip link add "${IFACE}" type vxlan id "${IF_VXLAN_ID}" ${ARGS}
# Set up FDB entries for peer VTEPs
if [ "${UCAST_MODE}" = "ptmp" ]; then
for peer in ${IF_VXLAN_PEER_IPS}; do
${MOCK} bridge fdb append 00:00:00:00:00:00 dev "${IFACE}" dst "${peer}" self permanent
done
fi
;;
destroy)
if [ -z "${MOCK}" -a ! -d "/sys/class/net/${IFACE}" ]; then
exit 0
fi
${MOCK} ip link del "${IFACE}"
;;
esac

119
executor-scripts/linux/wifi Executable file
View file

@ -0,0 +1,119 @@
#!/bin/sh
# Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# This software is provided 'as is' and without any warranty, express or
# implied. In no event shall the authors be liable for any damages arising
# from the use of this software.
#
# Manage wifi connections using wpa_supplicant.
#
# Vocabulary:
# wifi-ssid - The SSID name to connect to.
# wifi-psk - The pre-shared key to use.
# wifi-config - A path to a wpa_supplicant config file, for special configs.
#
# If wifi-config is not set, wifi-ssid and wifi-psk are required, and a config
# will be generated as /run/wpa_supplicant.$IFACE.conf.
#
# The wpa_supplicant PID is stored in /run/wpa_supplicant.$IFACE.pid.
die() {
printf "ERROR: %s\n" "$1" >&2
exit 1
}
[ -z "$IFACE" ] && die "IFACE not set"
[ -z "$PHASE" ] && die "PHASE not set"
PIDFILE="/run/wpa_supplicant.$IFACE.pid"
# Do not allow mixing wifi-config-path and wifi-ssid/wifi-psk.
[ -n "$IF_WIFI_CONFIG_PATH" -a -n "$IF_WIFI_SSID" ] && die "wifi-config-path cannot be used with wifi-ssid"
[ -n "$IF_WIFI_CONFIG_PATH" -a -n "$IF_WIFI_PSK" ] && die "wifi-config-path cannot be used with wifi-psk"
# Set IF_WIFI_CONFIG_PATH to the default path if not already set.
WIFI_CONFIG_PATH="$IF_WIFI_CONFIG_PATH"
[ -z "$WIFI_CONFIG_PATH" ] && WIFI_CONFIG_PATH="/run/wpa_supplicant.$IFACE.conf"
# Supplicant options.
WPA_SUPPLICANT_OPTS="-qq -B -i$IFACE -c$WIFI_CONFIG_PATH -P$PIDFILE"
# Given $IF_WIFI_SSID and $IF_WIFI_PSK, generate a config file at $WIFI_CONFIG_PATH.
generate_config() {
[ -z "$IF_WIFI_SSID" ] && die "wifi-ssid not set"
[ -z "$IF_WIFI_PSK" ] && die "wifi-psk not set"
# We use a pipeline here to avoid leaking PSK into the process name.
(echo $IF_WIFI_PSK | /sbin/wpa_passphrase $IF_WIFI_SSID) >$WIFI_CONFIG_PATH
[ ! -e "$WIFI_CONFIG_PATH" ] && die "failed to write temporary config: $WIFI_CONFIG_PATH"
}
# Should we use the supplicant?
use_supplicant() {
[ -n "$IF_WIFI_CONFIG_PATH" ] && return 0
[ -n "$IF_WIFI_PSK" ] && return 0
return 1
}
# Either start a supplicant process for $IFACE, or use iwconfig to trigger an
# association attempt.
start() {
if use_supplicant; then
# If there is no config file located at $WIFI_CONFIG_PATH, generate one.
[ ! -e "$WIFI_CONFIG_PATH" ] && generate_config
/sbin/wpa_supplicant $WPA_SUPPLICANT_OPTS
else
/usr/sbin/iwconfig $IFACE essid -- "$IF_WIFI_SSID" ap any
fi
}
# Stop wpa_supplicant safely
stop_wpa_supplicant() {
# Remove generated config file
[ -z "$IF_WIFI_CONFIG_PATH" ] && rm -- "$WIFI_CONFIG_PATH"
# If there is no PIDFILE, there is nothing we can do
[ ! -f "$PIDFILE" ] && return
pid=$(cat "$PIDFILE")
rm -- "$PIDFILE"
# If there is no process with this PID running, we're done here
if [ ! -d "/proc/$pid/" ]; then
return
fi
# Verify that the name of the running process matches wpa_supplicant
progname_path=$(readlink -n "/proc/$pid/exe")
progname=$(basename "$progname_path")
if [ "$progname" = "wpa_supplicant" ]; then
kill -9 $pid 2>/dev/null
fi
}
# Either stop the supplicant process for $IFACE, or use iwconfig to dissociate
# from the current SSID.
stop() {
if use_supplicant; then
stop_wpa_supplicant
else
/usr/sbin/iwconfig $IFACE essid any
fi
}
[ -z "$VERBOSE" ] || set -x
case "$PHASE" in
pre-up)
start
;;
post-down)
stop
;;
esac

View file

@ -0,0 +1,15 @@
#!/bin/sh
[ -n "$VERBOSE" ] && set -x
[ -z "$IF_WIREGUARD_CONFIG_PATH" ] && IF_WIREGUARD_CONFIG_PATH="/etc/wireguard/$IFACE.conf"
case "$PHASE" in
create)
${MOCK} ip link add $IFACE type wireguard
;;
pre-up)
${MOCK} wg setconf $IFACE $IF_WIREGUARD_CONFIG_PATH
;;
destroy)
${MOCK} ip link delete dev $IFACE
;;
esac

5
executor-scripts/stub/bond Executable file
View file

@ -0,0 +1,5 @@
#!/bin/sh
[ -z "$IF_BOND_MEMBERS" ] && IF_BOND_MEMBERS="$IF_BOND_SLAVES"
case "$PHASE" in
depend) echo "$IF_BOND_MEMBERS" ;;
esac

8
executor-scripts/stub/bridge Executable file
View file

@ -0,0 +1,8 @@
#!/bin/sh
case "$PHASE" in
depend)
if [ "$IF_BRIDGE_PORTS" != "none" ]; then
echo "$IF_BRIDGE_PORTS"
fi
;;
esac

114
libifupdown/compat.c Normal file
View file

@ -0,0 +1,114 @@
/*
* libifupdown/compat.c
* Purpose: compatiblity glue to other implementations
*
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "libifupdown/compat.h"
#include "libifupdown/config-file.h"
#include "libifupdown/dict.h"
#include "libifupdown/interface.h"
#include "libifupdown/tokenize.h"
static bool
compat_ifupdown2_bridge_ports_inherit_vlans(struct lif_dict *collection)
{
struct lif_node *iter;
/* Loop through all interfaces and search for bridges */
LIF_DICT_FOREACH(iter, collection)
{
struct lif_dict_entry *entry = iter->data;
struct lif_interface *bridge = entry->data;
/* We only care for bridges */
if (!bridge->is_bridge)
continue;
struct lif_dict_entry *bridge_pvid = lif_dict_find(&bridge->vars, "bridge-pvid");
struct lif_dict_entry *bridge_vids = lif_dict_find(&bridge->vars, "bridge-vids");
/* If there's nothing to inherit here, carry on */
if (bridge_pvid == NULL && bridge_vids == NULL)
continue;
struct lif_dict_entry *bridge_ports_entry = lif_dict_find(&bridge->vars, "bridge-ports");
/* This SHOULD not happen, but better save than sorry */
if (bridge_ports_entry == NULL)
continue;
char bridge_ports_str[4096] = {};
strlcpy(bridge_ports_str, bridge_ports_entry->data, sizeof bridge_ports_str);
/* If there are no bridge-ports configured, carry on */
if (strcmp(bridge_ports_str, "none") == 0)
continue;
/* Loop over all bridge-ports and set bridge-pvid and bridge-vid if not set already */
char *bufp = bridge_ports_str;
for (char *tokenp = lif_next_token(&bufp); *tokenp; tokenp = lif_next_token(&bufp))
{
entry = lif_dict_find(collection, tokenp);
/* There might be interfaces give within the bridge-ports for which there is no
* interface stanza. If this is the case, we add one, so we can inherit the
* bridge-vids/pvid to it. */
struct lif_interface *bridge_port;
if (entry)
bridge_port = entry->data;
else if (lif_config.compat_create_interfaces)
{
bridge_port = lif_interface_collection_find(collection, tokenp);
if (bridge_port == NULL)
{
fprintf(stderr, "Failed to add interface \"%s\"", tokenp);
return false;
}
}
/* We would have to creaet an interface, but shouldn't */
else
{
fprintf(stderr, "compat: Missing interface stanza for bridge-port \"%s\" but should not create one.\n",
tokenp);
continue;
}
/* Maybe pimp bridge-pvid */
struct lif_dict_entry *port_pvid = lif_dict_find(&bridge_port->vars, "bridge-pvid");
if (bridge_pvid && !port_pvid)
lif_dict_add(&bridge_port->vars, "bridge-pvid", bridge_pvid->data);
/* Maybe pimp bridge-vids */
struct lif_dict_entry *port_vids = lif_dict_find(&bridge_port->vars, "bridge-vids");
if (bridge_vids && !port_vids)
lif_dict_add(&bridge_port->vars, "bridge-vids", bridge_vids->data);
}
}
return true;
}
bool
lif_compat_apply(struct lif_dict *collection)
{
if (lif_config.compat_ifupdown2_bridge_ports_inherit_vlans &&
!compat_ifupdown2_bridge_ports_inherit_vlans(collection))
return false;
return true;
}

24
libifupdown/compat.h Normal file
View file

@ -0,0 +1,24 @@
/*
* libifupdown/compat.h
* Purpose: compatiblity glue to other implementations
*
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#ifndef LIBIFUPDOWN__COMPAT_H
#define LIBIFUPDOWN__COMPAT_H
#include "libifupdown/config-file.h"
#include "libifupdown/dict.h"
extern bool lif_compat_apply (struct lif_dict *collection);
#endif

75
libifupdown/config-file.c Normal file
View file

@ -0,0 +1,75 @@
/*
* libifupdown/config-file.c
* Purpose: config file loading
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include "libifupdown/libifupdown.h"
struct lif_config_file lif_config = {
.allow_addon_scripts = true,
.allow_any_iface_as_template = true,
.auto_executor_selection = true,
.compat_create_interfaces = true,
.compat_ifupdown2_bridge_ports_inherit_vlans = true,
.implicit_template_conversion = true,
.use_hostname_for_dhcp = true,
};
static bool
set_bool_value(const char *key, const char *value, void *opaque)
{
(void) key;
if (*value == '1' ||
*value == 'Y' || *value == 'y' ||
*value == 'T' || *value == 't')
*(bool *) opaque = true;
else if (*value == '0' ||
*value == 'N' || *value == 'n' ||
*value == 'F' || *value == 'f')
*(bool *) opaque = false;
else
return false;
return true;
}
static struct lif_config_handler handlers[] = {
{"allow_addon_scripts", set_bool_value, &lif_config.allow_addon_scripts},
{"allow_any_iface_as_template", set_bool_value, &lif_config.allow_any_iface_as_template},
{"auto_executor_selection", set_bool_value, &lif_config.auto_executor_selection},
{"compat_create_interfaces", set_bool_value, &lif_config.compat_create_interfaces},
{"compat_ifupdown2_bridge_ports_inherit_vlans", set_bool_value, &lif_config.compat_ifupdown2_bridge_ports_inherit_vlans},
{"implicit_template_conversion", set_bool_value, &lif_config.implicit_template_conversion},
{"use_hostname_for_dhcp", set_bool_value, &lif_config.use_hostname_for_dhcp},
};
bool
lif_config_load(const char *filename)
{
FILE *fd = fopen(filename, "r");
if (fd == NULL)
{
#if 0
fprintf(stderr, "ifupdown-ng: cannot open config %s: %s\n",
filename, strerror(errno));
#endif
return false;
}
return lif_config_parse_file(fd, filename, handlers, ARRAY_SIZE(handlers));
}

35
libifupdown/config-file.h Normal file
View file

@ -0,0 +1,35 @@
/*
* libifupdown/config-file.h
* Purpose: config file loading
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#ifndef LIBIFUPDOWN__CONFIG_FILE_H
#define LIBIFUPDOWN__CONFIG_FILE_H
#include <stdbool.h>
struct lif_config_file {
bool allow_addon_scripts;
bool allow_any_iface_as_template;
bool auto_executor_selection;
bool compat_create_interfaces;
bool compat_ifupdown2_bridge_ports_inherit_vlans;
bool implicit_template_conversion;
bool use_hostname_for_dhcp;
};
extern struct lif_config_file lif_config;
extern bool lif_config_load(const char *filename);
#endif

View file

@ -0,0 +1,87 @@
/*
* libifupdown/config-parser.c
* Purpose: config parsing
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "libifupdown/config-parser.h"
#include "libifupdown/fgetline.h"
#include "libifupdown/tokenize.h"
static int
handler_cmp(const void *a, const void *b)
{
const char *key = a;
const struct lif_config_handler *hdl = b;
return strcmp(key, hdl->key);
}
bool
lif_config_parse_file(FILE *fd, const char *filename, struct lif_config_handler *handlers, size_t handler_count)
{
char linebuf[4096];
size_t lineno = 0;
while (lif_fgetline(linebuf, sizeof linebuf, fd))
{
char *bufp = linebuf;
char *key = lif_next_token_eq(&bufp);
char *value = lif_next_token_eq(&bufp);
lineno++;
if (!*key || !*value)
continue;
if (*key == '#')
continue;
struct lif_config_handler *hdl = bsearch(key, handlers, handler_count, sizeof(*handlers),
handler_cmp);
if (hdl == NULL)
{
fprintf(stderr, "ifupdown-ng: %s:%zu: warning: unknown config setting %s\n",
filename, lineno, key);
continue;
}
if (!hdl->handle(key, value, hdl->opaque))
{
fclose(fd);
return false;
}
}
fclose(fd);
return true;
}
bool
lif_config_parse(const char *filename, struct lif_config_handler *handlers, size_t handler_count)
{
FILE *f = fopen(filename, "r");
if (f == NULL)
{
fprintf(stderr, "ifupdown-ng: unable to parse %s: %s\n", filename, strerror(errno));
return false;
}
return lif_config_parse_file(f, filename, handlers, handler_count);
}

View file

@ -0,0 +1,32 @@
/*
* libifupdown/config-parser.h
* Purpose: config parsing
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#ifndef LIBIFUPDOWN__CONFIG_PARSER_H
#define LIBIFUPDOWN__CONFIG_PARSER_H
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
struct lif_config_handler {
const char *key;
bool (*handle)(const char *key, const char *value, void *opaque);
void *opaque;
};
extern bool lif_config_parse_file(FILE *f, const char *filename, struct lif_config_handler *handlers, size_t handler_count);
extern bool lif_config_parse(const char *filename, struct lif_config_handler *handlers, size_t handler_count);
#endif

View file

@ -3,6 +3,7 @@
* Purpose: wrapping linked lists to provide a naive dictionary
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -13,6 +14,7 @@
* from the use of this software.
*/
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "libifupdown/dict.h"
@ -50,7 +52,41 @@ lif_dict_add(struct lif_dict *dict, const char *key, void *data)
}
struct lif_dict_entry *
lif_dict_find(struct lif_dict *dict, const char *key)
lif_dict_add_once(struct lif_dict *dict, const char *key, void *data,
lif_dict_cmp_t compar)
{
struct lif_list *existing = lif_dict_find_all(dict, key);
if (existing != NULL)
{
bool found = false;
struct lif_node *iter;
LIF_LIST_FOREACH(iter, existing->head)
{
if (!compar(data, iter->data))
{
found = true;
break;
}
}
lif_list_free_nodes(existing);
if (found)
return NULL;
}
struct lif_dict_entry *entry = calloc(1, sizeof *entry);
entry->key = strdup(key);
entry->data = data;
lif_node_insert_tail(&entry->node, entry, &dict->list);
return entry;
}
struct lif_dict_entry *
lif_dict_find(const struct lif_dict *dict, const char *key)
{
struct lif_node *iter;
@ -65,6 +101,31 @@ lif_dict_find(struct lif_dict *dict, const char *key)
return NULL;
}
struct lif_list *
lif_dict_find_all(const struct lif_dict *dict, const char *key)
{
struct lif_list *entries = calloc(1, sizeof *entries);
struct lif_node *iter;
LIF_LIST_FOREACH(iter, dict->list.head)
{
struct lif_dict_entry *entry = iter->data;
if (!strcmp(entry->key, key))
{
struct lif_node *new = calloc(1, sizeof *new);
lif_node_insert_tail(new, entry->data, entries);
}
}
if (entries->length == 0)
{
free(entries);
return NULL;
}
return entries;
}
void
lif_dict_delete(struct lif_dict *dict, const char *key)
{

View file

@ -3,6 +3,7 @@
* Purpose: wrapping linked lists to provide a naive dictionary
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -34,10 +35,17 @@ struct lif_dict_entry {
#define LIF_DICT_FOREACH_SAFE(iter, iter_next, dict) \
LIF_LIST_FOREACH_SAFE((iter), (iter_next), (dict)->list.head)
#define LIF_DICT_FOREACH_REVERSE(iter, dict) \
LIF_LIST_FOREACH_REVERSE((iter), (dict)->list.tail)
typedef int (*lif_dict_cmp_t)(const void *, const void *);
extern void lif_dict_init(struct lif_dict *dict);
extern void lif_dict_fini(struct lif_dict *dict);
extern struct lif_dict_entry *lif_dict_add(struct lif_dict *dict, const char *key, void *data);
extern struct lif_dict_entry *lif_dict_find(struct lif_dict *dict, const char *key);
extern struct lif_dict_entry *lif_dict_add_once(struct lif_dict *dict, const char *key, void *data, lif_dict_cmp_t compar);
extern struct lif_dict_entry *lif_dict_find(const struct lif_dict *dict, const char *key);
extern struct lif_list *lif_dict_find_all(const struct lif_dict *dict, const char *key);
extern void lif_dict_delete(struct lif_dict *dict, const char *key);
extern void lif_dict_delete_entry(struct lif_dict *dict, struct lif_dict_entry *entry);

View file

@ -20,14 +20,96 @@
#include <stdlib.h>
#include <string.h>
#include <spawn.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <poll.h>
#include "libifupdown/execute.h"
#define SHELL "/bin/sh"
#if defined(__linux__)
# include <sys/syscall.h>
#endif
/* POSIX compatible fallback using waitpid(2) and usleep(3) */
static inline bool
lif_process_monitor_busyloop(pid_t child, int timeout_sec, int *status)
{
int ticks = 0;
while (ticks < timeout_sec * 20)
{
/* Ugly hack: most executors finish very quickly,
* so give them a chance to finish before sleeping.
*/
usleep(50);
if (waitpid(child, status, WNOHANG) == child)
return true;
usleep(49950);
ticks++;
}
return false;
}
#if defined(__linux__) && defined(__NR_pidfd_open)
/* TODO: remove this wrapper once musl and glibc gain pidfd_open() directly. */
static inline int
lif_pidfd_open(pid_t pid, unsigned int flags)
{
return syscall(__NR_pidfd_open, pid, flags);
}
static inline bool
lif_process_monitor_procdesc(pid_t child, int timeout_sec, int *status)
{
int pidfd = lif_pidfd_open(child, 0);
/* pidfd_open() not available, fall back to busyloop */
if (pidfd == -1 && errno == ENOSYS)
return lif_process_monitor_busyloop(child, timeout_sec, status);
struct pollfd pfd = {
.fd = pidfd,
.events = POLLIN,
};
if (poll(&pfd, 1, timeout_sec * 1000) < 1)
return false;
waitpid(child, status, 0);
close(pidfd);
return true;
}
#endif
static inline bool
lif_process_monitor(const char *cmdbuf, pid_t child, int timeout_sec)
{
int status;
#if defined(__linux__) && defined(__NR_pidfd_open)
if (lif_process_monitor_procdesc(child, timeout_sec, &status))
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
#else
if (lif_process_monitor_busyloop(child, timeout_sec, &status))
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
#endif
fprintf(stderr, "execution of '%s': timeout after %d seconds\n", cmdbuf, timeout_sec);
kill(child, SIGKILL);
waitpid(child, &status, 0);
return false;
}
bool
lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...)
{
@ -53,8 +135,112 @@ lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const c
return false;
}
int status;
waitpid(child, &status, 0);
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
return lif_process_monitor(cmdbuf, child, opts->timeout);
}
bool
lif_execute_fmt_with_result(const struct lif_execute_opts *opts, char *buf, size_t bufsize, char *const envp[], const char *fmt, ...)
{
char cmdbuf[4096];
va_list va;
va_start(va, fmt);
vsnprintf(cmdbuf, sizeof cmdbuf, fmt, va);
va_end(va);
pid_t child;
char *argv[] = { SHELL, "-c", cmdbuf, NULL };
if (opts->verbose)
puts(cmdbuf);
if (opts->mock)
return true;
int pipefds[2];
if (pipe(pipefds) < 0)
{
fprintf(stderr, "execute '%s': %s\n", cmdbuf, strerror(errno));
return false;
}
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_init(&file_actions);
posix_spawn_file_actions_addclose(&file_actions, pipefds[0]);
posix_spawn_file_actions_adddup2(&file_actions, pipefds[1], 1);
posix_spawn_file_actions_addclose(&file_actions, pipefds[1]);
if (posix_spawn(&child, SHELL, &file_actions, NULL, argv, envp) != 0)
{
fprintf(stderr, "execute '%s': %s\n", cmdbuf, strerror(errno));
return false;
}
posix_spawn_file_actions_destroy(&file_actions);
close(pipefds[1]);
struct pollfd pfd = {
.fd = pipefds[0],
.events = POLLIN
};
if (poll(&pfd, 1, -1) < 1)
goto no_result;
if (read(pipefds[0], buf, bufsize) < 0)
{
fprintf(stderr, "reading from pipe: %s\n", strerror(errno));
return false;
}
no_result:
return lif_process_monitor(cmdbuf, child, opts->timeout);
}
bool
lif_file_is_executable(const char *path)
{
struct stat st;
if (stat(path, &st))
return false;
if (!S_ISREG(st.st_mode))
return false;
return !access(path, X_OK);
}
bool
lif_maybe_run_executor(const struct lif_execute_opts *opts, char *const envp[], const char *executor, const char *phase, const char *lifname)
{
if (opts->verbose)
fprintf(stderr, "ifupdown: %s: attempting to run %s executor for phase %s\n", lifname, executor, phase);
char pathbuf[4096];
snprintf(pathbuf, sizeof pathbuf, "%s/%s", opts->executor_path, executor);
if (!lif_file_is_executable(pathbuf))
return true;
return lif_execute_fmt(opts, envp, "%s", pathbuf);
}
bool
lif_maybe_run_executor_with_result(const struct lif_execute_opts *opts, char *const envp[], const char *executor, char *buf, size_t bufsize, const char *phase, const char *lifname)
{
if (opts->verbose)
fprintf(stderr, "ifupdown: %s: attempting to run %s executor for phase %s\n", lifname, executor, phase);
char pathbuf[4096];
snprintf(pathbuf, sizeof pathbuf, "%s/%s", opts->executor_path, executor);
if (!lif_file_is_executable(pathbuf))
return true;
return lif_execute_fmt_with_result(opts, buf, bufsize, envp, "%s", pathbuf);
}

View file

@ -22,8 +22,18 @@
struct lif_execute_opts {
bool verbose;
bool mock;
bool no_lock;
bool force;
const char *executor_path;
const char *interfaces_file;
const char *state_file;
int timeout;
};
extern bool lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...);
extern bool lif_execute_fmt_with_result(const struct lif_execute_opts *opts, char *buf, size_t bufsize, char *const envp[], const char *fmt, ...);
extern bool lif_file_is_executable(const char *path);
extern bool lif_maybe_run_executor(const struct lif_execute_opts *opts, char *const envp[], const char *executor, const char *phase, const char *lifname);
extern bool lif_maybe_run_executor_with_result(const struct lif_execute_opts *opts, char *const envp[], const char *executor, char *buf, size_t bufsize, const char *phase, const char *lifname);
#endif

View file

@ -3,6 +3,7 @@
* Purpose: /etc/network/interfaces parser
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,128 +15,519 @@
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include "libifupdown/interface-file.h"
#include "libifupdown/fgetline.h"
#include "libifupdown/tokenize.h"
#include <dirent.h>
#include <errno.h>
#include "libifupdown/libifupdown.h"
/* internally rewrite problematic ifupdown2 tokens to ifupdown-ng equivalents */
struct remap_token {
const char *token;
const char *alternative;
};
/* this list must be in alphabetical order for bsearch */
static const struct remap_token tokens[] = {
{"bond-ad-sys-priority", "bond-ad-actor-sys-prio"}, /* ifupdown2 */
{"bond-slaves", "bond-members"}, /* legacy ifupdown, ifupdown2 */
{"client", "dhcp-client-id"}, /* legacy ifupdown */
{"driver-message-level", "ethtool-msglvl"}, /* Debian ethtool integration */
{"endpoint", "tunnel-remote"}, /* legacy ifupdown */
{"ethernet-autoneg", "ethtool-ethernet-autoneg"}, /* Debian ethtool integration */
{"ethernet-pause-autoneg", "ethtool-pause-autoneg"}, /* Debian ethtool integration */
{"ethernet-pause-rx", "ethtool-pause-rx"}, /* Debian ethtool integration */
{"ethernet-pause-tx", "ethtool-pause-tx"}, /* Debian ethtool integration */
{"ethernet-port", "ethtool-ethernet-port"}, /* Debian ethtool integration */
{"ethernet-wol", "ethtool-ethernet-wol"}, /* Debian ethtool integration */
{"gro-offload", "ethtool-offload-gro"}, /* ifupdown2 */
{"gso-offload", "ethtool-offload-gso"}, /* ifupdown2 */
{"hardware-dma-ring-rx", "ethtool-dma-ring-rx"}, /* Debian ethtool integration */
{"hardware-dma-ring-rx-jumbo", "ethtool-dma-ring-rx-jumbo"}, /* Debian ethtool integration */
{"hardware-dma-ring-rx-mini", "ethtool-dma-ring-rx-mini"}, /* Debian ethtool integration */
{"hardware-dma-ring-tx", "ethtool-dma-ring-tx"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-adaptive-rx", "ethtool-coalesce-adaptive-rx"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-adaptive-tx", "ethtool-coalesce-adaptive-tx"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-pkt-rate-high", "ethtool-coalesce-pkt-rate-high"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-pkt-rate-low", "ethtool-coalesce-pkt-rate-low"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-frames", "ethtool-coalesce-rx-frames"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-frames-high", "ethtool-coalesce-rx-frames-high"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-frames-irq", "ethtool-coalesce-rx-frames-irq"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-frames-low", "ethtool-coalesce-rx-frames-low"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-usecs", "ethtool-coalesce-rx-usecs"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-usecs-high", "ethtool-coalesce-rx-usecs-high"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-usecs-irq", "ethtool-coalesce-rx-usecs-irq"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-rx-usecs-low", "ethtool-coalesce-rx-usecs-low"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-sample-interval", "ethtool-coalesce-sample-interval"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-stats-block-usecs", "ethtool-coalesce-stats-block-usecs"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-frames", "ethtool-coalesce-tx-frames"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-frames-high", "ethtool-coalesce-tx-frames-high"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-frames-irq", "ethtool-coalesce-tx-frames-irq"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-frames-low", "ethtool-coalesce-tx-frames-low"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-usecs", "ethtool-coalesce-tx-usecs"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-usecs-high", "ethtool-coalesce-tx-usecs-high"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-usecs-irq", "ethtool-coalesce-tx-usecs-irq"}, /* Debian ethtool integration */
{"hardware-irq-coalesce-tx-usecs-low", "ethtool-coalesce-tx-usecs-low"}, /* Debian ethtool integration */
{"hostname", "dhcp-hostname"}, /* legacy ifupdown */
{"key", "tunnel-key"}, /* legacy ifupdown */
{"leasetime", "dhcp-leasetime"}, /* legacy ifupdown */
{"link-autoneg", "ethtool-ethernet-autoneg"}, /* ifupdown2 */
{"link-duplex", "ethtool-link-duplex"}, /* Debian ethtool integration */
{"link-fec", "ethtool-link-fec"}, /* ifupdown2 */
{"link-speed", "ethtool-link-speed"}, /* Debian ethtool integration */
{"local", "tunnel-local"}, /* legacy ifupdown */
{"lro-offload", "ethtool-offload-lro"}, /* ifupdown2 */
{"mode", "tunnel-mode"}, /* legacy ifupdown */
{"offload-gro", "ethtool-offload-gro"}, /* Debian ethtool integration */
{"offload-gso", "ethtool-offload-gso"}, /* Debian ethtool integration */
{"offload-lro", "ethtool-offload-lro"}, /* Debian ethtool integration */
{"offload-rx", "ethtool-offload-rx"}, /* Debian ethtool integration */
{"offload-sg", "ethtool-offload-sg"}, /* Debian ethtool integration */
{"offload-tso", "ethtool-offload-tso"}, /* Debian ethtool integration */
{"offload-tx", "ethtool-offload-tx"}, /* Debian ethtool integration */
{"offload-ufo", "ethtool-offload-ufo"}, /* Debian ethtool integration */
{"pointopoint", "point-to-point"}, /* legacy ifupdown, ifupdown2 */
{"provider", "ppp-provider"}, /* legacy ifupdown, ifupdown2 */
{"script", "dhcp-script"}, /* legacy ifupdown */
{"rx-offload", "ethtool-offload-rx"}, /* ifupdown2 */
{"tso-offload", "ethtool-offload-tso"}, /* ifupdown2 */
{"ttl", "tunnel-ttl"}, /* legacy ifupdown */
{"tunnel-endpoint", "tunnel-remote"}, /* ifupdown2 */
{"tunnel-physdev", "tunnel-dev"}, /* ifupdown2 */
{"tx-offload", "ethtool-offload-tx"}, /* ifupdown2 */
{"ufo-offload", "ethtool-offload-ufo"}, /* ifupdown2 */
{"vendor", "dhcp-vendor"}, /* legacy ifupdown */
{"vrf", "vrf-member"}, /* ifupdown2 */
{"vxlan-local-tunnelip", "vxlan-local-ip"}, /* ifupdown2 */
{"vxlan-remote-group", "vxlan-peer-group"}, /* ifupdown-ng */
{"vxlan-remoteip", "vxlan-peer-ips"}, /* ifupdown2 */
{"vxlan-remote-ip", "vxlan-peer-ips"}, /* ifupdown-ng */
{"vxlan-svcnodeip", "vxlan-peer-group"}, /* ifupdown2 */
};
static int
token_cmp(const void *a, const void *b)
{
const char *key = a;
const struct remap_token *token = b;
return strcmp(key, token->token);
}
static char *
maybe_remap_token(const char *token)
{
const struct remap_token *tok = NULL;
static char tokbuf[4096];
tok = bsearch(token, tokens, ARRAY_SIZE(tokens), sizeof(*tokens), token_cmp);
strlcpy(tokbuf, tok != NULL ? tok->alternative : token, sizeof tokbuf);
return tokbuf;
}
static void
report_error(struct lif_interface_file_parse_state *state, const char *errfmt, ...)
{
char errbuf[4096];
va_list va;
va_start(va, errfmt);
vsnprintf(errbuf, sizeof errbuf, errfmt, va);
va_end(va);
fprintf(stderr, "%s:%zu: %s\n", state->cur_filename, state->cur_lineno, errbuf);
}
static bool
handle_address(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
(void) token;
char *addr = lif_next_token(&bufp);
if (state->cur_iface == NULL)
{
report_error(state, "%s '%s' without interface", token, addr);
/* Ignore this address, but don't fail hard */
return true;
}
lif_interface_address_add(state->cur_iface, addr);
return true;
}
static bool
handle_auto(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
(void) token;
char *ifname = lif_next_token(&bufp);
if (!*ifname && state->cur_iface == NULL)
{
report_error(state, "auto without interface");
return true;
}
else
{
state->cur_iface = lif_interface_collection_find(state->collection, ifname);
if (state->cur_iface == NULL)
return false;
}
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;
}
static bool
handle_gateway(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
(void) token;
char *addr = lif_next_token(&bufp);
if (state->cur_iface == NULL)
{
report_error(state, "%s '%s' without interface", token, addr);
/* Ignore this gateway, but don't fail hard */
return true;
}
lif_interface_use_executor(state->cur_iface, "static");
lif_dict_add(&state->cur_iface->vars, token, strdup(addr));
return true;
}
static bool
handle_generic(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
if (state->cur_iface == NULL)
return true;
token = maybe_remap_token(token);
/* This smells like a bridge */
if (strcmp(token, "bridge-ports") == 0)
state->cur_iface->is_bridge = true;
/* Skip any leading whitespaces in value for <token> */
while (isspace (*bufp))
bufp++;
lif_dict_add(&state->cur_iface->vars, token, strdup(bufp));
if (!lif_config.auto_executor_selection)
return true;
/* Check if token looks like <word1>-<word*> and assume <word1> is an addon */
char *word_end = strchr(token, '-');
if (word_end != NULL)
{
/* Copy word1 to not mangle *token */
char *addon = strndup(token, word_end - token);
lif_interface_use_executor(state->cur_iface, addon);
free(addon);
}
return true;
}
static bool
handle_hostname(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
char *hostname = lif_next_token(&bufp);
if (state->cur_iface == NULL)
{
report_error(state, "%s '%s' without interface", token, hostname);
/* Ignore this hostname, but don't fail hard */
return true;
}
lif_dict_delete(&state->cur_iface->vars, "dhcp-hostname");
lif_dict_add(&state->cur_iface->vars, "dhcp-hostname", strdup(hostname));
return true;
}
static bool handle_inherit(struct lif_interface_file_parse_state *state, char *token, char *bufp);
static bool
handle_iface(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
char *ifname = lif_next_token(&bufp);
if (!*ifname)
{
report_error(state, "%s without any other tokens", token);
/* This is broken but not fatal */
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)
{
report_error(state, "could not upsert interface %s", ifname);
return false;
}
/* mark the state->cur_iface as a template iface if `template` keyword
* is used.
*/
if (!strcmp(token, "template"))
{
state->cur_iface->is_auto = false;
state->cur_iface->is_template = true;
}
/* in original ifupdown config, we can have "inet loopback"
* or "inet dhcp" or such to designate hints. lets pick up
* those hints here.
*/
token = lif_next_token(&bufp);
while (*token)
{
if (!strcmp(token, "dhcp"))
lif_interface_use_executor(state->cur_iface, "dhcp");
else if (!strcmp(token, "ppp"))
lif_interface_use_executor(state->cur_iface, "ppp");
else if (!strcmp(token, "inherits"))
{
if (!handle_inherit(state, token, bufp))
return false;
}
token = lif_next_token(&bufp);
}
return true;
}
static bool
handle_inherit(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
char *target = lif_next_token(&bufp);
if (state->cur_iface == NULL)
{
report_error(state, "%s '%s' without interface", token, target);
/* This is broken but not fatal */
return true;
}
if (!*target)
{
report_error(state, "iface %s: unspecified inherit target", state->cur_iface->ifname);
/* Mark this interface as errornous but carry on */
state->cur_iface->has_config_error = true;
return true;
}
struct lif_interface *parent = lif_interface_collection_find(state->collection, target);
if (parent == NULL)
{
report_error(state, "iface %s: could not inherit from %s: not found",
state->cur_iface->ifname, target);
/* Mark this interface as errornous but carry on */
state->cur_iface->has_config_error = true;
return true;
}
if (!lif_config.allow_any_iface_as_template && !parent->is_template)
{
report_error(state, "iface %s: could not inherit from %ss: inheritence from non-template interface not allowed",
state->cur_iface->ifname, target);
/* Mark this interface as errornous but carry on */
state->cur_iface->has_config_error = true;
return true;
}
if (!lif_interface_collection_inherit(state->cur_iface, parent))
{
report_error(state, "iface %s: could not inherit from %s", state->cur_iface->ifname, target);
/* Mark this interface as errornous but carry on */
state->cur_iface->has_config_error = true;
return true;
}
return true;
}
static bool
handle_source(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
(void) token;
char *source_filename = lif_next_token(&bufp);
if (!*source_filename)
{
report_error(state, "missing filename to source");
/* Broken but not fatal */
return true;
}
return lif_interface_file_parse(state, source_filename);
}
static bool
handle_source_directory(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
(void) token;
char *source_directory = lif_next_token(&bufp);
if (!*source_directory)
{
report_error(state, "missing directory to source");
/* Broken but not fatal */
return true;
}
DIR *source_dir = opendir(source_directory);
if (source_dir == NULL)
{
report_error(state, "while opening directory %s: %s", source_directory, strerror(errno));
/* Broken but not fatal */
return true;
}
struct dirent *dirent_p;
for (dirent_p = readdir(source_dir); dirent_p != NULL; dirent_p = readdir(source_dir))
{
if (dirent_p->d_type != DT_REG)
continue;
char pathbuf[4096];
snprintf(pathbuf, sizeof pathbuf, "%s/%s", source_directory, dirent_p->d_name);
if (!lif_interface_file_parse(state, pathbuf))
{
closedir(source_dir);
return false;
}
}
closedir(source_dir);
return true;
}
static bool
handle_use(struct lif_interface_file_parse_state *state, char *token, char *bufp)
{
char *executor = lif_next_token(&bufp);
if (state->cur_iface == NULL)
{
report_error(state, "%s '%s' without interface", token, executor);
/* Broken but not fatal */
return true;
}
lif_interface_use_executor(state->cur_iface, executor);
return true;
}
/* map keywords to parser functions */
struct parser_keyword {
const char *token;
bool (*handle)(struct lif_interface_file_parse_state *state, char *token, char *bufp);
};
static const struct parser_keyword keywords[] = {
{"address", handle_address},
{"auto", handle_auto},
{"dhcp-hostname", handle_hostname},
{"gateway", handle_gateway},
{"hostname", handle_hostname},
{"iface", handle_iface},
{"inherit", handle_inherit},
{"interface", handle_iface},
{"source", handle_source},
{"source-directory", handle_source_directory},
{"template", handle_iface},
{"use", handle_use},
};
static int
keyword_cmp(const void *a, const void *b)
{
const char *key = a;
const struct parser_keyword *token = b;
return strcmp(key, token->token);
}
bool
lif_interface_file_parse(struct lif_dict *collection, const char *filename)
lif_interface_file_parse(struct lif_interface_file_parse_state *state, const char *filename)
{
lif_interface_collection_init(collection);
struct lif_interface *cur_iface = NULL;
struct lif_dict_entry *entry = lif_dict_find(&state->loaded, filename);
if (entry != NULL)
{
report_error(state, "skipping already included file %s", filename);
return true;
}
FILE *f = fopen(filename, "r");
if (f == NULL)
return false;
const char *old_filename = state->cur_filename;
state->cur_filename = filename;
size_t old_lineno = state->cur_lineno;
state->cur_lineno = 0;
lif_dict_add(&state->loaded, filename, NULL);
char linebuf[4096];
while (lif_fgetline(linebuf, sizeof linebuf, f) != NULL)
{
state->cur_lineno++;
char *bufp = linebuf;
char *token = lif_next_token(&bufp);
if (!*token || !isalpha(*token))
continue;
if (!strcmp(token, "source"))
const struct parser_keyword *parserkw =
bsearch(token, keywords, ARRAY_SIZE(keywords), sizeof(*keywords), keyword_cmp);
if (parserkw != NULL)
{
char *source_filename = lif_next_token(&bufp);
if (!*source_filename)
if (!parserkw->handle(state, token, bufp))
goto parse_error;
if (!strcmp(filename, source_filename))
{
fprintf(stderr, "%s: attempt to source %s would create infinite loop\n",
filename, source_filename);
goto parse_error;
}
lif_interface_file_parse(collection, source_filename);
}
else if (!strcmp(token, "auto"))
{
char *ifname = lif_next_token(&bufp);
if (!*ifname && cur_iface == NULL)
goto parse_error;
else
{
cur_iface = lif_interface_collection_find(collection, ifname);
if (cur_iface == NULL)
goto parse_error;
}
cur_iface->is_auto = true;
}
else if (!strcmp(token, "iface"))
{
char *ifname = lif_next_token(&bufp);
if (!*ifname)
goto parse_error;
cur_iface = lif_interface_collection_find(collection, ifname);
if (cur_iface == NULL)
goto parse_error;
/* in original ifupdown config, we can have "inet loopback"
* or "inet dhcp" or such to designate hints. lets pick up
* those hints here.
*/
char *inet_type = lif_next_token(&bufp);
if (!*inet_type)
continue;
char *hint = lif_next_token(&bufp);
if (!*hint)
continue;
if (!strcmp(hint, "dhcp"))
{
cur_iface->is_dhcp = true;
lif_dict_add(&cur_iface->vars, "use", strdup("dhcp"));
}
}
else if (!strcmp(token, "use"))
{
char *executor = lif_next_token(&bufp);
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
if (!strcmp(executor, "dhcp"))
cur_iface->is_dhcp = true;
else if (!strcmp(executor, "loopback"))
cur_iface->is_loopback = true;
else if (!strcmp(executor, "bridge"))
cur_iface->is_bridge = true;
else if (!strcmp(executor, "bond"))
cur_iface->is_bond = true;
lif_dict_add(&cur_iface->vars, token, strdup(executor));
}
else if (!strcmp(token, "address"))
{
char *addr = lif_next_token(&bufp);
if (cur_iface == NULL)
{
fprintf(stderr, "%s: address '%s' without interface\n", filename, addr);
goto parse_error;
}
lif_interface_address_add(cur_iface, addr);
}
else if (cur_iface != NULL)
{
lif_dict_add(&cur_iface->vars, token, strdup(bufp));
}
else if (!handle_generic(state, token, bufp))
goto parse_error;
}
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;
parse_error:
fprintf(stderr, "libifupdown: %s: failed to parse line \"%s\"\n",
filename, linebuf);
fclose(f);
state->cur_filename = old_filename;
state->cur_lineno = old_lineno;
return false;
}

View file

@ -18,7 +18,17 @@
#include <stdbool.h>
#include "libifupdown/interface.h"
#include "libifupdown/dict.h"
extern bool lif_interface_file_parse(struct lif_dict *collection, const char *filename);
struct lif_interface_file_parse_state {
struct lif_interface *cur_iface;
struct lif_dict *collection;
const char *cur_filename;
size_t cur_lineno;
struct lif_dict loaded;
};
extern bool lif_interface_file_parse(struct lif_interface_file_parse_state *state, const char *filename);
#endif

View file

@ -3,6 +3,7 @@
* Purpose: interface management
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -15,7 +16,9 @@
#include <stdio.h>
#include <string.h>
#include <sys/utsname.h>
#include "libifupdown/interface.h"
#include "libifupdown/config-file.h"
bool
lif_address_parse(struct lif_address *address, const char *presentation)
@ -56,12 +59,75 @@ lif_address_unparse(const struct lif_address *address, char *buf, size_t buflen,
return true;
}
static inline size_t
count_set_bits(const char *netmask)
{
/* netmask set to CIDR length */
if (strchr(netmask, '.') == NULL)
return strtol(netmask, NULL, 10);
size_t r = 0;
struct in_addr in;
if (inet_pton(AF_INET, netmask, &in) == 0)
return r;
/* take the IP, put it in host endian order, and
* flip it so that all the set bits are set to the right.
* then we can simply count down from 32 and right-shift
* until the bit field is all zero.
*/
unsigned int bits = htonl(in.s_addr);
for (bits = ~bits, r = 32; bits; bits >>= 1, 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
lif_address_format_cidr(const struct lif_interface *iface, struct lif_dict_entry *entry, char *buf, size_t buflen)
{
struct lif_address *addr = entry->data;
size_t orig_netmask = addr->netmask;
if (!addr->netmask)
addr->netmask = determine_interface_netmask(iface, addr);
if (!lif_address_unparse(addr, buf, buflen, true))
{
addr->netmask = orig_netmask;
return false;
}
addr->netmask = orig_netmask;
return true;
}
void
lif_interface_init(struct lif_interface *interface, const char *ifname)
{
memset(interface, '\0', sizeof *interface);
interface->ifname = strdup(ifname);
lif_interface_use_executor(interface, "link");
/* keep the 'vlan' executor as a config hint for backwards compatibility */
if (strchr(ifname, '.') != NULL)
lif_interface_use_executor(interface, "vlan");
}
bool
@ -75,7 +141,10 @@ lif_interface_address_add(struct lif_interface *interface, const char *address)
return false;
}
lif_interface_use_executor(interface, "static");
lif_dict_add(&interface->vars, "address", addr);
return true;
}
@ -125,6 +194,61 @@ lif_interface_fini(struct lif_interface *interface)
free(interface->ifname);
}
void
lif_interface_use_executor(struct lif_interface *interface, const char *executor)
{
char *exec_addon = strdup(executor);
if (lif_dict_add_once(&interface->vars, "use", exec_addon, (lif_dict_cmp_t) strcmp) == NULL)
free(exec_addon);
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
if (!strcmp(executor, "bridge"))
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, "dhcp-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
lif_interface_collection_init(struct lif_dict *collection)
{
@ -134,10 +258,9 @@ 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 = if_lo->is_loopback = true;
lif_dict_add(&if_lo->vars, "use", strdup("loopback"));
lif_interface_address_add(if_lo, "127.0.0.1/8");
if_lo->is_auto = true;
if_lo->is_explicit = true;
lif_interface_use_executor(if_lo, "loopback");
}
void
@ -208,3 +331,41 @@ lif_interface_collection_delete(struct lif_dict *collection, struct lif_interfac
lif_dict_delete_entry(collection, entry);
}
bool
lif_interface_collection_inherit(struct lif_interface *interface, struct lif_interface *parent)
{
/* maybe convert any interface we are inheriting from into a template */
if (lif_config.implicit_template_conversion)
parent->is_template = true;
lif_dict_add(&interface->vars, "inherit", strdup(parent->ifname));
interface->is_bond = parent->is_bond;
interface->is_bridge = parent->is_bridge;
/* copy the variables */
struct lif_node *iter;
LIF_DICT_FOREACH(iter, &parent->vars)
{
struct lif_dict_entry *entry = iter->data;
if (!strcmp(entry->key, "address"))
{
struct lif_address *addr = calloc(1, sizeof *addr);
struct lif_address *other_addr = entry->data;
memcpy(addr, other_addr, sizeof *addr);
lif_dict_add(&interface->vars, entry->key, addr);
}
else
{
char *value = strdup(entry->data);
if (lif_dict_add_once(&interface->vars, entry->key, value, (lif_dict_cmp_t) strcmp) == NULL)
free(value);
}
}
return true;
}

View file

@ -3,6 +3,7 @@
* Purpose: interface management
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -31,9 +32,6 @@ struct lif_address {
int domain;
};
extern bool lif_address_parse(struct lif_address *address, const char *presentation);
extern bool lif_address_unparse(const struct lif_address *address, char *buf, size_t buflen, bool with_netmask);
/*
* Interfaces are contained in a dictionary, with the interfaces mapped by
* interface name to their `struct lif_interface`.
@ -47,15 +45,19 @@ extern bool lif_address_unparse(const struct lif_address *address, char *buf, si
struct lif_interface {
char *ifname;
bool is_dhcp;
bool is_loopback;
bool is_auto;
bool is_bridge;
bool is_bond;
bool is_template;
bool is_pending;
bool is_explicit;
bool has_config_error; /* error found in interface configuration */
struct lif_dict vars;
bool is_up;
size_t refcount; /* > 0 if up, else 0 */
size_t rdepends_count; /* > 0 if any reverse dependency */
};
#define LIF_INTERFACE_COLLECTION_FOREACH(iter, collection) \
@ -64,15 +66,22 @@ struct lif_interface {
#define LIF_INTERFACE_COLLECTION_FOREACH_SAFE(iter, iter_next, collection) \
LIF_DICT_FOREACH_SAFE((iter), (iter_next), (collection))
extern bool lif_address_parse(struct lif_address *address, const char *presentation);
extern bool lif_address_unparse(const struct lif_address *address, char *buf, size_t buflen, bool with_netmask);
extern bool lif_address_format_cidr(const struct lif_interface *iface, struct lif_dict_entry *entry, char *buf, size_t buflen);
extern void lif_interface_init(struct lif_interface *interface, const char *ifname);
extern bool lif_interface_address_add(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_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);
extern struct lif_interface *lif_interface_collection_find(struct lif_dict *collection, const char *ifname);
extern struct lif_interface *lif_interface_collection_upsert(struct lif_dict *collection, struct lif_interface *interface);
extern bool lif_interface_collection_inherit(struct lif_interface *interface, struct lif_interface *parent);
extern void lif_interface_collection_delete(struct lif_dict *collection, struct lif_interface *interface);
#endif

View file

@ -27,5 +27,12 @@
#include "libifupdown/execute.h"
#include "libifupdown/lifecycle.h"
#include "libifupdown/tokenize.h"
#include "libifupdown/config-file.h"
#include "libifupdown/config-parser.h"
#include "libifupdown/compat.h"
#ifndef ARRAY_SIZE
# define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
#endif
#endif

View file

@ -3,6 +3,7 @@
* Purpose: management of interface lifecycle (bring up, takedown, reload)
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -15,6 +16,9 @@
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "libifupdown/environment.h"
#include "libifupdown/execute.h"
@ -22,17 +26,18 @@
#include "libifupdown/lifecycle.h"
#include "libifupdown/state.h"
#include "libifupdown/tokenize.h"
#include "libifupdown/config-file.h"
#define BUFFER_LEN 4096
static bool
handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[], struct lif_interface *iface, const char *lifname, const char *phase)
handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[], const struct lif_interface *iface, const char *phase)
{
struct lif_node *iter;
(void) lifname;
const struct lif_node *iter;
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
const struct lif_dict_entry *entry = iter->data;
if (strcmp(entry->key, phase))
continue;
@ -45,171 +50,162 @@ handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[
return true;
}
static bool
handle_address(const struct lif_execute_opts *opts, struct lif_address *addr, const char *cmd, const char *lifname)
static inline bool
handle_single_executor_for_phase(const struct lif_dict_entry *entry, const struct lif_execute_opts *opts, char *const envp[], const char *phase, const char *lifname)
{
char addrbuf[4096];
if (!lif_address_unparse(addr, addrbuf, sizeof addrbuf, true))
return false;
return lif_execute_fmt(opts, NULL, "/sbin/ip -%d addr %s %s dev %s",
addr->domain == AF_INET ? 4 : 6, cmd, addrbuf, lifname);
}
static bool
handle_gateway(const struct lif_execute_opts *opts, const char *gateway, const char *cmd)
{
int ipver = strchr(gateway, ':') ? 6 : 4;
return lif_execute_fmt(opts, NULL, "/sbin/ip -%d route %s default via %s",
ipver, cmd, gateway);
}
static bool
handle_pre_up(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
{
(void) opts;
(void) iface;
(void) lifname;
return true;
}
static bool
handle_up(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
{
struct lif_node *iter;
if (!lif_execute_fmt(opts, NULL, "/sbin/ip link set up dev %s", lifname))
return false;
if (iface->is_loopback)
if (strcmp(entry->key, "use"))
return true;
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
if (!strcmp(entry->key, "address"))
{
struct lif_address *addr = entry->data;
if (!handle_address(opts, addr, "add", lifname))
return false;
}
else if (!strcmp(entry->key, "gateway"))
{
if (!handle_gateway(opts, entry->data, "add"))
return false;
}
}
if (iface->is_dhcp)
{
/* XXX: determine which dhcp client we should use */
if (!lif_execute_fmt(opts, NULL, "/sbin/udhcpc -b -R -p /var/run/udhcpc.%s.pid -i %s", lifname, lifname))
return false;
}
return true;
}
static bool
handle_down(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
{
struct lif_node *iter;
if (iface->is_loopback)
goto skip_addresses;
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
if (!strcmp(entry->key, "address"))
{
struct lif_address *addr = entry->data;
if (!handle_address(opts, addr, "del", lifname))
return false;
}
else if (!strcmp(entry->key, "gateway"))
{
if (!handle_gateway(opts, entry->data, "del"))
return false;
}
}
if (iface->is_dhcp)
{
/* XXX: determine which dhcp client we should use */
if (!lif_execute_fmt(opts, NULL, "/bin/kill $(cat /var/run/udhcpc.%s.pid)", lifname))
return false;
}
skip_addresses:
if (!lif_execute_fmt(opts, NULL, "/sbin/ip link set down dev %s", lifname))
const char *cmd = entry->data;
if (!lif_maybe_run_executor(opts, envp, cmd, phase, lifname))
return false;
return true;
}
static bool
handle_post_down(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
handle_executors_for_phase(const struct lif_execute_opts *opts, char *const envp[], const struct lif_interface *iface, bool up, const char *phase)
{
(void) opts;
(void) iface;
(void) lifname;
const struct lif_node *iter;
if (up)
{
LIF_DICT_FOREACH(iter, &iface->vars)
handle_single_executor_for_phase(iter->data, opts, envp, phase, iface->ifname);
}
else
{
LIF_DICT_FOREACH_REVERSE(iter, &iface->vars)
handle_single_executor_for_phase(iter->data, opts, envp, phase, iface->ifname);
}
return true;
}
bool
lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up)
static bool
query_dependents_from_executors(const struct lif_execute_opts *opts, char *const envp[], const struct lif_interface *iface, char *buf, size_t bufsize, const char *phase)
{
char **envp = NULL;
const struct lif_node *iter;
lif_environment_push(&envp, "IFACE", iface->ifname);
lif_environment_push(&envp, "PHASE", phase);
LIF_DICT_FOREACH(iter, &iface->vars)
{
char resbuf[1024] = {};
const struct lif_dict_entry *entry = iter->data;
struct lif_execute_opts exec_opts = {
.verbose = opts->verbose,
.executor_path = opts->executor_path,
.interfaces_file = opts->interfaces_file,
.timeout = opts->timeout,
};
/* try to provide $METHOD for ifupdown1 scripts if we can */
if (iface->is_loopback)
lif_environment_push(&envp, "METHOD", "loopback");
else if (iface->is_dhcp)
lif_environment_push(&envp, "METHOD", "dhcp");
if (strcmp(entry->key, "use"))
continue;
/* same for $MODE */
if (up)
lif_environment_push(&envp, "MODE", "start");
else
lif_environment_push(&envp, "MODE", "stop");
const char *cmd = entry->data;
if (!lif_maybe_run_executor_with_result(&exec_opts, envp, cmd, resbuf, sizeof resbuf, phase, iface->ifname))
return false;
struct lif_node *iter;
if (!*resbuf)
continue;
strlcat(buf, " ", bufsize);
strlcat(buf, resbuf, bufsize);
}
return true;
}
static bool
append_to_buffer(char **buffer, size_t *buffer_len, char **end, const char *value)
{
size_t value_len = strlen (value);
/* Make sure there is enough room to add the value to the buffer */
if (*buffer_len < strlen (*buffer) + value_len + 2)
{
size_t end_offset = *end - *buffer;
char *tmp = realloc (*buffer, *buffer_len * 2);
if (tmp != NULL)
{
*buffer = tmp;
*end = tmp + end_offset;
*buffer_len = *buffer_len * 2;
}
else
return false;
}
/* Append value to buffer */
size_t printed = snprintf (*end, value_len + 2, "%s ", value);
if (printed < value_len + 1)
/* Here be dragons */
return false;
/* Move end pointer to last printed byte */
*end += printed;
return true;
}
static void
build_environment(char **envp[], const struct lif_execute_opts *opts, const struct lif_interface *iface, const char *lifname, const char *phase, const char *mode)
{
if (lifname == NULL)
lifname = iface->ifname;
lif_environment_push(envp, "IFACE", lifname);
lif_environment_push(envp, "PHASE", phase);
lif_environment_push(envp, "MODE", mode);
lif_environment_push(envp, "METHOD", "none");
if (opts->verbose)
lif_environment_push(envp, "VERBOSE", "1");
if (opts->interfaces_file)
lif_environment_push(envp, "INTERFACES_FILE", opts->interfaces_file);
const struct lif_node *iter;
bool did_address = false, did_gateway = false;
/* Allocate a buffer for all possible addresses, if any */
char *addresses = calloc (BUFFER_LEN, 1);
size_t addresses_size = BUFFER_LEN;
char *addresses_end = addresses;
/* Allocate a buffer for all possible gateways, if any */
char *gateways = calloc (BUFFER_LEN, 1);
size_t gateways_size = BUFFER_LEN;
char *gateways_end = gateways;
LIF_DICT_FOREACH(iter, &iface->vars)
{
struct lif_dict_entry *entry = iter->data;
if (!strcmp(entry->key, "address"))
{
char addrbuf[4096];
if (!lif_address_format_cidr(iface, entry, addrbuf, sizeof(addrbuf)))
continue;
/* Append address to buffer */
append_to_buffer(&addresses, &addresses_size, &addresses_end, addrbuf);
/* Only print IF_ADDRESS once */
if (did_address)
continue;
struct lif_address *addr = entry->data;
char addrbuf[4096];
if (!lif_address_unparse(addr, addrbuf, sizeof addrbuf, true))
continue;
lif_environment_push(&envp, "IF_ADDRESS", addrbuf);
lif_environment_push(envp, "IF_ADDRESS", addrbuf);
did_address = true;
continue;
}
else if (!strcmp(entry->key, "gateway"))
{
/* Append address to buffer */
append_to_buffer(&gateways, &gateways_size, &gateways_end, entry->data);
if (did_gateway)
continue;
@ -218,10 +214,7 @@ lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interfac
else if (!strcmp(entry->key, "requires"))
{
if (iface->is_bridge)
lif_environment_push(&envp, "IF_BRIDGE_PORTS", (const char *) entry->data);
if (iface->is_bond)
lif_environment_push(&envp, "IF_BOND_SLAVES", (const char *) entry->data);
lif_environment_push(envp, "IF_BRIDGE_PORTS", (const char *) entry->data);
}
char envkey[4096] = "IF_";
@ -236,43 +229,132 @@ lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interfac
*ep = '_';
}
lif_environment_push(&envp, envkey, (const char *) entry->data);
lif_environment_push(envp, envkey, (const char *) entry->data);
}
if (!strcmp(phase, "pre-up"))
if (addresses != NULL)
lif_environment_push(envp, "IF_ADDRESSES", addresses);
if (gateways != NULL)
lif_environment_push(envp, "IF_GATEWAYS", gateways);
/* Clean up */
free (addresses);
free (gateways);
}
bool
lif_lifecycle_query_dependents(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
{
char deps[4096] = {};
char final_deps[4096] = {};
if (lifname == NULL)
lifname = iface->ifname;
char **envp = NULL;
build_environment(&envp, opts, iface, lifname, "depend", "depend");
struct lif_dict_entry *entry = lif_dict_find(&iface->vars, "requires");
if (entry != NULL)
strlcpy(deps, entry->data, sizeof deps);
if (!query_dependents_from_executors(opts, envp, iface, deps, sizeof deps, "depend"))
return false;
char *p = deps;
while (*p)
{
if (!handle_pre_up(opts, iface, lifname))
goto on_error;
}
else if (!strcmp(phase, "up"))
{
if (!handle_up(opts, iface, lifname))
goto on_error;
}
else if (!strcmp(phase, "down"))
{
if (!handle_down(opts, iface, lifname))
goto on_error;
}
else if (!strcmp(phase, "post-down"))
{
if (!handle_post_down(opts, iface, lifname))
goto on_error;
char *token = lif_next_token(&p);
if (strstr(final_deps, token) != NULL)
continue;
strlcat(final_deps, token, sizeof final_deps);
strlcat(final_deps, " ", sizeof final_deps);
}
handle_commands_for_phase(opts, envp, iface, lifname, phase);
if (entry != NULL)
{
free(entry->data);
entry->data = strdup(final_deps);
}
else if (*final_deps)
lif_dict_add(&iface->vars, "requires", strdup(final_deps));
lif_environment_free(&envp);
return true;
}
bool
lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up)
{
char **envp = NULL;
build_environment(&envp, opts, iface, lifname, phase, up ? "start" : "stop");
if (!handle_executors_for_phase(opts, envp, iface, up, phase))
goto handle_error;
if (!handle_commands_for_phase(opts, envp, iface, phase))
goto handle_error;
/* if we don't need to support /etc/if-X.d we're done here */
if (!lif_config.allow_addon_scripts)
goto out_free;
/* Check if scripts dir for this phase is present and bail out if it isn't */
struct stat dir_stat;
char dir_path[4096];
snprintf (dir_path, 4096, "/etc/network/if-%s.d", phase);
if (stat (dir_path, &dir_stat) != 0 || S_ISDIR (dir_stat.st_mode) == 0) {
goto out_free;
}
/* we should do error handling here, but ifupdown1 doesn't */
lif_execute_fmt(opts, envp, "/bin/run-parts /etc/network/if-%s.d", phase);
lif_execute_fmt(opts, envp, "/bin/run-parts %s", dir_path);
out_free:
lif_environment_free(&envp);
return true;
on_error:
handle_error:
lif_environment_free(&envp);
return false;
}
/* this function returns true if we can skip processing the interface for now,
* otherwise false.
*/
static bool
handle_refcounting(struct lif_dict *state, struct lif_interface *iface, bool up)
{
size_t orig_refcount = iface->refcount;
if (up)
lif_state_ref_if(state, iface->ifname, iface);
else
lif_state_unref_if(state, iface->ifname, iface);
#ifdef DEBUG_REFCOUNTING
fprintf(stderr, "handle_refcounting(): orig_refcount=%zu, refcount=%zu, direction=%s\n",
orig_refcount, iface->refcount, up ? "UP" : "DOWN");
#endif
/* if going up and orig_refcount > 0 -- we're already configured. */
if (up && orig_refcount > 0)
return true;
/* if going down and iface->refcount > 1 -- we still have other dependents. */
if (!up && iface->refcount > 1)
return true;
/* we can change this interface -- no blocking dependents. */
return false;
}
static bool
handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *parent, struct lif_dict *collection, struct lif_dict *state, bool up)
{
@ -282,6 +364,9 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par
if (requires == NULL)
return true;
/* set the parent's pending flag to break dependency cycles */
parent->is_pending = true;
char require_ifs[4096] = {};
strlcpy(require_ifs, requires->data, sizeof require_ifs);
char *bufp = require_ifs;
@ -290,24 +375,67 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par
{
struct lif_interface *iface = lif_interface_collection_find(collection, tokenp);
/* already up or down, skip */
if (up == iface->is_up)
if (iface->has_config_error)
{
if (opts->force)
fprintf (stderr, "ifupdown: (de)configuring dependent interface %s (of %s) despite config errors\n",
iface->ifname, parent->ifname);
else
{
fprintf (stderr, "ifupdown: skipping dependent interface %s (of %s) as it has config errors\n",
iface->ifname, parent->ifname);
continue;
}
}
/* if handle_refcounting returns true, it means we've already
* configured the interface, or it is too soon to deconfigure
* the interface.
*/
if (handle_refcounting(state, iface, up))
{
if (opts->verbose)
fprintf(stderr, "ifupdown: skipping dependent interface %s (of %s) -- %s\n",
iface->ifname, parent->ifname,
up ? "already configured" : "transient dependencies still exist");
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");
if (!lif_lifecycle_run(opts, iface, collection, state, iface->ifname, up))
{
parent->is_pending = false;
return false;
}
}
parent->is_pending = false;
return true;
}
bool
lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *lifname, bool up)
{
/* if we're already pending, exit */
if (iface->is_pending)
return true;
if (iface->is_template)
return false;
if (lifname == NULL)
lifname = iface->ifname;
@ -321,6 +449,9 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
* but, right now neither debian ifupdown or busybox ifupdown do any recovery,
* so we wont right now.
*/
if (!lif_lifecycle_run_phase(opts, iface, "create", lifname, up))
return false;
if (!lif_lifecycle_run_phase(opts, iface, "pre-up", lifname, up))
return false;
@ -330,9 +461,7 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
if (!lif_lifecycle_run_phase(opts, iface, "post-up", lifname, up))
return false;
lif_state_upsert(state, lifname, iface);
iface->is_up = true;
lif_state_ref_if(state, lifname, iface);
}
else
{
@ -345,14 +474,126 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
if (!lif_lifecycle_run_phase(opts, iface, "post-down", lifname, up))
return false;
if (!lif_lifecycle_run_phase(opts, iface, "destroy", lifname, up))
return false;
/* when going up, dependents go down last. */
if (!handle_dependents(opts, iface, collection, state, up))
return false;
lif_state_delete(state, lifname);
iface->is_up = false;
lif_state_unref_if(state, lifname, iface);
}
return true;
}
static bool
count_interface_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection, struct lif_interface *parent, size_t depth)
{
/* if we have looped, return true immediately to break the loop. */
if (parent->is_pending)
return true;
/* query our dependents if we don't have them already */
if (!lif_lifecycle_query_dependents(opts, parent, parent->ifname))
return false;
/* set rdepends_count to depth, dependents will be depth + 1 */
parent->is_pending = true;
parent->rdepends_count = depth;
struct lif_dict_entry *requires = lif_dict_find(&parent->vars, "requires");
/* no dependents, nothing to worry about */
if (requires == NULL)
{
parent->is_pending = false;
return true;
}
/* walk any dependents */
char require_ifs[4096] = {};
strlcpy(require_ifs, requires->data, sizeof require_ifs);
char *bufp = require_ifs;
for (char *tokenp = lif_next_token(&bufp); *tokenp; tokenp = lif_next_token(&bufp))
{
struct lif_interface *iface = lif_interface_collection_find(collection, tokenp);
if (!count_interface_rdepends(opts, collection, iface, depth + 1))
{
parent->is_pending = false;
return false;
}
}
parent->is_pending = false;
return true;
}
ssize_t
lif_lifecycle_count_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection)
{
struct lif_node *iter;
LIF_DICT_FOREACH(iter, collection)
{
struct lif_dict_entry *entry = iter->data;
struct lif_interface *iface = entry->data;
/* start depth at interface's rdepends_count, which will be 0 for the root,
* but will be more if additional rdepends are found...
*/
if (!count_interface_rdepends(opts, collection, iface, iface->rdepends_count))
{
fprintf(stderr, "ifupdown: dependency graph is broken for interface %s\n", iface->ifname);
return -1;
}
}
/* figure out the max depth */
size_t maxdepth = 0;
LIF_DICT_FOREACH(iter, collection)
{
struct lif_dict_entry *entry = iter->data;
struct lif_interface *iface = entry->data;
if (iface->rdepends_count > maxdepth)
maxdepth = iface->rdepends_count;
}
/* move the collection to a temporary list so we can reorder it */
struct lif_list temp_list = {};
struct lif_node *iter_next;
LIF_LIST_FOREACH_SAFE(iter, iter_next, collection->list.head)
{
void *data = iter->data;
lif_node_delete(iter, &collection->list);
memset(iter, 0, sizeof *iter);
lif_node_insert(iter, data, &temp_list);
}
/* walk backwards from maxdepth to 0, readding nodes */
for (ssize_t curdepth = maxdepth; curdepth > -1; curdepth--)
{
LIF_LIST_FOREACH_SAFE(iter, iter_next, temp_list.head)
{
struct lif_dict_entry *entry = iter->data;
struct lif_interface *iface = entry->data;
if ((ssize_t) iface->rdepends_count != curdepth)
continue;
lif_node_delete(iter, &temp_list);
memset(iter, 0, sizeof *iter);
lif_node_insert(iter, entry, &collection->list);
}
}
return maxdepth;
}

View file

@ -19,8 +19,10 @@
#include "libifupdown/interface.h"
#include "libifupdown/execute.h"
extern bool lif_lifecycle_query_dependents(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname);
extern bool lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up);
extern bool lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *lifname, bool up);
extern ssize_t lif_lifecycle_count_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection);
#endif

View file

@ -3,6 +3,7 @@
* Purpose: linked lists
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -14,8 +15,25 @@
*/
#include <stdint.h>
#include <stdlib.h>
#include "libifupdown/list.h"
void
lif_list_free_nodes(struct lif_list *list)
{
if (list == NULL)
return;
struct lif_node *iter, *iter_next;
LIF_LIST_FOREACH_SAFE(iter, iter_next, list->head)
{
free (iter);
}
free (list);
}
void
lif_node_insert(struct lif_node *node, void *data, struct lif_list *list)
{

View file

@ -3,6 +3,7 @@
* Purpose: linked lists
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
@ -29,6 +30,8 @@ struct lif_list {
size_t length;
};
extern void lif_list_free_nodes(struct lif_list *list);
extern void lif_node_insert(struct lif_node *node, void *data, struct lif_list *list);
extern void lif_node_insert_tail(struct lif_node *node, void *data, struct lif_list *list);
extern void lif_node_delete(struct lif_node *node, struct lif_list *list);
@ -37,6 +40,9 @@ extern void lif_node_delete(struct lif_node *node, struct lif_list *list);
for ((iter) = (head); (iter) != NULL; (iter) = (iter)->next)
#define LIF_LIST_FOREACH_SAFE(iter, iter_next, head) \
for ((iter) = (head), (iter_next) = (iter)->next; (iter) != NULL; (iter) = (iter_next), (iter_next) = (iter) != NULL ? (iter)->next : NULL)
for ((iter) = (head), (iter_next) = (iter) != NULL ? (iter)->next : NULL; (iter) != NULL; (iter) = (iter_next), (iter_next) = (iter) != NULL ? (iter)->next : NULL)
#define LIF_LIST_FOREACH_REVERSE(iter, tail) \
for ((iter) = (tail); (iter) != NULL; (iter) = (iter)->prev)
#endif

View file

@ -13,27 +13,46 @@
* from the use of this software.
*/
#include <limits.h>
#include <string.h>
#include "libifupdown/state.h"
#include "libifupdown/fgetline.h"
#include "libifupdown/tokenize.h"
bool
lif_state_read(struct lif_dict *state, FILE *fd)
{
char linebuf[4096];
while (lif_fgetline(linebuf, sizeof linebuf, fd))
{
char *ifname = linebuf;
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)
{
rc = strtoul(refcount, NULL, 10);
if (rc == 0 || rc == ULONG_MAX)
rc = 1;
}
if (equals_p == NULL)
{
lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = ifname });
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 });
lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = equals_p, .refcount = rc, .is_explicit = is_explicit });
}
return true;
@ -45,8 +64,9 @@ lif_state_read_path(struct lif_dict *state, const char *path)
FILE *fd = fopen(path, "r");
bool ret;
/* if file cannot be opened, assume an empty state */
if (fd == NULL)
return false;
return true;
ret = lif_state_read(state, fd);
fclose(fd);
@ -54,10 +74,39 @@ lif_state_read_path(struct lif_dict *state, const char *path)
return ret;
}
void
lif_state_ref_if(struct lif_dict *state, const char *ifname, struct lif_interface *iface)
{
iface->refcount++;
lif_state_upsert(state, ifname, iface);
}
void
lif_state_unref_if(struct lif_dict *state, const char *ifname, struct lif_interface *iface)
{
if (iface->refcount == 0)
return;
iface->refcount--;
if (iface->refcount)
lif_state_upsert(state, ifname, iface);
else
lif_state_delete(state, ifname);
}
void
lif_state_upsert(struct lif_dict *state, const char *ifname, struct lif_interface *iface)
{
lif_dict_add(state, ifname, strdup(iface->ifname));
lif_state_delete(state, ifname);
struct lif_state_record *rec = calloc(1, sizeof(*rec));
rec->mapped_if = strdup(iface->ifname);
rec->refcount = iface->refcount;
rec->is_explicit = iface->is_explicit;
lif_dict_add(state, ifname, rec);
}
void
@ -68,7 +117,10 @@ lif_state_delete(struct lif_dict *state, const char *ifname)
if (entry == NULL)
return;
free(entry->data);
struct lif_state_record *rec = entry->data;
free(rec->mapped_if);
free(rec);
lif_dict_delete_entry(state, entry);
}
@ -80,8 +132,10 @@ lif_state_write(const struct lif_dict *state, FILE *f)
LIF_DICT_FOREACH(iter, state)
{
struct lif_dict_entry *entry = iter->data;
struct lif_state_record *rec = entry->data;
fprintf(f, "%s=%s\n", entry->key, (const char *) entry->data);
fprintf(f, "%s=%s %zu%s\n", entry->key, rec->mapped_if, rec->refcount,
rec->is_explicit ? " explicit" : "");
}
}
@ -107,7 +161,8 @@ lif_state_lookup(struct lif_dict *state, struct lif_dict *if_collection, const c
if (entry == NULL)
return NULL;
struct lif_dict_entry *if_entry = lif_dict_find(if_collection, (const char *) entry->data);
struct lif_state_record *rec = entry->data;
struct lif_dict_entry *if_entry = lif_dict_find(if_collection, rec->mapped_if);
if (if_entry == NULL)
return NULL;
@ -123,9 +178,11 @@ lif_state_sync(struct lif_dict *state, struct lif_dict *if_collection)
LIF_DICT_FOREACH(iter, state)
{
struct lif_dict_entry *entry = iter->data;
struct lif_interface *iface = lif_interface_collection_find(if_collection, entry->key);
struct lif_state_record *rec = entry->data;
struct lif_interface *iface = lif_interface_collection_find(if_collection, rec->mapped_if);
iface->is_up = true;
iface->refcount = rec->refcount;
iface->is_explicit = rec->is_explicit;
}
return true;

View file

@ -17,11 +17,21 @@
#define LIBIFUPDOWN_STATE_H__GUARD
#include <stdio.h>
#include <stdbool.h>
#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);
extern bool lif_state_read_path(struct lif_dict *state, const char *path);
extern void lif_state_upsert(struct lif_dict *state, const char *ifname, struct lif_interface *iface);
extern void lif_state_ref_if(struct lif_dict *state, const char *ifname, struct lif_interface *iface);
extern void lif_state_unref_if(struct lif_dict *state, const char *ifname, struct lif_interface *iface);
extern void lif_state_delete(struct lif_dict *state, const char *ifname);
extern void lif_state_write(const struct lif_dict *state, FILE *f);
extern bool lif_state_write_path(const struct lif_dict *state, const char *path);

View file

@ -18,6 +18,24 @@
#include <ctype.h>
static inline char *
lif_next_token_eq(char **buf)
{
char *out = *buf;
while (*out && (isspace(*out) || *out == '='))
out++;
char *end = out;
while (*end && !isspace(*end) && *end != '=')
end++;
*end++ = '\0';
*buf = end;
return out;
}
static inline char *
lif_next_token(char **buf)
{

View file

@ -21,19 +21,20 @@
void
lif_common_version(void)
{
printf("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
printf("\nCopyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>\n\n");
printf("Permission to use, copy, modify, and/or distribute this software for any\n");
printf("purpose with or without fee is hereby granted, provided that the above\n");
printf("copyright notice and this permission notice appear in all copies.\n\n");
printf("This software is provided 'as is' and without any warranty, express or\n");
printf("implied. In no event shall the authors be liable for any damages arising\n");
printf("from the use of this software.\n\n");
printf("Report bugs at <%s>.\n", PACKAGE_BUGREPORT);
printf(PACKAGE_NAME " " PACKAGE_VERSION "\n"
"\n"
"Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>\n"
"Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>\n"
"\n"
"Permission to use, copy, modify, and/or distribute this software for any\n"
"purpose with or without fee is hereby granted, provided that the above\n"
"copyright notice and this permission notice appear in all copies.\n"
"\n"
"This software is provided 'as is' and without any warranty, express or\n"
"implied. In no event shall the authors be liable for any damages arising\n"
"from the use of this software.\n"
"\n"
"Report bugs at <" PACKAGE_BUGREPORT ">.\n");
exit(EXIT_SUCCESS);
}

127
libifupdown/yaml-base.c Normal file
View file

@ -0,0 +1,127 @@
/*
* libifupdown/yaml-base.c
* Purpose: YAML implementation -- base
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <string.h>
#include "libifupdown/libifupdown.h"
#include "libifupdown/yaml-base.h"
void
lif_yaml_document_init(struct lif_yaml_node *doc, const char *name)
{
memset(doc, '\0', sizeof *doc);
doc->value_type = LIF_YAML_OBJECT;
if (name != NULL)
doc->name = strdup(name);
}
struct lif_yaml_node *
lif_yaml_document_new(const char *name)
{
struct lif_yaml_node *doc = calloc(1, sizeof *doc);
lif_yaml_document_init(doc, name);
doc->malloced = true;
return doc;
}
struct lif_yaml_node *
lif_yaml_node_new_boolean(const char *name, bool value)
{
struct lif_yaml_node *node = calloc(1, sizeof *node);
node->malloced = true;
node->value_type = LIF_YAML_BOOLEAN;
if (name != NULL)
node->name = strdup(name);
node->value.bool_value = value;
return node;
}
struct lif_yaml_node *
lif_yaml_node_new_string(const char *name, const char *value)
{
struct lif_yaml_node *node = calloc(1, sizeof *node);
node->malloced = true;
node->value_type = LIF_YAML_STRING;
if (name != NULL)
node->name = strdup(name);
if (value != NULL)
node->value.str_value = strdup(value);
return node;
}
struct lif_yaml_node *
lif_yaml_node_new_object(const char *name)
{
struct lif_yaml_node *node = calloc(1, sizeof *node);
node->malloced = true;
node->value_type = LIF_YAML_OBJECT;
if (name != NULL)
node->name = strdup(name);
return node;
}
struct lif_yaml_node *
lif_yaml_node_new_list(const char *name)
{
struct lif_yaml_node *node = calloc(1, sizeof *node);
node->malloced = true;
node->value_type = LIF_YAML_LIST;
if (name != NULL)
node->name = strdup(name);
return node;
}
void
lif_yaml_node_free(struct lif_yaml_node *node)
{
struct lif_node *iter, *next;
LIF_LIST_FOREACH_SAFE(iter, next, node->children.head)
{
struct lif_yaml_node *iter_node = iter->data;
lif_yaml_node_free(iter_node);
}
free(node->name);
if (node->value_type == LIF_YAML_STRING)
free(node->value.str_value);
if (node->malloced)
free(node);
}
void
lif_yaml_node_append_child(struct lif_yaml_node *parent, struct lif_yaml_node *child)
{
lif_node_insert_tail(&child->node, child, &parent->children);
}

52
libifupdown/yaml-base.h Normal file
View file

@ -0,0 +1,52 @@
/*
* libifupdown/yaml-base.h
* Purpose: YAML implementation -- base
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#ifndef LIBIFUPDOWN_YAML_BASE_H__GUARD
#define LIBIFUPDOWN_YAML_BASE_H__GUARD
#include "libifupdown/libifupdown.h"
/* this is a subset of types supported by our implementation */
enum lif_yaml_value {
LIF_YAML_STRING,
LIF_YAML_LIST,
LIF_YAML_OBJECT,
LIF_YAML_BOOLEAN
};
struct lif_yaml_node {
struct lif_node node;
bool malloced;
char *name;
enum lif_yaml_value value_type;
union {
char *str_value; /* for string nodes */
bool bool_value; /* for boolean nodes */
} value;
struct lif_list children; /* for list and object nodes */
};
extern void lif_yaml_document_init(struct lif_yaml_node *doc, const char *name);
extern struct lif_yaml_node *lif_yaml_document_new(const char *name);
extern struct lif_yaml_node *lif_yaml_node_new_boolean(const char *name, bool value);
extern struct lif_yaml_node *lif_yaml_node_new_string(const char *name, const char *value);
extern struct lif_yaml_node *lif_yaml_node_new_object(const char *name);
extern struct lif_yaml_node *lif_yaml_node_new_list(const char *name);
extern void lif_yaml_node_free(struct lif_yaml_node *node);
extern void lif_yaml_node_append_child(struct lif_yaml_node *parent, struct lif_yaml_node *child);
#endif

66
libifupdown/yaml-writer.c Normal file
View file

@ -0,0 +1,66 @@
/*
* libifupdown/yaml-writer.c
* Purpose: YAML implementation -- writer
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#include <stdio.h>
#include <string.h>
#include "libifupdown/libifupdown.h"
#include "libifupdown/yaml-base.h"
#include "libifupdown/yaml-writer.h"
static const size_t INDENT_WIDTH = 2;
static void
lif_yaml_write_node(const struct lif_yaml_node *node, FILE *f, size_t indent, bool type_annotations)
{
const struct lif_node *iter;
if (node->name != NULL)
fprintf(f, "%*s%s: ", (int) indent, "", node->name);
size_t child_indent = indent + INDENT_WIDTH;
switch (node->value_type)
{
case LIF_YAML_BOOLEAN:
fprintf(f, "%s%s\n", type_annotations ? "!!bool " : "", node->value.bool_value ? "true" : "false");
break;
case LIF_YAML_STRING:
fprintf(f, "%s%s\n", type_annotations ? "!!str " : "", node->value.str_value);
break;
case LIF_YAML_OBJECT:
fprintf(f, "\n");
break;
case LIF_YAML_LIST:
fprintf(f, "\n");
child_indent += INDENT_WIDTH;
break;
}
LIF_LIST_FOREACH(iter, node->children.head)
{
const struct lif_yaml_node *iter_node = iter->data;
if (node->value_type == LIF_YAML_LIST)
fprintf(f, "%*s-\n", (int) (child_indent - INDENT_WIDTH), "");
lif_yaml_write_node(iter_node, f, child_indent, type_annotations);
}
}
void
lif_yaml_write(const struct lif_yaml_node *doc, FILE *f, bool type_annotations)
{
lif_yaml_write_node(doc, f, 0, type_annotations);
}

24
libifupdown/yaml-writer.h Normal file
View file

@ -0,0 +1,24 @@
/*
* libifupdown/yaml-writer.h
* Purpose: YAML implementation -- writer
*
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* This software is provided 'as is' and without any warranty, express or
* implied. In no event shall the authors be liable for any damages arising
* from the use of this software.
*/
#ifndef LIBIFUPDOWN_YAML_WRITER_H__GUARD
#define LIBIFUPDOWN_YAML_WRITER_H__GUARD
#include "libifupdown/libifupdown.h"
#include "libifupdown/yaml-base.h"
extern void lif_yaml_write(const struct lif_yaml_node *doc, FILE *f, bool type_annotations);
#endif

View file

@ -2,6 +2,9 @@ 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'}
include('linux/Kyuafile')

1
tests/executors/bond Symbolic link
View file

@ -0,0 +1 @@
mock-executor

1
tests/executors/bridge Symbolic link
View file

@ -0,0 +1 @@
mock-executor

1
tests/executors/dhcp Symbolic link
View file

@ -0,0 +1 @@
mock-executor

1
tests/executors/link Symbolic link
View file

@ -0,0 +1 @@
mock-executor

View file

@ -0,0 +1,7 @@
#!/bin/sh
[ -z "$IF_MOCK_DEPENDS" ] && IF_MOCK_DEPENDS="eth0 eth1 eth2 eth3 eth4"
case "$PHASE" in
depend) echo "$IF_MOCK_DEPENDS" ;;
esac

2
tests/executors/mock-executor Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
exit 0

1
tests/executors/static Symbolic link
View file

@ -0,0 +1 @@
mock-executor

Some files were not shown because too many files have changed in this diff Show more