Compare commits

..

403 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
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
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
119 changed files with 6099 additions and 630 deletions

View file

@ -10,7 +10,7 @@ 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
@ -18,5 +18,8 @@ jobs:
- name: Build
run: make
- name: Build documentation
run: make docs
- name: Run tests
run: make check

1
.gitignore vendored
View file

@ -13,4 +13,5 @@ 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

View file

@ -4,20 +4,23 @@ LIBBSD_CFLAGS =
LIBBSD_LIBS =
PACKAGE_NAME := ifupdown-ng
PACKAGE_VERSION := 0.8.5
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
CONFIG_FILE := /etc/network/ifupdown-ng.conf
EXECUTOR_PATH := /usr/libexec/ifupdown-ng
CFLAGS ?= -ggdb3 -Os
CFLAGS += -Wall -Wextra
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}\"
@ -34,8 +37,10 @@ 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
@ -43,7 +48,8 @@ MULTICALL_SRC = \
cmd/multicall.c \
cmd/multicall-options.c \
cmd/multicall-exec-options.c \
cmd/multicall-match-options.c
cmd/multicall-match-options.c \
cmd/pretty-print-iface.c
MULTICALL_OBJ = ${MULTICALL_SRC:.c=.o}
MULTICALL = ifupdown
@ -69,6 +75,22 @@ MULTICALL_${CONFIG_IFCTRSTAT}_OBJ += ${IFCTRSTAT_SRC:.c=.o}
CMDS_${CONFIG_IFCTRSTAT} += ifctrstat
CPPFLAGS_${CONFIG_IFCTRSTAT} += -DCONFIG_IFCTRSTAT
# 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
# 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}
@ -78,18 +100,28 @@ EXECUTOR_SCRIPTS_CORE ?= \
ipv6-ra \
static \
link \
ppp
ppp \
forward
EXECUTOR_SCRIPTS_OPT ?= \
batman \
bond \
bridge \
vrf \
ethtool \
gre \
mpls \
tunnel \
gre
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}
@ -124,21 +156,38 @@ install: all
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
MANPAGES_5 = \
doc/interfaces.5
MANPAGES_7 = \
doc/ifupdown-executor.7
doc/ifctrstat.8 \
doc/ifparse.8
MANPAGES = ${MANPAGES_5} ${MANPAGES_7} ${MANPAGES_8}

View file

@ -30,18 +30,20 @@ For compatibility with some legacy ifupdown executors, we also provide the
On musl systems, simply do `make` and `make install` to build and install.
On glibc systems, you must additionally define LIBBSD_CFLAGS and LIBBSD_LIBS:
On glibc systems, you must install `libbsd-dev` or equivalent and additionally define `LIBBSD_CFLAGS` and `LIBBSD_LIBS`:
export LIBBSD_CFLAGS=$(pkg-config --cflags libbsd-overlay)
export LIBBSD_LIBS=$(pkg-config --libs libbsd-overlay)
make
# 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`.
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`).
the documentation requires scdoc (`apk add scdoc` / `apt install scdoc`).
## Discussion
Discuss ifupdown-ng on IRC: irc.as7007.net #ifupdown-ng
Discuss ifupdown-ng on IRC: irc.oftc.net #ifupdown-ng

View file

@ -17,7 +17,8 @@
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include "multicall.h"
#include "cmd/multicall.h"
#include "cmd/ifctrstat-linux.h"
struct counter_desc {
const char *name;
@ -41,7 +42,7 @@ counter_compare(const void *key, const void *candidate)
return strcasecmp((const char *)key, ((struct counter_desc *)candidate)->name);
}
char *
const char *
read_counter(const char *interface, const char *counter)
{
FILE *fp;

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

View file

@ -20,12 +20,11 @@
#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;
extern const char *read_counter(const char *interface, const char *counter);
static bool show_label = true;
static bool
@ -96,7 +95,7 @@ ifctrstat_set_nolabel(const char *opt_arg)
show_label = false;
}
int
static int
ifctrstat_main(int argc, char *argv[])
{
if (optind >= argc)
@ -152,7 +151,7 @@ static struct if_option_group local_option_group = {
struct if_applet ifctrstat_applet = {
.name = "ifctrstat",
.desc = "Display statistics about an interface",
.desc = "display statistics about an interface",
.main = ifctrstat_main,
.usage = "ifctrstat [options] <interface> <counter>\n ifctrstat [options] --list",
.manpage = "8 ifctrstat",

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,56 +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 (!lif_lifecycle_query_dependents(&exec_opts, iface, iface->ifname))
return;
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 (!lif_lifecycle_query_dependents(&exec_opts, iface, iface->ifname))
return;
if (iface->is_up)
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");
@ -86,37 +48,16 @@ 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;
}
}
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;
}
void
static void
print_interface_property(struct lif_interface *iface, const char *property)
{
struct lif_node *iter;
@ -131,26 +72,10 @@ print_interface_property(struct lif_interface *iface, const char *property)
if (printing_address)
{
struct lif_address *addr = entry->data;
size_t orig_netmask = addr->netmask;
if (!addr->netmask)
{
/* if fallback netmask is not set, default to 255.255.255.0 */
addr->netmask = 24;
struct lif_dict_entry *entry = lif_dict_find(&iface->vars, "netmask");
if (entry != NULL)
addr->netmask = count_set_bits(entry->data);
}
char addr_buf[512];
if (!lif_address_unparse(addr, addr_buf, sizeof addr_buf, true))
if (!lif_address_format_cidr(iface, entry, addr_buf, sizeof(addr_buf)))
continue;
addr->netmask = orig_netmask;
printf("%s\n", addr_buf);
}
else
@ -158,16 +83,16 @@ print_interface_property(struct lif_interface *iface, const char *property)
}
}
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)
@ -187,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
@ -198,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;
@ -215,12 +143,16 @@ 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" : "");
}
}
static bool listing = false, listing_stat = false;
static void
set_listing(const char *opt_arg)
{
@ -235,6 +167,13 @@ set_show_state(const char *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)
{
@ -255,12 +194,21 @@ 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 = {
@ -269,11 +217,16 @@ static struct if_option_group local_option_group = {
.group = local_options
};
int
static int
ifquery_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))
{
@ -281,14 +234,26 @@ ifquery_main(int argc, char *argv[])
return EXIT_FAILURE;
}
if (!lif_interface_file_parse(&collection, exec_opts.interfaces_file))
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;
}
/* --list --state is not allowed */
if (listing && listing_stat)
if (listing && (listing_stat || listing_running))
generic_usage(self_applet, EXIT_FAILURE);
if (listing)
@ -296,7 +261,7 @@ ifquery_main(int argc, char *argv[])
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;
@ -316,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)
@ -327,7 +295,7 @@ ifquery_main(int argc, char *argv[])
if (match_opts.property != NULL)
print_interface_property(iface, match_opts.property);
else
print_interface(iface);
prettyprint_interface_eni(iface);
}
return EXIT_SUCCESS;

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
@ -26,7 +27,7 @@
static bool up;
bool
static bool
is_ifdown()
{
if (strstr(argv0, "ifdown") != NULL)
@ -35,7 +36,7 @@ is_ifdown()
return false;
}
int
static int
acquire_state_lock(const char *state_path, const char *lifname)
{
if (exec_opts.mock || exec_opts.no_lock)
@ -45,7 +46,7 @@ acquire_state_lock(const char *state_path, const char *lifname)
snprintf(lockpath, sizeof lockpath, "%s.%s.lock", state_path, lifname);
int fd = open(lockpath, O_CREAT | O_WRONLY | O_TRUNC);
int fd = open(lockpath, O_CREAT | O_WRONLY | O_TRUNC, 0600);
if (fd < 0)
{
if (exec_opts.verbose)
@ -93,8 +94,60 @@ acquire_state_lock(const char *state_path, const char *lifname)
return fd;
}
bool
change_interface(struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *ifname)
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);
@ -104,6 +157,14 @@ change_interface(struct lif_interface *iface, struct lif_dict *collection, struc
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",
@ -124,10 +185,16 @@ change_interface(struct lif_interface *iface, struct lif_dict *collection, struc
if (lockfd != -1)
close(lockfd);
if (up && update_state)
{
iface->is_explicit = true;
lif_state_upsert(state, ifname, iface);
}
return true;
}
bool
static bool
change_auto_interfaces(struct lif_dict *collection, struct lif_dict *state, struct match_options *opts)
{
struct lif_node *iter;
@ -148,20 +215,46 @@ 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 lif_interface_file_parse_state parse_state = {
.collection = &collection,
};
lif_interface_collection_init(&collection);
if (!lif_state_read_path(&state, exec_opts.state_file))
{
@ -169,12 +262,24 @@ ifupdown_main(int argc, char *argv[])
return EXIT_FAILURE;
}
if (!lif_interface_file_parse(&collection, exec_opts.interfaces_file))
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 (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;
}
if (!lif_state_sync(&state, &collection))
{
fprintf(stderr, "%s: could not sync state\n", argv0);
@ -184,9 +289,9 @@ 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)
generic_usage(self_applet, EXIT_FAILURE);
@ -215,23 +320,17 @@ 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, exec_opts.state_file))
{
fprintf(stderr, "%s: could not update %s\n", argv0, exec_opts.state_file);
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
return update_state_file_and_exit(EXIT_SUCCESS, &state);
}
struct if_applet ifup_applet = {

View file

@ -21,10 +21,13 @@
#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
.state_file = STATE_FILE,
.timeout = DEFAULT_TIMEOUT,
};
static void
@ -68,19 +71,29 @@ set_no_lock(const char *opt_arg)
}
static void
no_op(const char *opt_arg)
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, no_op},
{'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 = {

View file

@ -39,7 +39,7 @@ set_include_pattern(const char *opt_arg)
static void
set_exclude_pattern(const char *opt_arg)
{
match_opts.include_pattern = opt_arg;
match_opts.exclude_pattern = opt_arg;
}
static struct if_option match_options[] = {

View file

@ -36,6 +36,10 @@ extern struct if_applet ifdown_applet;
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;
@ -46,6 +50,9 @@ struct if_applet *applet_table[] = {
#ifdef CONFIG_IFUPDOWN
&ifdown_applet,
#endif
#ifdef CONFIG_IFPARSE
&ifparse_applet,
#endif
#ifdef CONFIG_IFQUERY
&ifquery_applet,
#endif
@ -55,7 +62,7 @@ struct if_applet *applet_table[] = {
&ifupdown_applet,
};
int
static int
applet_cmp(const void *a, const void *b)
{
const char *key = a;
@ -66,12 +73,16 @@ applet_cmp(const void *a, const void *b)
void multicall_usage(int status) __attribute__((noreturn));
struct if_applet ifupdown_applet;
int
main(int argc, char *argv[])
{
argv0 = basename(argv[0]);
const struct if_applet **app;
lif_config_load(CONFIG_FILE);
app = bsearch(argv0, applet_table,
ARRAY_SIZE(applet_table), sizeof(*applet_table),
applet_cmp);
@ -83,12 +94,14 @@ main(int argc, char *argv[])
}
self_applet = *app;
process_options(*app, argc, argv);
return (*app)->main(argc, argv);
if (self_applet != &ifupdown_applet)
process_options(*app, argc, argv);
return self_applet->main(argc, argv);
}
int
static int
multicall_main(int argc, char *argv[])
{
if (argc < 2)
@ -97,24 +110,23 @@ multicall_main(int argc, char *argv[])
return main(argc - 1, argv + 1);
}
struct if_applet ifupdown_applet;
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(status);
}

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
}

View file

@ -20,6 +20,10 @@ in order of importance:
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.
@ -38,7 +42,59 @@ in order of importance:
All configuration examples in this guide concern the
`/etc/network/interfaces` file.
## Basic Configuration
## 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
@ -71,7 +127,7 @@ iface eth0
use dhcp
```
## IPv6 RA Configuration
### IPv6 RA Configuration
With IPv6, stateless auto-configuration is typically used to
configure network interfaces. If you are not interested in
@ -85,7 +141,7 @@ iface eth0
use ipv6-ra
```
## Static Configuration
### Static Configuration
We can use the `static` executor to configure static IPv4 and
IPv6 addresses. If you use the `address` keyword, the `static`
@ -98,11 +154,11 @@ iface eth0
gateway 203.0.113.1
```
### Multi-homing
#### Multiple Addresses
A typical scenario on servers is multi-homing, where a server
has multiple IP addresses on a single interface. In this case
you simply add additional `address` lines like this:
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
@ -113,7 +169,7 @@ iface eth0
gateway 203.0.113.1
```
### Dual-stack configurations
#### Dual-stack configurations
Another typical scenario for servers is to run a dual-stack
configuration, where interfaces have both an IPv4 and an IPv6
@ -184,7 +240,7 @@ iface bond0
[...]
```
Is equivalent to:
Is with respect to dependency equivalent to:
```
auto bond0
@ -215,6 +271,5 @@ 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]: irc://irc.as7007.net/#ifupdown-ng
configure a specific scenario, drop by the
[ifupdown-ng IRC channel](irc://irc.oftc.net/#ifupdown-ng).

View file

@ -45,6 +45,10 @@ configured in the configuration 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.
@ -54,8 +58,9 @@ configured in the configuration database.
# SEE ALSO
*ifup*(8)++
*ifquery*(8)++
*ifupdown-ng.conf*(5)
*ifup*(8)
*ifquery*(8)
*interfaces*(5)
# AUTHORS

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>

View file

@ -32,6 +32,10 @@ configuration file to the current format.
*-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.
@ -50,9 +54,18 @@ configuration file to the current format.
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.

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>

View file

@ -48,6 +48,10 @@ configured in the configuration 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.
@ -57,8 +61,9 @@ configured in the configuration database.
# SEE ALSO
*ifdown*(8)++
*ifquery*(8)++
*ifupdown-ng.conf*(5)
*ifdown*(8)
*ifquery*(8)
*interfaces*(5)
# AUTHORS

View file

@ -12,7 +12,7 @@ protocol documented in this man page.
# PHASES
Executors are run to react to seven different phases and are not
Executors are run to react to nine different phases and are not
required to take any specific action. These phases are:
*depend*
@ -24,6 +24,10 @@ required to take any specific action. These phases are:
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.
@ -42,6 +46,10 @@ required to take any specific action. These phases are:
*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

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>

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>

View file

@ -11,7 +11,7 @@ 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 here.
well, but that syntax is not discussed in detail here.
# FILE SYNTAX
@ -24,6 +24,8 @@ 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:
```
@ -47,6 +49,16 @@ with an address of *203.0.113.2* and gateway of *203.0.113.1*.
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
@ -54,11 +66,11 @@ 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.
*gateway* _address_
Associates an IPv4 or IPv6 address with the parent interface
for use as a default route (gateway).
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
@ -66,19 +78,46 @@ the system will only respond to certain keywords by default:
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* _interface_
Designates that the parent interface should inherit
configuration data from _interface_.
*inherit* _object_
Designates that the configured interface should inherit
configuration data from _object_. Normally _object_
must be a *template*.
*use* _option_
Designates that an option should be used. See _OPTIONS_
section for more information on options.
*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.
@ -98,14 +137,19 @@ the system will only respond to certain keywords by default:
*post-up* _command_
Runs _command_ after bringing the interface up.
Additional packages such as *bonding*, *bridge*, *tunnel* and
*vrf* add additional keywords to this vocabulary.
Additional packages such as *bonding*, *bridge*, *tunnel*, *vrf* and
*vxlan* add additional keywords to this vocabulary.
# OPTIONS
# EXECUTORS
The *use* keyword designates that an _option_ should be used.
The *use* keyword designates that an _executor_ should be used.
This system is extendable by additional packages, but the
most common options are:
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
@ -121,17 +165,47 @@ most common options are:
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.
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,
@ -162,6 +236,27 @@ 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>
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

View file

@ -1,9 +1,9 @@
#!/bin/sh
set -e
[ -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
@ -13,6 +13,10 @@ set -e
# 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
@ -51,13 +55,10 @@ all_ports() {
add_ports() {
local port=
for port in $PORTS; do
if [ -x /etc/network/if-pre-up.d/vlan ]; then
env IFACE=$port /etc/network/if-pre-up.d/vlan
fi
if [ -n "$IF_BRIDGE_HW" ]; then
ip link set dev $port addr $IF_BRIDGE_HW
fi
brctl addif $IFACE $port && ip link set dev $port up
ip link set dev $port master $IFACE && ip link set dev $port up
done
}
@ -65,22 +66,17 @@ del_ports() {
local port=
for port in $PORTS; do
ip link set dev $port down
brctl delif $IFACE $port
if [ -x /etc/network/ip-post-down.d/vlan ]; then
env IFACE=$port /etc/network/if-post-down.d/vlan
fi
ip link set dev $port nomaster
done
}
set_bridge_opts() {
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_GCINT" ] \
&& brctl setgcint $IFACE $IF_BRIDGE_GCINT
[ -n "$IF_BRIDGE_HELLO" ] \
&& brctl sethello $IFACE $IF_BRIDGE_HELLO
[ -n "$IF_BRIDGE_MAXAGE" ] \
@ -93,6 +89,44 @@ set_bridge_opts() {
&& 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
@ -122,7 +156,92 @@ wait_bridge() {
done
}
[ -z "$IF_BRIDGE_PORTS" ] && IF_BRIDGE_PORTS="$IF_REQUIRES"
################################################################################
# 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
"") ;;
@ -131,19 +250,53 @@ all) PORTS=$(all_ports);;
*) PORTS="$IF_BRIDGE_PORTS";;
esac
[ -z "$PORTS" ] && ! env | grep -q "^IF_BRIDGE" && exit
case "$PHASE" in
pre-up)
brctl addbr $IFACE || exit 1
wait_ports
set_bridge_opts
add_ports
wait_bridge
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)
del_ports
ip link set dev $IFACE down
brctl delbr $IFACE || exit 1
# 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

View file

@ -1,6 +1,6 @@
#!/bin/sh
set -e
# 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
@ -14,18 +14,26 @@ determine_implementation() {
start() {
case "$1" in
dhcpcd)
[ -n "$IF_HOSTNAME" ] && optargs="$optargs -h $IF_HOSTNAME"
[ -n "$IF_VENDOR" ] && optargs="$optargs -i $IF_VENDOR"
[ -n "$IF_CLIENT" ] && optargs="$optargs -i $IF_CLIENT"
[ -n "$IF_LEASETIME" ] && optargs="$optargs -l $IF_LEASETIME"
[ -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)
${MOCK} /usr/sbin/dhclient -pf /var/run/dhclient.$IFACE.pid $IFACE
# 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)
UDHCPC_OPTS=$(eval echo $IF_UDHCPC_OPTS)
${MOCK} /sbin/udhcpc -b -R -p /var/run/udhcpc.$IFACE.pid -i $IFACE $UDHCPC_OPTS
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
;;
*)
;;

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

View file

@ -1,7 +1,4 @@
#!/bin/sh
set -e
# Executor for advanced GRE tunnel management.
[ -z "$IF_GRE_LOCAL" ] && exit 1
@ -19,10 +16,10 @@ PARAMS="mode $IF_GRE_MODE local '$IF_GRE_LOCAL' remote '$IF_GRE_REMOTE'"
[ -n "$PARAMS" ] || exit 0
case "$PHASE" in
pre-up)
create)
${MOCK} eval ip $FAMILY $COMMAND add $IFACE $PARAMS
;;
post-down)
destroy)
${MOCK} ip $FAMILY $COMMAND del $IFACE
;;
depend)

View file

@ -1,7 +1,4 @@
#!/bin/sh
set -e
start() {
${MOCK} /bin/sh -c "echo 1 > /proc/sys/net/ipv6/conf/$IFACE/accept_ra"
}

View file

@ -1,13 +1,6 @@
#!/bin/sh
set -e
[ -n "$VERBOSE" ] && set -x
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"
is_vlan() {
case "$IFACE" in
*#*) return 1 ;;
@ -32,18 +25,49 @@ is_vlan() {
}
case "$PHASE" in
up|down)
depend)
# vlan-raw-device
if is_vlan; then
ADD_DEL="add"
[ "$PHASE" = "down" ] && ADD_DEL="delete"
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
if [ "$PHASE" = "up" -a -e /sys/class/net/$IFACE ]; then
exit 0
fi
if [ -z "$MOCK" ]; then
if ! ip link show "$IF_VLAN_RAW_DEVICE" >/dev/null; then
echo "Device $IF_VLAN_RAW_DEVICE for $IFACE does not exist"
${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
@ -53,17 +77,36 @@ up|down)
fi
fi
${MOCK} ip link $ADD_DEL link "$IF_VLAN_RAW_DEVICE" name "$IFACE" type vlan id "$IF_VLAN_ID"
[ "$PHASE" = "down" ] && exit 0
${MOCK} ip link set $PHASE dev $IFACE $IF_LINK_OPTIONS
else
${MOCK} ip link set $PHASE dev $IFACE $IF_LINK_OPTIONS
${MOCK} ip link add link "${IF_VLAN_RAW_DEVICE}" name "${IFACE}" type vlan id "${IF_VLAN_ID}"
fi
;;
depend)
if is_vlan; then
echo "$IF_VLAN_RAW_DEVICE"
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

View file

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

View file

@ -1,10 +1,11 @@
#!/bin/sh
[ -z "${VERBOSE}" ] || set -x
set -e
[ -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}"
[ -z "$IF_METRIC" ] && IF_METRIC="1"
[ -n "$IF_VRF_TABLE" ] && VRF_TABLE="table $IF_VRF_TABLE"
[ -n "$IF_METRIC" ] && METRIC="metric $IF_METRIC"
addr_family() {
if [ "$1" != "${1#*[0-9].[0-9]}" ]; then
@ -17,20 +18,31 @@ addr_family() {
}
configure_addresses() {
for i in $(ifquery -p address -i $INTERFACES_FILE $IFACE); do
addrfam=$(addr_family $i)
${MOCK} ip $addrfam addr $1 $i dev $IFACE
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 i in $(ifquery -p gateway -i $INTERFACES_FILE $IFACE); do
addrfam=$(addr_family $i)
${MOCK} ip $addrfam route $1 default via $i $VRF_TABLE $METRIC
for gw in ${IF_GATEWAYS}; do
addrfam=$(addr_family ${gw})
${MOCK} ip "${addrfam}" route add default via "${gw}" ${VRF_TABLE} ${METRIC} dev "${IFACE}"
done
}
[ -z "$VERBOSE" ] || set -x
flush() {
cmd="addr"
arg="dev ${IFACE}"
${MOCK} ip ${cmd} flush ${arg}
}
case "$PHASE" in
up)
@ -38,8 +50,7 @@ up)
configure_gateways add
;;
down)
configure_gateways del
configure_addresses del
flush
;;
*) exit 0 ;;
esac

View file

@ -1,27 +1,106 @@
#!/bin/sh
set -e
# 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" ] && exit 1
[ -z "$IF_TUNNEL_LOCAL" -a -z "$IF_TUNNEL_LOCAL_DEV" ] && exit 1
[ -z "$IF_TUNNEL_REMOTE" ] && exit 1
[ -z "$IF_TUNNEL_MODE" ] && exit 1
COMMAND="tunnel"
FAMILY="-4"
[ -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
gretap)
COMMAND="link"
;;
vti6|ipip6|ip6*)
FAMILY="-6"
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
@ -37,13 +116,13 @@ PARAMS=$(set | sed -E '
[ "$PARAMS" ] || exit 0
case "$PHASE" in
pre-up)
${MOCK} eval ip $FAMILY $COMMAND add $IFACE $PARAMS
create)
${MOCK} eval ip -$FAMILY $OBJECT add $IFACE $TYPE_KW $TUNNEL_MODE $PARAMS $MORE_PARAMS
;;
post-down)
${MOCK} ip $FAMILY $COMMAND del $IFACE
destroy)
${MOCK} ip -$FAMILY $OBJECT del $IFACE
;;
depend)
echo "$IF_TUNNEL_DEV"
echo "${IF_TUNNEL_DEV}" "${IF_TUNNEL_LOCAL_DEV}"
;;
esac

View file

@ -1,6 +1,4 @@
#!/bin/sh
set -e
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
@ -14,14 +12,14 @@ handle_member() {
[ -n "$VERBOSE" ] && set -x
case "$PHASE" in
pre-up)
create)
[ -n "$IF_VRF_TABLE" ] && handle_init "add"
[ -n "$IF_VRF_MEMBER" ] && handle_member
exit 0
;;
post-down)
pre-up)
[ -n "$IF_VRF_MEMBER" ] && handle_member
;;
destroy)
[ -n "$IF_VRF_TABLE" ] && handle_init "del"
exit 0
;;
depend)
echo "$IF_VRF_MEMBER"

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

View file

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

View file

@ -1,5 +1,4 @@
#!/bin/sh
set -e
case "$PHASE" in
depend)
if [ "$IF_BRIDGE_PORTS" != "none" ]; then

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

@ -86,7 +86,7 @@ lif_dict_add_once(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_find(const struct lif_dict *dict, const char *key)
{
struct lif_node *iter;
@ -102,7 +102,7 @@ lif_dict_find(struct lif_dict *dict, const char *key)
}
struct lif_list *
lif_dict_find_all(struct lif_dict *dict, const char *key)
lif_dict_find_all(const struct lif_dict *dict, const char *key)
{
struct lif_list *entries = calloc(1, sizeof *entries);
struct lif_node *iter;

View file

@ -44,8 +44,8 @@ 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_add_once(struct lif_dict *dict, const char *key, void *data, lif_dict_cmp_t compar);
extern struct lif_dict_entry *lif_dict_find(struct lif_dict *dict, const char *key);
extern struct lif_list *lif_dict_find_all(struct lif_dict *dict, const char *key);
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

@ -30,6 +30,86 @@
#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, ...)
{
@ -55,10 +135,7 @@ 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
@ -118,11 +195,8 @@ lif_execute_fmt_with_result(const struct lif_execute_opts *opts, char *buf, size
return false;
}
int status;
no_result:
waitpid(child, &status, 0);
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
return lif_process_monitor(cmdbuf, child, opts->timeout);
}
bool
@ -140,10 +214,10 @@ lif_file_is_executable(const char *path)
}
bool
lif_maybe_run_executor(const struct lif_execute_opts *opts, char *const envp[], const char *executor, const char *phase)
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: attempting to run %s executor for phase %s\n", executor, phase);
fprintf(stderr, "ifupdown: %s: attempting to run %s executor for phase %s\n", lifname, executor, phase);
char pathbuf[4096];
@ -156,10 +230,10 @@ lif_maybe_run_executor(const struct lif_execute_opts *opts, char *const envp[],
}
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)
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: attempting to run %s executor for phase %s\n", executor, phase);
fprintf(stderr, "ifupdown: %s: attempting to run %s executor for phase %s\n", lifname, executor, phase);
char pathbuf[4096];

View file

@ -23,15 +23,17 @@ 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);
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);
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,8 +15,11 @@
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include "libifupdown/libifupdown.h"
/* internally rewrite problematic ifupdown2 tokens to ifupdown-ng equivalents */
@ -26,14 +30,80 @@ struct remap_token {
/* 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
@ -57,184 +127,407 @@ maybe_remap_token(const char *token)
return tokbuf;
}
bool
lif_interface_file_parse(struct lif_dict *collection, const char *filename)
static void
report_error(struct lif_interface_file_parse_state *state, const char *errfmt, ...)
{
lif_interface_collection_init(collection);
struct lif_interface *cur_iface = NULL;
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_interface_file_parse_state *state, const char *filename)
{
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 *token = lif_next_token(&bufp);
while (*token)
{
if (!strcmp(token, "dhcp"))
lif_interface_use_executor(cur_iface, "dhcp");
else if (!strcmp(token, "ppp"))
lif_interface_use_executor(cur_iface, "ppp");
else if (!strcmp(token, "inherits"))
{
token = lif_next_token(&bufp);
if (!*token)
{
fprintf(stderr, "%s: inherits without interface\n", filename);
goto parse_error;
}
if (!lif_interface_collection_inherit(cur_iface, collection, token))
{
fprintf(stderr, "%s: could not inherit %s\n", filename, token);
goto parse_error;
}
}
token = lif_next_token(&bufp);
}
}
else if (!strcmp(token, "use"))
{
char *executor = lif_next_token(&bufp);
if (cur_iface == NULL)
{
fprintf(stderr, "%s: use '%s' without interface\n", filename, executor);
goto parse_error;
}
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
if (!strcmp(executor, "bridge"))
cur_iface->is_bridge = true;
else if (!strcmp(executor, "bond"))
cur_iface->is_bond = true;
lif_interface_use_executor(cur_iface, executor);
}
else if (!strcmp(token, "inherit"))
{
token = lif_next_token(&bufp);
if (!*token)
{
fprintf(stderr, "%s: inherits without interface\n", filename);
goto parse_error;
}
if (!lif_interface_collection_inherit(cur_iface, collection, token))
{
fprintf(stderr, "%s: could not inherit %s\n", filename, token);
goto parse_error;
}
}
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 (!strcmp(token, "gateway"))
{
char *addr = lif_next_token(&bufp);
if (cur_iface == NULL)
{
fprintf(stderr, "%s: gateway '%s' without interface\n", filename, addr);
goto parse_error;
}
lif_interface_use_executor(cur_iface, "static");
lif_dict_add(&cur_iface->vars, token, strdup(addr));
}
else if (cur_iface != NULL)
{
token = maybe_remap_token(token);
lif_dict_add(&cur_iface->vars, token, strdup(bufp));
/* 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(cur_iface, addon);
free(addon);
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
if (!strcmp(addon, "bridge"))
cur_iface->is_bridge = true;
else if (!strcmp(addon, "bond"))
cur_iface->is_bond = true;
}
}
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,6 +59,63 @@ 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)
{
@ -141,6 +201,52 @@ lif_interface_use_executor(struct lif_interface *interface, const char *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
@ -153,6 +259,7 @@ lif_interface_collection_init(struct lif_dict *collection)
/* always enable loopback interface as part of a collection */
if_lo = lif_interface_collection_find(collection, "lo");
if_lo->is_auto = true;
if_lo->is_explicit = true;
lif_interface_use_executor(if_lo, "loopback");
}
@ -226,14 +333,13 @@ lif_interface_collection_delete(struct lif_dict *collection, struct lif_interfac
}
bool
lif_interface_collection_inherit(struct lif_interface *interface, struct lif_dict *collection, const char *ifname)
lif_interface_collection_inherit(struct lif_interface *interface, struct lif_interface *parent)
{
struct lif_interface *parent = lif_interface_collection_find(collection, ifname);
/* maybe convert any interface we are inheriting from into a template */
if (lif_config.implicit_template_conversion)
parent->is_template = true;
if (parent == NULL)
return false;
lif_dict_add(&interface->vars, "inherit", strdup(ifname));
lif_dict_add(&interface->vars, "inherit", strdup(parent->ifname));
interface->is_bond = parent->is_bond;
interface->is_bridge = parent->is_bridge;

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`.
@ -50,10 +48,16 @@ struct lif_interface {
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) \
@ -62,17 +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_dict *collection, const char *ifname);
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,6 +27,9 @@
#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))

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,15 +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 *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;
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;
@ -44,57 +51,58 @@ handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[
}
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)
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)
{
if (strcmp(entry->key, "use"))
return true;
const char *cmd = entry->data;
if (!lif_maybe_run_executor(opts, envp, cmd, phase))
if (!lif_maybe_run_executor(opts, envp, cmd, phase, lifname))
return false;
return true;
}
static bool
handle_executors_for_phase(const struct lif_execute_opts *opts, char *const envp[], struct lif_interface *iface, bool up, const char *phase)
handle_executors_for_phase(const struct lif_execute_opts *opts, char *const envp[], const struct lif_interface *iface, bool up, const char *phase)
{
struct lif_node *iter;
const struct lif_node *iter;
if (up)
{
LIF_DICT_FOREACH(iter, &iface->vars)
handle_single_executor_for_phase(iter->data, opts, envp, phase);
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);
handle_single_executor_for_phase(iter->data, opts, envp, phase, iface->ifname);
}
return true;
}
static bool
query_dependents_from_executors(const struct lif_execute_opts *opts, char *const envp[], struct lif_interface *iface, char *buf, size_t bufsize, const char *phase)
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)
{
struct lif_node *iter;
const struct lif_node *iter;
LIF_DICT_FOREACH(iter, &iface->vars)
{
char resbuf[1024] = {};
struct lif_dict_entry *entry = iter->data;
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
.interfaces_file = opts->interfaces_file,
.timeout = opts->timeout,
};
if (strcmp(entry->key, "use"))
continue;
const char *cmd = entry->data;
if (!lif_maybe_run_executor_with_result(&exec_opts, envp, cmd, resbuf, sizeof resbuf, phase))
if (!lif_maybe_run_executor_with_result(&exec_opts, envp, cmd, resbuf, sizeof resbuf, phase, iface->ifname))
return false;
if (!*resbuf)
@ -107,8 +115,41 @@ query_dependents_from_executors(const struct lif_execute_opts *opts, char *const
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, struct lif_interface *iface, const char *lifname, const char *phase, const char *mode)
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;
@ -124,22 +165,35 @@ build_environment(char **envp[], const struct lif_execute_opts *opts, struct lif
if (opts->interfaces_file)
lif_environment_push(envp, "INTERFACES_FILE", opts->interfaces_file);
struct lif_node *iter;
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"))
{
if (did_address)
continue;
struct lif_address *addr = entry->data;
char addrbuf[4096];
if (!lif_address_unparse(addr, addrbuf, sizeof addrbuf, true))
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;
lif_environment_push(envp, "IF_ADDRESS", addrbuf);
@ -149,6 +203,9 @@ build_environment(char **envp[], const struct lif_execute_opts *opts, struct lif
}
else if (!strcmp(entry->key, "gateway"))
{
/* Append address to buffer */
append_to_buffer(&gateways, &gateways_size, &gateways_end, entry->data);
if (did_gateway)
continue;
@ -158,9 +215,6 @@ build_environment(char **envp[], const struct lif_execute_opts *opts, struct lif
{
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);
}
char envkey[4096] = "IF_";
@ -177,6 +231,15 @@ build_environment(char **envp[], const struct lif_execute_opts *opts, struct lif
lif_environment_push(envp, envkey, (const char *) entry->data);
}
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
@ -237,9 +300,23 @@ lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interfac
if (!handle_commands_for_phase(opts, envp, iface, phase))
goto handle_error;
/* we should do error handling here, but ifupdown1 doesn't */
lif_execute_fmt(opts, envp, "/bin/run-parts /etc/network/if-%s.d", phase);
/* 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 %s", dir_path);
out_free:
lif_environment_free(&envp);
return true;
@ -248,6 +325,36 @@ handle_error:
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)
{
@ -257,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;
@ -265,12 +375,39 @@ 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)\n",
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;
}
@ -279,21 +416,29 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par
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;
if (!lif_lifecycle_query_dependents(opts, iface, lifname))
return false;
if (up)
{
/* when going up, dependents go up first. */
@ -304,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;
@ -313,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
{
@ -328,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

@ -22,6 +22,7 @@
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

@ -40,7 +40,7 @@ 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)

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;
@ -55,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
@ -69,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);
}
@ -81,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" : "");
}
}
@ -108,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;
@ -124,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,7 @@ syntax(2)
test_suite('ifupdown-ng')
atf_test_program{name='multicall_test'}
atf_test_program{name='ifquery_test'}
atf_test_program{name='ifup_test'}
atf_test_program{name='ifdown_test'}

View file

@ -0,0 +1,6 @@
lo=lo 1
br0=br0 1
bond0=bond0 2
eth0=eth0 3
eth1=eth1 2
tun0=tun0 1

View file

@ -0,0 +1,9 @@
auto br0
iface br0
requires bond0
iface bond0
requires eth0 eth1
iface tun0
requires eth0

View file

@ -0,0 +1,5 @@
eth0=eth0 5
tun0=tun0 1
tun1=tun1 1
tun2=tun2 1
tun3=tun3 1

View file

@ -0,0 +1,11 @@
iface tun0
requires eth0
iface tun1
requires eth0
iface tun2
requires eth0
iface tun3
requires eth0

View file

@ -0,0 +1,3 @@
lo=lo
a=a
b=b

View file

@ -0,0 +1,9 @@
auto a
iface a
use link
requires b
auto b
iface b
use link
requires a

View file

@ -0,0 +1,10 @@
iface eth0
use dhcp
hostname foo
iface eth1
use dhcp
iface eth2
use dhcp
dhcp-hostname bar

View file

@ -0,0 +1,14 @@
# cidr and without-cidr should be equivalent
iface cidr
address 203.0.113.1/32
iface cidr
address 203.0.113.2/24
iface without-cidr
address 203.0.113.1
netmask 32
iface without-cidr
address 203.0.113.2
netmask 24

View file

@ -0,0 +1,7 @@
auto eth0
iface eth0
address 203.0.113.2/32
pointopoint 192.0.2.1
gateway 192.0.2.1
address 2001:db8:1000:2::2/64
gateway 2001:db8:1000:2::1

View file

@ -0,0 +1,3 @@
dummy=dummy 2
bat=bat 2
br=br 1

View file

@ -0,0 +1,13 @@
auto dummy
iface dummy
link-type dummy
auto bat
iface bat
link-type dummy
requires dummy
auto br
iface br
link-type dummy
requires bat

9
tests/fixtures/wireguard.interfaces vendored Normal file
View file

@ -0,0 +1,9 @@
iface eth0
address 203.0.113.2/24
gateway 203.0.113.1
auto wg0
iface wg0
use wireguard
address 1.2.3.4/24
requires eth0

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