Compare commits
625 commits
ifupdown-n
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e978d1a42c | ||
|
|
80074c997f | ||
|
|
0e99af7669 | ||
|
|
b75e509f3d | ||
|
|
d83c8259e6 | ||
|
|
2477e7266c | ||
|
|
97b1a11be0 | ||
|
|
941d7c51d7 | ||
|
|
7a46b61996 | ||
|
|
571786ae91 | ||
|
|
dd3a99cfa8 | ||
|
|
67fc80fc78 | ||
|
|
65e5e07c5f | ||
|
|
0547924ee8 | ||
|
|
4033f6374f | ||
|
|
b25448f42f | ||
|
|
96fa8ccbf9 | ||
|
|
108c88014d | ||
|
|
9c00111932 | ||
|
|
12ffc3de12 | ||
|
|
34753136b8 | ||
|
|
559b4ad942 | ||
|
|
ed9aae85ed | ||
|
|
73d9788fab | ||
|
|
2b358fafc6 | ||
|
|
9467f85067 | ||
|
|
6175961880 | ||
|
|
c8a05a742b | ||
|
|
d4907af84f | ||
|
|
98241f4781 | ||
|
|
aee2d45e18 | ||
|
|
5860882ffb | ||
|
|
0755f7c32d | ||
|
|
bd319a9166 | ||
|
|
58b1cf1021 | ||
|
|
02bc14da19 | ||
|
|
279d3818b0 | ||
|
|
f3f8164ef1 | ||
|
|
667943f46c | ||
|
|
5b7f5b712b | ||
|
|
381c0ed1a5 | ||
|
|
bec5fcefce | ||
|
|
258d397d84 | ||
|
|
cea9840651 | ||
|
|
e88a6b7e10 | ||
|
|
9faa988326 | ||
|
|
5f9ba7e246 | ||
|
|
cd98102e62 | ||
|
|
98b23370e8 | ||
|
|
876a7700d7 | ||
|
|
2cec4ed05c | ||
|
|
e4fa275067 | ||
|
|
4c64b5138b | ||
|
|
1ee485666f | ||
|
|
7ef6be6d98 | ||
|
|
606e98d7bd | ||
|
|
ffcf1976b8 | ||
|
|
1c2fddfbaa | ||
|
|
71bc4b3dc9 | ||
|
|
b9d6c190e4 | ||
|
|
31bc5c3b10 | ||
|
|
f34ad12751 | ||
|
|
27c10c57d1 | ||
|
|
d1286e57f9 | ||
|
|
65aa268e9d | ||
|
|
1f7fe26dd9 | ||
|
|
3734aaecbd | ||
|
|
8a8f56dda8 | ||
|
|
bed0b67583 | ||
|
|
383ae31372 | ||
|
|
d7cc2a9917 | ||
|
|
93c8827205 | ||
|
|
589a1f3023 | ||
|
|
4e3a4c6cb8 | ||
|
|
eeb40937fb | ||
|
|
b2f5a62c35 | ||
|
|
f77d3558f7 | ||
|
|
cef4fafdb9 | ||
|
|
3d47b34d7a | ||
|
|
b10094eae7 | ||
|
|
c4d9d6fd06 | ||
|
|
0dd7756df2 | ||
|
|
43159fec82 | ||
|
|
259851a829 | ||
|
|
e1fb4c6087 | ||
|
|
ff8e5d392c | ||
|
|
b21cb37df0 | ||
|
|
aada42795c | ||
|
|
6730db5468 | ||
|
|
7911944633 | ||
|
|
46bb0565fa | ||
|
|
9715b41c28 | ||
|
|
89b96a5108 | ||
|
|
545c3870ab | ||
|
|
d2a75c7bb4 | ||
|
|
dd8064142c | ||
|
|
3bf406bf92 | ||
|
|
c5520f95dd | ||
|
|
7ca5305063 | ||
|
|
8097d5015f | ||
|
|
dbfebbff87 | ||
|
|
5010dce3d5 | ||
|
|
95f0ea4895 | ||
|
|
2ec3a39c89 | ||
|
|
cfb43e9573 | ||
|
|
fe1664d311 | ||
|
|
ec6077f26f | ||
|
|
2a9ca329ff | ||
|
|
f6ad65d99e | ||
|
|
d250ab213c | ||
|
|
f5fc3a3c1a | ||
|
|
748a226786 | ||
|
|
c8dad76fe1 | ||
|
|
c7eeef27a4 | ||
|
|
52f739c943 | ||
|
|
71d832cceb | ||
|
|
632af7b716 | ||
|
|
af25cedf33 | ||
|
|
c292297396 | ||
|
|
5667b6adec | ||
|
|
2d2e73b8ac | ||
|
|
068f464e4c | ||
|
|
0050995b64 | ||
|
|
20d9e3fe91 | ||
|
|
b76981e832 | ||
|
|
8cae485ca7 | ||
|
|
6cadc44183 | ||
|
|
da751ce14d | ||
|
|
dcbb3be15b | ||
|
|
19a5a671eb | ||
|
|
90453f1412 | ||
|
|
b4f87cbd1e | ||
|
|
2a8a72eee7 | ||
|
|
eb9bebebc6 | ||
|
|
61097b1db2 | ||
|
|
b09d622cfc | ||
|
|
dae7d59864 | ||
|
|
4f7063ba0f | ||
|
|
817262fa33 | ||
|
|
b201f351ec | ||
|
|
9bcf914250 | ||
|
|
c810cd5817 | ||
|
|
e5d9ee25fc | ||
|
|
02a74985ab | ||
|
|
fb1d3181fe | ||
|
|
ab7b1f5d24 | ||
|
|
480fc5eecb | ||
|
|
d86297f29c | ||
|
|
56beefdd28 | ||
|
|
a5761afd70 | ||
|
|
05a3b1539b | ||
|
|
03528b01ad | ||
|
|
3d743f512f | ||
|
|
6c5d856ac4 | ||
|
|
36eb6e3377 | ||
|
|
e7ee26ac19 | ||
|
|
80a590ca33 | ||
|
|
d96f579d7f | ||
|
|
509b75bc73 | ||
|
|
3e6129907c | ||
|
|
e6de8465de | ||
|
|
47f74997a7 | ||
|
|
4aa2749737 | ||
|
|
a210cf6a66 | ||
|
|
aec7dad1c1 | ||
|
|
086eca2b4e | ||
|
|
f6fe06298f | ||
|
|
ee5e8b5702 | ||
|
|
5035d2e160 | ||
|
|
8d4c7461af | ||
|
|
0df3e03b68 | ||
|
|
dcb6ef97ef | ||
|
|
3bcfe91e84 | ||
|
|
d76c2df460 | ||
|
|
ef3be06b38 | ||
|
|
1668d17c6b | ||
|
|
fcea23dbcd | ||
|
|
311ad74792 | ||
|
|
e300867d26 | ||
|
|
89d84ec475 | ||
|
|
3b7181cf28 | ||
|
|
24bfcc1737 | ||
|
|
de207a5950 | ||
|
|
365461a6f6 | ||
|
|
a72d87df0b | ||
|
|
02324bebd5 | ||
|
|
17a410b87c | ||
|
|
98ba7f46cf | ||
|
|
68c51a79e7 | ||
|
|
f707c5c1ef | ||
|
|
6423a284b8 | ||
|
|
172daa16a0 | ||
|
|
a1828c688b | ||
|
|
2d32b1577c | ||
|
|
4c354ebf35 | ||
|
|
959617df88 | ||
|
|
0cade539f8 | ||
|
|
122a54377d | ||
|
|
b2e657cdf0 | ||
|
|
175f002b3e | ||
|
|
953b2274f7 | ||
|
|
5db2f29891 | ||
|
|
a67d518ea9 | ||
|
|
5b6cb5a128 | ||
|
|
13a8daf96e | ||
|
|
27a7201b45 | ||
|
|
332ea7c7d3 | ||
|
|
59290415a0 | ||
|
|
5d6c7732ed | ||
|
|
5302bee850 | ||
|
|
1a2298a759 | ||
|
|
99b0d67b8e | ||
|
|
4a8230f916 | ||
|
|
5557804af9 | ||
|
|
44be0c0721 | ||
|
|
8a58c0ae6d | ||
|
|
6f588a01d5 | ||
|
|
df6835bd62 | ||
|
|
6dc43a2bb4 | ||
|
|
9832865539 | ||
|
|
63a5503185 | ||
|
|
87fa1760be | ||
|
|
584f0634ed | ||
|
|
596dd8165b | ||
|
|
05711db6ce | ||
|
|
adf0f9dd46 | ||
|
|
3d1d384a6b | ||
|
|
48c87e34c1 | ||
|
|
37299e312f | ||
|
|
a66e8465d1 | ||
|
|
9ee3a874d4 | ||
|
|
67163c6561 | ||
|
|
cc06712611 | ||
|
|
74b6f9487c | ||
|
|
68e08cae7e | ||
|
|
7cf353dd22 | ||
|
|
704b2feecb | ||
|
|
0b23f5bc86 | ||
|
|
98ac007ffe | ||
|
|
12b0872349 | ||
|
|
1f1d4f5bad | ||
|
|
fef7c55270 | ||
|
|
c6faf452b9 | ||
|
|
ee582ac9b0 | ||
|
|
3f67b2137d | ||
|
|
bd8ad91de6 | ||
|
|
3035627c93 | ||
|
|
f708bb1465 | ||
|
|
044d8307f9 | ||
|
|
5615c30fd0 | ||
|
|
9c76528fe1 | ||
|
|
88f25e41c6 | ||
|
|
a3d11ded43 | ||
|
|
8dc2295006 | ||
|
|
fafce2f262 | ||
|
|
d177bfa553 | ||
|
|
a55ef85776 | ||
|
|
60d0ed34b8 | ||
|
|
243a9b92ce | ||
|
|
f224c04804 | ||
|
|
f9683c2242 | ||
|
|
580f054d7e | ||
|
|
94f00b2fdc | ||
|
|
41a71173d1 | ||
|
|
5c5c316ebf | ||
|
|
a3987b46c8 | ||
|
|
eb70e48ece | ||
|
|
850b20d5a8 | ||
|
|
0a0252d218 | ||
|
|
2e81234df1 | ||
|
|
abc3b13fd7 | ||
|
|
566732321d | ||
|
|
3cb635b443 | ||
|
|
f96cb273a0 | ||
|
|
a69c81b6be | ||
|
|
3e7125aca0 | ||
|
|
2078f63e62 | ||
|
|
c19def3764 | ||
|
|
e8a2aab7e1 | ||
|
|
6ae3414329 | ||
|
|
4e78d7e5d5 | ||
|
|
0a58383ed8 | ||
|
|
e4b4d8b70a | ||
|
|
72277e51e2 | ||
|
|
b0480a3343 | ||
|
|
f7e7b4be85 | ||
|
|
eac42c104b | ||
|
|
63ec8d4b3b | ||
|
|
a8fcf5502f | ||
|
|
27e11adac6 | ||
|
|
7bacb2a0f7 | ||
|
|
f35242e5c7 | ||
|
|
f10faf953c | ||
|
|
d814aa754f | ||
|
|
27a383cfa7 | ||
|
|
4b30dc4573 | ||
|
|
c1c9115e5d | ||
|
|
b4e35c442e | ||
|
|
5b27d8408c | ||
|
|
02d044c391 | ||
|
|
fafa5ed7f9 | ||
|
|
537a56414a | ||
|
|
f9d0fbb4c3 | ||
|
|
67da195f9f | ||
|
|
aba546cf8e | ||
|
|
b4b0889c7a | ||
|
|
6048d65d87 | ||
|
|
5d4e3699b2 | ||
|
|
a7e27ffa2c | ||
|
|
e15f3ffbaa | ||
|
|
e754e836af | ||
|
|
ceb82f4fd2 | ||
|
|
a6e022ad99 | ||
|
|
258e2e8a52 | ||
|
|
8c8727e30f | ||
|
|
2569503afa | ||
|
|
a5eebda391 | ||
|
|
67dce280d7 | ||
|
|
04a65c5c38 | ||
|
|
0674a70c35 | ||
|
|
e02e495257 | ||
|
|
d9e78e14fb | ||
|
|
af94d760d0 | ||
|
|
35e03475e4 | ||
|
|
e6c6f49143 | ||
|
|
be9857f66f | ||
|
|
5a0b53cd73 | ||
|
|
bdc66bcba9 | ||
|
|
ce6954d628 | ||
|
|
00da19a381 | ||
|
|
277ecaf78a | ||
|
|
885126174d | ||
|
|
6999a125c8 | ||
|
|
c021b9420f | ||
|
|
910985cd22 | ||
|
|
3b178131bd | ||
|
|
b514d31c26 | ||
|
|
efe9b60e37 | ||
|
|
8de83fdd9a | ||
|
|
f139bc2416 | ||
|
|
ca07082ff4 | ||
|
|
6603ff1000 | ||
|
|
288976f015 | ||
|
|
865bf7cac6 | ||
|
|
8e4eb5d00c | ||
|
|
923b96fab8 | ||
|
|
3b10494b40 | ||
|
|
30fa4c4a2e | ||
|
|
ae1dc41c88 | ||
|
|
017a12760c | ||
|
|
be6ce3c319 | ||
|
|
9784392a68 | ||
|
|
32b7dda832 | ||
|
|
4a05010539 | ||
|
|
aa3e94acf8 | ||
|
|
d24b4ab3e6 | ||
|
|
37a7d8f097 | ||
|
|
58f010fe91 | ||
|
|
2e6b3ca1ff | ||
|
|
a6b95d495c | ||
|
|
0e5ec5b260 | ||
|
|
a59109cb66 | ||
|
|
acb555b75a | ||
|
|
f32481deb9 | ||
|
|
471549db4b | ||
|
|
6805262a9b | ||
|
|
2311316bfe | ||
|
|
7edb29778b | ||
|
|
2d7668bc01 | ||
|
|
4b32a0787d | ||
|
|
ab7ff2b206 | ||
|
|
e854819e8f | ||
|
|
7d81ceb898 | ||
|
|
64b24b02ed | ||
|
|
375b5d46cb | ||
|
|
570679c5bf | ||
|
|
61ed18db2c | ||
|
|
1f21d2bb45 | ||
|
|
8f3e1f06f1 | ||
|
|
583ff684df | ||
|
|
e51ce613e6 | ||
|
|
36fe61d8e7 | ||
|
|
e52e94fe5e | ||
|
|
d21d83fb26 | ||
|
|
14914f1251 | ||
|
|
68415ce71d | ||
|
|
e7ef2d1e83 | ||
|
|
4d64176ea3 | ||
|
|
b30f84fdbd | ||
|
|
cca3608ad7 | ||
|
|
6c7c3f570d | ||
|
|
b311293c6b | ||
|
|
0ff263a02a | ||
|
|
24d092ee46 | ||
|
|
aefaaa7457 | ||
|
|
3997b6a952 | ||
|
|
db5d2b8afd | ||
|
|
b378305286 | ||
|
|
de20e5f8a9 | ||
|
|
dfb979d00d | ||
|
|
80cdfc7f96 | ||
|
|
dc52642d73 | ||
|
|
8fe264a120 | ||
|
|
aba4b18862 | ||
|
|
050ab77bd3 | ||
|
|
68021bc652 | ||
|
|
f17fa09944 | ||
|
|
7da13ddf83 | ||
|
|
536612d02b | ||
|
|
14e2fecc20 | ||
|
|
3abe6b1bd5 | ||
|
|
1384b38c78 | ||
|
|
8e7c935cdf | ||
|
|
badcccaa5b | ||
|
|
72ad50213f | ||
|
|
90ba72efe2 | ||
|
|
05df95964e | ||
|
|
885778a412 | ||
|
|
ce07e1ff1f | ||
|
|
6d15f21073 | ||
|
|
aba140a977 | ||
|
|
4a11d4fdd8 | ||
|
|
df9c0284b1 | ||
|
|
b57aba1a97 | ||
|
|
d36a522470 | ||
|
|
a7dc005b8d | ||
|
|
4fdd91beee | ||
|
|
0928f07af5 | ||
|
|
e5c592f2c1 | ||
|
|
846df3272f | ||
|
|
d463d455da | ||
|
|
9caffc01c2 | ||
|
|
184526401b | ||
|
|
c86952ce71 | ||
|
|
6d2ae938d1 | ||
|
|
d31f8614a3 | ||
|
|
8f057286d3 | ||
|
|
b08b52939f | ||
|
|
cd1b0d5833 | ||
|
|
59ba50ba4e | ||
|
|
93116920da | ||
|
|
075352b631 | ||
|
|
0238051052 | ||
|
|
5fb1b4b26c | ||
|
|
953666f1fb | ||
|
|
c24143982f | ||
|
|
a4f147b5d0 | ||
|
|
36271e8657 | ||
|
|
9cc74b58c4 | ||
|
|
ac56a85074 | ||
|
|
e18ab114e0 | ||
|
|
c2465344d5 | ||
|
|
472319b847 | ||
|
|
0dd8dc1b0e | ||
|
|
dd02502461 | ||
|
|
823544aace | ||
|
|
aa11601893 | ||
|
|
71290da711 | ||
|
|
cb03eb1ee9 | ||
|
|
eeb40fc689 | ||
|
|
56323828a9 | ||
|
|
3e427a1327 | ||
|
|
a08df1249b | ||
|
|
2c37b22d70 | ||
|
|
ba98705c8f | ||
|
|
ba2ad348ff | ||
|
|
a2a532c8ba | ||
|
|
962ef0d415 | ||
|
|
d6c7bdb299 | ||
|
|
fb38e22cd1 | ||
|
|
06e2d3b035 | ||
|
|
f282704186 | ||
|
|
b9efc841a3 | ||
|
|
b084f1c99c | ||
|
|
0a3f1e33a8 | ||
|
|
9928beef68 | ||
|
|
99571a4c03 | ||
|
|
2c562b7be0 | ||
|
|
520b831bdf | ||
|
|
e2959275b6 | ||
|
|
327e738a55 | ||
|
|
9fed844352 | ||
|
|
1a11bb768f | ||
|
|
7b45d3939d | ||
|
|
6106164d7c | ||
|
|
95d943ee44 | ||
|
|
05e0ac139b | ||
|
|
36f0930c27 | ||
|
|
db57f7c07c | ||
|
|
0999347dab | ||
|
|
196c000161 | ||
|
|
b30e0223d0 | ||
|
|
ff4822cf05 | ||
|
|
4e3d2dbbc3 | ||
|
|
f7d21d4b46 | ||
|
|
fda7bfae57 | ||
|
|
f6d9f65248 | ||
|
|
fdfe8ac080 | ||
|
|
52509c508c | ||
|
|
f871002ecc | ||
|
|
2be5add47b | ||
|
|
b817bdb999 | ||
|
|
b481db6c20 | ||
|
|
ebd04cafda | ||
|
|
f96a74f232 | ||
|
|
41e9751ac0 | ||
|
|
a547ab2825 | ||
|
|
ff65bd53b5 | ||
|
|
b87a0df83d | ||
|
|
3205a89840 | ||
|
|
5bf6c84464 | ||
|
|
d9fd545ad1 | ||
|
|
0de4a3f6f9 | ||
|
|
b27caaedf6 | ||
|
|
072c1ea148 | ||
|
|
049af8b9fb | ||
|
|
66e6a09bbe | ||
|
|
8b00727370 | ||
|
|
369f3ff3c2 | ||
|
|
49dd8942f2 | ||
|
|
8615cf62ef | ||
|
|
df2519c1ec | ||
|
|
0d6b9d5d17 | ||
|
|
6728d2febe | ||
|
|
6c7fa2919d | ||
|
|
3d5ca9050e | ||
|
|
0eb25f1d62 | ||
|
|
1964a99e5d | ||
|
|
ed56d2b33b | ||
|
|
7a4c16c665 | ||
|
|
58bc969bbb | ||
|
|
ef3bdd79da | ||
|
|
c71c01862d | ||
|
|
0ca8e42ee6 | ||
|
|
91115edeee | ||
|
|
2fecbfc7e9 | ||
|
|
2be1fa5b0b | ||
|
|
edfb91c05f | ||
|
|
e4c3c4e06e | ||
|
|
37b5c372f3 | ||
|
|
81d0ebc3e8 | ||
|
|
cfbfa07e85 | ||
|
|
58816712c6 | ||
|
|
8045081024 | ||
|
|
9d958892ed | ||
|
|
73f3690432 | ||
|
|
2420023b74 | ||
|
|
f51c976b52 | ||
|
|
b1dfa609f9 | ||
|
|
cf59f6af4c | ||
|
|
21bcbe1017 | ||
|
|
c97594b89b | ||
|
|
fb5ff4eac2 | ||
|
|
3a75de9f8c | ||
|
|
3302089d4b | ||
|
|
cb9d347c8a | ||
|
|
ef586a6b94 | ||
|
|
5b9ade9db8 | ||
|
|
e1918f3fd5 | ||
|
|
7af585e738 | ||
|
|
8ef2815ef3 | ||
|
|
d88d9b7de4 | ||
|
|
8fb2a0571d | ||
|
|
326e27cdb4 | ||
|
|
83440e3f88 | ||
|
|
e3a6e08e40 | ||
|
|
d35a3e3b38 | ||
|
|
5f078537bf | ||
|
|
08805f7de3 | ||
|
|
be00984946 | ||
|
|
83ccb4f81d | ||
|
|
e046ea6a24 | ||
|
|
52d2bad3b8 | ||
|
|
05e8f9bbb5 | ||
|
|
12307aeb9d | ||
|
|
43c147c6cf | ||
|
|
c1e1d30a58 | ||
|
|
e29e552cc2 | ||
|
|
cae16c5758 | ||
|
|
9e4e17707f | ||
|
|
0669e897fd | ||
|
|
987e6399de | ||
|
|
052156ac09 | ||
|
|
f06ee76d41 | ||
|
|
690190a0b9 | ||
|
|
4e452c5fec | ||
|
|
ea1c39ff5a | ||
|
|
3e2d447a44 | ||
|
|
38af1ce2d2 | ||
|
|
00c1d0f58c | ||
|
|
96392f0dd1 | ||
|
|
201e00bbac | ||
|
|
c51c9f7103 | ||
|
|
7eeb7c98b5 | ||
|
|
cd8fbb64fb | ||
|
|
559bf2d1a4 | ||
|
|
fa0a0d7f79 | ||
|
|
cf8ae46ca0 | ||
|
|
dca34ebba7 | ||
|
|
95a72b5663 | ||
|
|
f8c14fc233 | ||
|
|
83d64b0a69 | ||
|
|
96112d5dc9 | ||
|
|
4a5a37a620 | ||
|
|
eb8f1938dc | ||
|
|
b35d985ea4 | ||
|
|
1919f9b0d3 | ||
|
|
cbcd8e8326 | ||
|
|
492e9e7038 | ||
|
|
d582e405b6 | ||
|
|
60633e4acb | ||
|
|
03e00cbb02 | ||
|
|
532aaa30de | ||
|
|
475aeb2992 | ||
|
|
cebe24e1ec | ||
|
|
915f04912c | ||
|
|
bc48534814 | ||
|
|
6d1c67ed38 | ||
|
|
713d5071d5 | ||
|
|
464c078bbe | ||
|
|
ef86d9400b | ||
|
|
7eb81a1b3a | ||
|
|
ef6600a91e | ||
|
|
53b7fbd393 | ||
|
|
85cab33148 |
153 changed files with 9264 additions and 695 deletions
10
.github/workflows/build.yml
vendored
10
.github/workflows/build.yml
vendored
|
|
@ -10,10 +10,16 @@ jobs:
|
|||
- name: Update system and add dependencies
|
||||
run: |
|
||||
apk upgrade -Ua
|
||||
apk add build-base git kyua atf
|
||||
apk add build-base git kyua atf scdoc
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Build and run tests
|
||||
- name: Build
|
||||
run: make
|
||||
|
||||
- name: Build documentation
|
||||
run: make docs
|
||||
|
||||
- name: Run tests
|
||||
run: make check
|
||||
|
|
|
|||
12
.gitignore
vendored
12
.gitignore
vendored
|
|
@ -1,5 +1,17 @@
|
|||
*.o
|
||||
*.a
|
||||
*.1
|
||||
*.2
|
||||
*.3
|
||||
*.4
|
||||
*.5
|
||||
*.6
|
||||
*.7
|
||||
*.8
|
||||
ifupdown
|
||||
ifquery
|
||||
ifup
|
||||
ifdown
|
||||
ifctrstat
|
||||
ifparse
|
||||
*.lock
|
||||
|
|
|
|||
3
COPYING
3
COPYING
|
|
@ -1,4 +1,5 @@
|
|||
Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
Copyright (c) 2020-2021 Ariadne Conill <ariadne@dereferenced.org>
|
||||
Copyright (c) 2020-2021 Maximilian Wilhelm <max@sdn.clinic>
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
|
|||
198
Makefile
198
Makefile
|
|
@ -1,12 +1,30 @@
|
|||
LAYOUT ?= linux
|
||||
SCDOC := scdoc
|
||||
LIBBSD_CFLAGS =
|
||||
LIBBSD_LIBS =
|
||||
|
||||
PACKAGE_NAME := ifupdown-ng
|
||||
PACKAGE_VERSION := 0.2
|
||||
PACKAGE_BUGREPORT := https://github.com/kaniini/ifupdown-ng/issues/new
|
||||
PACKAGE_VERSION := 0.11.3
|
||||
PACKAGE_BUGREPORT := https://github.com/ifupdown-ng/ifupdown-ng/issues/new
|
||||
|
||||
|
||||
INTERFACES_FILE := /etc/network/interfaces
|
||||
STATE_FILE := /run/ifstate
|
||||
CFLAGS = -ggdb3 -Os -Wall -Wextra
|
||||
CPPFLAGS = -I. -DINTERFACES_FILE=\"${INTERFACES_FILE}\" -DSTATE_FILE=\"${STATE_FILE}\" -DPACKAGE_NAME=\"${PACKAGE_NAME}\" -DPACKAGE_VERSION=\"${PACKAGE_VERSION}\" -DPACKAGE_BUGREPORT=\"${PACKAGE_BUGREPORT}\"
|
||||
CONFIG_FILE := /etc/network/ifupdown-ng.conf
|
||||
EXECUTOR_PATH := /usr/libexec/ifupdown-ng
|
||||
|
||||
CFLAGS ?= -ggdb3 -Os
|
||||
CFLAGS += -Wall -Wextra -Werror
|
||||
CFLAGS += -Wmissing-declarations -Wmissing-prototypes -Wcast-align -Wpointer-arith -Wreturn-type
|
||||
CFLAGS += ${LIBBSD_CFLAGS}
|
||||
CPPFLAGS = -I.
|
||||
CPPFLAGS += -DINTERFACES_FILE=\"${INTERFACES_FILE}\"
|
||||
CPPFLAGS += -DSTATE_FILE=\"${STATE_FILE}\"
|
||||
CPPFLAGS += -DCONFIG_FILE=\"${CONFIG_FILE}\"
|
||||
CPPFLAGS += -DPACKAGE_NAME=\"${PACKAGE_NAME}\"
|
||||
CPPFLAGS += -DPACKAGE_VERSION=\"${PACKAGE_VERSION}\"
|
||||
CPPFLAGS += -DPACKAGE_BUGREPORT=\"${PACKAGE_BUGREPORT}\"
|
||||
CPPFLAGS += -DEXECUTOR_PATH=\"${EXECUTOR_PATH}\"
|
||||
|
||||
|
||||
LIBIFUPDOWN_SRC = \
|
||||
|
|
@ -19,47 +37,183 @@ LIBIFUPDOWN_SRC = \
|
|||
libifupdown/state.c \
|
||||
libifupdown/environment.c \
|
||||
libifupdown/execute.c \
|
||||
libifupdown/lifecycle.c
|
||||
|
||||
libifupdown/lifecycle.c \
|
||||
libifupdown/config-parser.c \
|
||||
libifupdown/config-file.c \
|
||||
libifupdown/compat.c
|
||||
LIBIFUPDOWN_OBJ = ${LIBIFUPDOWN_SRC:.c=.o}
|
||||
LIBIFUPDOWN_LIB = libifupdown.a
|
||||
|
||||
MULTICALL_SRC = cmd/multicall.c
|
||||
MULTICALL_SRC = \
|
||||
cmd/multicall.c \
|
||||
cmd/multicall-options.c \
|
||||
cmd/multicall-exec-options.c \
|
||||
cmd/multicall-match-options.c \
|
||||
cmd/pretty-print-iface.c
|
||||
MULTICALL_OBJ = ${MULTICALL_SRC:.c=.o}
|
||||
MULTICALL = ifupdown
|
||||
|
||||
IFQUERY_SRC = cmd/ifquery.c
|
||||
IFQUERY_OBJ = ${IFQUERY_SRC:.c=.o}
|
||||
|
||||
# enable ifup/ifdown applets (+16 KB)
|
||||
CONFIG_IFUPDOWN ?= Y
|
||||
IFUPDOWN_SRC = cmd/ifupdown.c
|
||||
IFUPDOWN_OBJ = ${IFUPDOWN_SRC:.c=.o}
|
||||
MULTICALL_${CONFIG_IFUPDOWN}_OBJ += ${IFUPDOWN_SRC:.c=.o}
|
||||
CMDS_${CONFIG_IFUPDOWN} += ifup ifdown
|
||||
CPPFLAGS_${CONFIG_IFUPDOWN} += -DCONFIG_IFUPDOWN
|
||||
|
||||
CMD_OBJ = ${MULTICALL_OBJ} ${IFQUERY_OBJ} ${IFUPDOWN_OBJ}
|
||||
# enable ifquery applet (+4 KB)
|
||||
# [+20 KB without ifup/ifdown]
|
||||
CONFIG_IFQUERY ?= Y
|
||||
IFQUERY_SRC = cmd/ifquery.c
|
||||
MULTICALL_${CONFIG_IFQUERY}_OBJ += ${IFQUERY_SRC:.c=.o}
|
||||
CMDS_${CONFIG_IFQUERY} += ifquery
|
||||
CPPFLAGS_${CONFIG_IFQUERY} += -DCONFIG_IFQUERY
|
||||
|
||||
CMDS = ifup ifdown ifquery
|
||||
# enable ifctrstat applet (+1 KB)
|
||||
CONFIG_IFCTRSTAT ?= Y
|
||||
IFCTRSTAT_SRC = cmd/ifctrstat.c cmd/ifctrstat-${LAYOUT}.c
|
||||
MULTICALL_${CONFIG_IFCTRSTAT}_OBJ += ${IFCTRSTAT_SRC:.c=.o}
|
||||
CMDS_${CONFIG_IFCTRSTAT} += ifctrstat
|
||||
CPPFLAGS_${CONFIG_IFCTRSTAT} += -DCONFIG_IFCTRSTAT
|
||||
|
||||
LIBS = ${LIBIFUPDOWN_LIB}
|
||||
# enable ifparse applet (+1 KB)
|
||||
CONFIG_IFPARSE ?= Y
|
||||
IFPARSE_SRC = cmd/ifparse.c
|
||||
MULTICALL_${CONFIG_IFPARSE}_OBJ += ${IFPARSE_SRC:.c=.o}
|
||||
CMDS_${CONFIG_IFPARSE} += ifparse
|
||||
CPPFLAGS_${CONFIG_IFPARSE} += -DCONFIG_IFPARSE
|
||||
|
||||
all: libifupdown.a ${MULTICALL} ${CMDS}
|
||||
# enable YAML support (+2 KB)
|
||||
CONFIG_YAML ?= Y
|
||||
YAML_SRC = \
|
||||
libifupdown/yaml-base.c \
|
||||
libifupdown/yaml-writer.c
|
||||
LIBIFUPDOWN_${CONFIG_YAML}_OBJ += ${YAML_SRC:.c=.o}
|
||||
CPPFLAGS_${CONFIG_YAML} += -DCONFIG_YAML
|
||||
|
||||
LIBIFUPDOWN_OBJ += ${LIBIFUPDOWN_Y_OBJ}
|
||||
MULTICALL_OBJ += ${MULTICALL_Y_OBJ}
|
||||
CMDS += ${CMDS_Y}
|
||||
CPPFLAGS += ${CPPFLAGS_Y}
|
||||
|
||||
EXECUTOR_SCRIPTS_CORE ?= \
|
||||
dhcp \
|
||||
ipv6-ra \
|
||||
static \
|
||||
link \
|
||||
ppp \
|
||||
forward
|
||||
|
||||
EXECUTOR_SCRIPTS_OPT ?= \
|
||||
batman \
|
||||
bond \
|
||||
bridge \
|
||||
ethtool \
|
||||
gre \
|
||||
mpls \
|
||||
tunnel \
|
||||
vrf \
|
||||
vxlan \
|
||||
wifi \
|
||||
wireguard
|
||||
|
||||
EXECUTOR_SCRIPTS ?= ${EXECUTOR_SCRIPTS_CORE} ${EXECUTOR_SCRIPTS_OPT}
|
||||
|
||||
EXECUTOR_SCRIPTS_STUB ?=
|
||||
|
||||
EXECUTOR_SCRIPTS_NATIVE ?=
|
||||
|
||||
TARGET_LIBS = ${LIBIFUPDOWN_LIB}
|
||||
LIBS += ${TARGET_LIBS} ${LIBBSD_LIBS}
|
||||
|
||||
all: ${MULTICALL} ${CMDS}
|
||||
|
||||
${CMDS}: ${MULTICALL}
|
||||
ln -s ifupdown $@
|
||||
ln -sf ifupdown $@
|
||||
|
||||
${MULTICALL}: ${LIBS} ${CMD_OBJ}
|
||||
${CC} -o $@ ${CMD_OBJ} ${LIBS}
|
||||
${MULTICALL}: ${TARGET_LIBS} ${MULTICALL_OBJ}
|
||||
${CC} -o $@ ${MULTICALL_OBJ} ${LIBS}
|
||||
|
||||
libifupdown.a: ${LIBIFUPDOWN_OBJ}
|
||||
${LIBIFUPDOWN_LIB}: ${LIBIFUPDOWN_OBJ}
|
||||
${AR} -rcs $@ ${LIBIFUPDOWN_OBJ}
|
||||
|
||||
clean:
|
||||
rm -f ${LIBIFUPDOWN_OBJ} ${CMD_OBJ}
|
||||
rm -f ${LIBIFUPDOWN_OBJ} ${MULTICALL_OBJ}
|
||||
rm -f ${LIBIFUPDOWN_LIB}
|
||||
rm -f ${CMDS} ${MULTICALL}
|
||||
rm -f ${MANPAGES}
|
||||
|
||||
check: libifupdown.a ${CMDS}
|
||||
kyua test
|
||||
check: ${LIBIFUPDOWN_LIB} ${CMDS}
|
||||
kyua test || (kyua report --verbose && exit 1)
|
||||
|
||||
install: all
|
||||
install -D -m755 ${MULTICALL} ${DESTDIR}/sbin/${MULTICALL}
|
||||
for i in ${CMDS}; do \
|
||||
ln -s /sbin/${MULTICALL} ${DESTDIR}/sbin/$$i; \
|
||||
done
|
||||
for i in ${EXECUTOR_SCRIPTS}; do \
|
||||
install -D -m755 executor-scripts/${LAYOUT}/$$i ${DESTDIR}${EXECUTOR_PATH}/$$i; \
|
||||
done
|
||||
for i in ${EXECUTOR_SCRIPTS_STUB}; do \
|
||||
install -D -m755 executor-scripts/stub/$$i ${DESTDIR}${EXECUTOR_PATH}/$$i; \
|
||||
done
|
||||
for i in ${EXECUTOR_SCRIPTS_NATIVE}; do \
|
||||
install -D -m755 executor-scripts/${LAYOUT}-native/$$i ${DESTDIR}${EXECUTOR_PATH}/$$i; \
|
||||
done
|
||||
install -D -m644 dist/ifupdown-ng.conf.example ${DESTDIR}${CONFIG_FILE}.example
|
||||
|
||||
.scd.1 .scd.2 .scd.3 .scd.4 .scd.5 .scd.6 .scd.7 .scd.8:
|
||||
${SCDOC} < $< > $@
|
||||
|
||||
MANPAGES_5 = \
|
||||
doc/ifstate.5 \
|
||||
doc/ifupdown-ng.conf.5 \
|
||||
doc/interfaces.5 \
|
||||
doc/interfaces-bond.5 \
|
||||
doc/interfaces-batman.5 \
|
||||
doc/interfaces-bridge.5 \
|
||||
doc/interfaces-forward.5 \
|
||||
doc/interfaces-ppp.5 \
|
||||
doc/interfaces-tunnel.5 \
|
||||
doc/interfaces-vrf.5 \
|
||||
doc/interfaces-vxlan.5 \
|
||||
doc/interfaces-wifi.5 \
|
||||
doc/interfaces-wireguard.5
|
||||
|
||||
MANPAGES_7 = \
|
||||
doc/ifupdown-executor.7
|
||||
|
||||
MANPAGES_8 = \
|
||||
doc/ifquery.8 \
|
||||
doc/ifup.8 \
|
||||
doc/ifdown.8 \
|
||||
doc/ifctrstat.8 \
|
||||
doc/ifparse.8
|
||||
|
||||
MANPAGES = ${MANPAGES_5} ${MANPAGES_7} ${MANPAGES_8}
|
||||
|
||||
docs: ${MANPAGES}
|
||||
|
||||
install_docs: docs
|
||||
for i in ${MANPAGES_5}; do \
|
||||
target=$$(basename $$i); \
|
||||
install -D -m644 $$i ${DESTDIR}/usr/share/man/man5/$$target; \
|
||||
done
|
||||
for i in ${MANPAGES_7}; do \
|
||||
target=$$(basename $$i); \
|
||||
install -D -m644 $$i ${DESTDIR}/usr/share/man/man7/$$target; \
|
||||
done
|
||||
for i in ${MANPAGES_8}; do \
|
||||
target=$$(basename $$i); \
|
||||
install -D -m644 $$i ${DESTDIR}/usr/share/man/man8/$$target; \
|
||||
done
|
||||
|
||||
.SUFFIXES: .scd .1 .2 .3 .4 .5 .6 .7 .8
|
||||
|
||||
DIST_NAME = ${PACKAGE_NAME}-${PACKAGE_VERSION}
|
||||
DIST_TARBALL = ${DIST_NAME}.tar.xz
|
||||
|
||||
distcheck: check dist
|
||||
dist: ${DIST_TARBALL}
|
||||
${DIST_TARBALL}:
|
||||
git archive --format=tar --prefix=${DIST_NAME}/ -o ${DIST_NAME}.tar ${DIST_NAME}
|
||||
xz ${DIST_NAME}.tar
|
||||
|
|
|
|||
49
README.md
49
README.md
|
|
@ -1,18 +1,49 @@
|
|||
# ifupdown-ng
|
||||
|
||||
This package is a work in progress implementation of the ifupdown suite. It is
|
||||
intended to be largely compatible with ifupdown and ifupdown2, with some caveats:
|
||||
ifupdown-ng is a network device manager that is largely compatible with Debian
|
||||
ifupdown, BusyBox ifupdown and Cumulus Networks' ifupdown2.
|
||||
|
||||
For more information read the [admin guide](doc/ADMIN-GUIDE.md).
|
||||
|
||||
## Dependency Resolution
|
||||
|
||||

|
||||
|
||||
ifupdown-ng uses a dependency resolver to determine interface bring-up order
|
||||
in a deterministic way.
|
||||
|
||||
This is accomplished through a combination of manual hinting using the `requires`
|
||||
keyword and dependency learning using native executors.
|
||||
|
||||
For compatibility with some legacy ifupdown executors, we also provide the
|
||||
`requires` keyword under other environment variables in some cases.
|
||||
|
||||
## Caveats
|
||||
|
||||
* ifupdown2 python plugins are not supported at this time. An executor could be
|
||||
written to handle them.
|
||||
|
||||
* ifupdown-ng uses a SAT solver to determine interface bring-up order, like
|
||||
ifupdown2. However, relationships must be explicitly defined instead of
|
||||
inferred by plugins in ifupdown2. This simplifies the executors and ensures
|
||||
consistent behaviour across executors.
|
||||
|
||||
* ifupdown-ng retains compatibility with /etc/network/if-X.d scripts, but will
|
||||
prefer using executors in /usr/libexec/ifupdown-ng where appropriate.
|
||||
|
||||
This package is planned to replace BusyBox ifupdown in Alpine at some point in
|
||||
the future.
|
||||
## Building
|
||||
|
||||
On musl systems, simply do `make` and `make install` to build and install.
|
||||
|
||||
On glibc systems, you must install `libbsd-dev` or equivalent and additionally define `LIBBSD_CFLAGS` and `LIBBSD_LIBS`:
|
||||
|
||||
# instal packages
|
||||
apt install build-essential libbsd0 libbsd-dev
|
||||
|
||||
# build ifupdown-ng
|
||||
make LIBBSD_CFLAGS="$(pkg-config --cflags libbsd-overlay)" LIBBSD_LIBS="$(pkg-config --cflags --libs libbsd-overlay)"
|
||||
make install
|
||||
|
||||
To run the tests, do `make check`. Running the checks requires `kyua` (`apk add kyua` / `apt install kyua`).
|
||||
|
||||
To build the documentation, do `make docs` and `make install_docs`. Building
|
||||
the documentation requires scdoc (`apk add scdoc` / `apt install scdoc`).
|
||||
|
||||
## Discussion
|
||||
|
||||
Discuss ifupdown-ng on IRC: irc.oftc.net #ifupdown-ng
|
||||
|
|
|
|||
97
cmd/ifctrstat-linux.c
Normal file
97
cmd/ifctrstat-linux.c
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
* cmd/ifctrstat-linux.c
|
||||
* Purpose: Implement ifctrstat system-specific routines for Linux
|
||||
*
|
||||
* Copyright (c) 2020 Adélie Software in the Public Benefit, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* This software is provided 'as is' and without any warranty, express or
|
||||
* implied. In no event shall the authors be liable for any damages arising
|
||||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "cmd/multicall.h"
|
||||
#include "cmd/ifctrstat-linux.h"
|
||||
|
||||
struct counter_desc {
|
||||
const char *name;
|
||||
const void *data;
|
||||
} avail_counters[] = {
|
||||
{"rx.discard", "rx_dropped"},
|
||||
{"rx.errors", "rx_errors"},
|
||||
{"rx.octets", "rx_bytes"},
|
||||
{"rx.packets", "rx_packets"},
|
||||
{"tx.discard", "tx_dropped"},
|
||||
{"tx.errors", "tx_errors"},
|
||||
{"tx.octets", "tx_bytes"},
|
||||
{"tx.packets", "tx_packets"}
|
||||
};
|
||||
|
||||
size_t avail_counters_count = ARRAY_SIZE(avail_counters);
|
||||
|
||||
static int
|
||||
counter_compare(const void *key, const void *candidate)
|
||||
{
|
||||
return strcasecmp((const char *)key, ((struct counter_desc *)candidate)->name);
|
||||
}
|
||||
|
||||
const char *
|
||||
read_counter(const char *interface, const char *counter)
|
||||
{
|
||||
FILE *fp;
|
||||
const char *path;
|
||||
char full_path[PATH_MAX];
|
||||
char buffer[1024];
|
||||
size_t in_count;
|
||||
struct counter_desc *ctrdata;
|
||||
|
||||
errno = 0;
|
||||
|
||||
ctrdata = bsearch(counter, avail_counters, avail_counters_count, sizeof(struct counter_desc), counter_compare);
|
||||
if (ctrdata) {
|
||||
path = (const char *)ctrdata->data;
|
||||
} else {
|
||||
errno = ENOSYS;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (snprintf(full_path, PATH_MAX, "/sys/class/net/%s/statistics/%s", interface, path) > PATH_MAX)
|
||||
{
|
||||
errno = ENOMEM;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fp = fopen(full_path, "r");
|
||||
if (!fp)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
in_count = fread(buffer, 1, sizeof(buffer), fp);
|
||||
|
||||
if (in_count == sizeof(buffer))
|
||||
{
|
||||
errno = ENOMEM;
|
||||
fclose(fp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (ferror(fp))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
/* take away the \n, we add our own */
|
||||
buffer[in_count - 1] = '\0';
|
||||
|
||||
return strdup(buffer);
|
||||
}
|
||||
22
cmd/ifctrstat-linux.h
Normal file
22
cmd/ifctrstat-linux.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* cmd/ifctrstat-linux.c
|
||||
* Purpose: Implement ifctrstat system-specific routines for Linux
|
||||
*
|
||||
* Copyright (c) 2021 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* This software is provided 'as is' and without any warranty, express or
|
||||
* implied. In no event shall the authors be liable for any damages arising
|
||||
* from the use of this software.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef IFUPDOWN_IFCTRSTAT_LINUX__H__GUARD
|
||||
#define IFUPDOWN_IFCTRSTAT_LINUX__H__GUARD
|
||||
|
||||
extern const char * read_counter(const char *interface, const char *counter);
|
||||
|
||||
#endif
|
||||
159
cmd/ifctrstat.c
Normal file
159
cmd/ifctrstat.c
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* cmd/ifctrstat.c
|
||||
* Purpose: Display statistics about interfaces on the system
|
||||
*
|
||||
* Copyright (c) 2020 Adélie Software in the Public Benefit, Inc.
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* This software is provided 'as is' and without any warranty, express or
|
||||
* implied. In no event shall the authors be liable for any damages arising
|
||||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "libifupdown/libifupdown.h"
|
||||
#include "cmd/multicall.h"
|
||||
#include "cmd/ifctrstat-linux.h"
|
||||
|
||||
extern struct counter_desc { const char *name; const void *data; } avail_counters[];
|
||||
extern int avail_counters_count;
|
||||
|
||||
static bool show_label = true;
|
||||
|
||||
static bool
|
||||
counter_is_valid(const char *candidate)
|
||||
{
|
||||
for (int i = 0; i < avail_counters_count; i++)
|
||||
{
|
||||
if (strcasecmp(avail_counters[i].name, candidate) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
print_counter(const char *iface, const char *name, const char *value)
|
||||
{
|
||||
(void) iface;
|
||||
|
||||
if (show_label)
|
||||
fprintf(stdout, "%s: %s\n", name, value);
|
||||
else
|
||||
fprintf(stdout, "%s\n", value);
|
||||
}
|
||||
|
||||
static int
|
||||
print_all_counters(const char *iface)
|
||||
{
|
||||
int code = EXIT_SUCCESS;
|
||||
const char *res;
|
||||
|
||||
for (int i = 0; i < avail_counters_count; i++)
|
||||
{
|
||||
const char *ctr = avail_counters[i].name;
|
||||
|
||||
res = read_counter(iface, ctr);
|
||||
if (!res)
|
||||
{
|
||||
fprintf(stderr, "%s: could not determine value of %s for interface %s: %s\n", argv0, ctr, iface, strerror(errno));
|
||||
code = EXIT_FAILURE;
|
||||
}
|
||||
else
|
||||
{
|
||||
print_counter(iface, ctr, res);
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
}
|
||||
|
||||
static void
|
||||
ifctrstat_list_counters(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
|
||||
for (int i = 0; i < avail_counters_count; i++)
|
||||
{
|
||||
fprintf(stdout, "%s\n", avail_counters[i].name);
|
||||
}
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void
|
||||
ifctrstat_set_nolabel(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
show_label = false;
|
||||
}
|
||||
|
||||
static int
|
||||
ifctrstat_main(int argc, char *argv[])
|
||||
{
|
||||
if (optind >= argc)
|
||||
generic_usage(self_applet, EXIT_FAILURE);
|
||||
|
||||
int idx = optind;
|
||||
if (argc - idx == 0)
|
||||
{
|
||||
fprintf(stderr, "%s: interface required\n",
|
||||
argv0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
const char *iface = argv[idx++];
|
||||
|
||||
if (argc - idx == 0)
|
||||
{
|
||||
return print_all_counters(iface);
|
||||
}
|
||||
|
||||
for (; idx < argc; idx++)
|
||||
{
|
||||
if (!counter_is_valid(argv[idx]))
|
||||
{
|
||||
fprintf(stderr, "%s: counter %s is not valid or not available\n", argv0, argv[idx]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
const char *res = read_counter(iface, argv[idx]);
|
||||
if (!res)
|
||||
{
|
||||
fprintf(stderr, "%s: could not determine value of %s for interface %s: %s\n", argv0, argv[idx], iface, strerror(errno));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
print_counter(iface, argv[idx], res);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
static struct if_option local_options[] = {
|
||||
{'L', "list", NULL, "list available counters", false, ifctrstat_list_counters},
|
||||
{'n', "no-label", NULL, "print value without counter label", false, ifctrstat_set_nolabel}
|
||||
};
|
||||
|
||||
static struct if_option_group local_option_group = {
|
||||
.desc = "Program-specific options",
|
||||
.group_size = ARRAY_SIZE(local_options),
|
||||
.group = local_options
|
||||
};
|
||||
|
||||
struct if_applet ifctrstat_applet = {
|
||||
.name = "ifctrstat",
|
||||
.desc = "display statistics about an interface",
|
||||
.main = ifctrstat_main,
|
||||
.usage = "ifctrstat [options] <interface> <counter>\n ifctrstat [options] --list",
|
||||
.manpage = "8 ifctrstat",
|
||||
.groups = { &global_option_group, &local_option_group, NULL }
|
||||
};
|
||||
224
cmd/ifparse.c
Normal file
224
cmd/ifparse.c
Normal 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 },
|
||||
};
|
||||
288
cmd/ifquery.c
288
cmd/ifquery.c
|
|
@ -20,50 +20,18 @@
|
|||
#include <getopt.h>
|
||||
#include "libifupdown/libifupdown.h"
|
||||
#include "cmd/multicall.h"
|
||||
#include "cmd/pretty-print-iface.h"
|
||||
|
||||
void
|
||||
print_interface(struct lif_interface *iface)
|
||||
{
|
||||
if (iface->is_auto)
|
||||
printf("auto %s\n", iface->ifname);
|
||||
|
||||
printf("iface %s\n", iface->ifname);
|
||||
|
||||
struct lif_node *iter;
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
if (!strcmp(entry->key, "address"))
|
||||
{
|
||||
struct lif_address *addr = entry->data;
|
||||
char addr_buf[512];
|
||||
|
||||
if (!lif_address_unparse(addr, addr_buf, sizeof addr_buf, true))
|
||||
{
|
||||
printf(" # warning: failed to unparse address\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
printf(" %s %s\n", entry->key, addr_buf);
|
||||
}
|
||||
else
|
||||
printf(" %s %s\n", entry->key, (const char *) entry->data);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
print_interface_dot(struct lif_dict *collection, struct lif_interface *iface, struct lif_interface *parent)
|
||||
{
|
||||
if (iface->is_up)
|
||||
if (!lif_lifecycle_query_dependents(&exec_opts, iface, iface->ifname))
|
||||
return;
|
||||
|
||||
if (parent != NULL)
|
||||
printf("\"%s\" -> ", parent->ifname);
|
||||
printf("\"%s (%zu)\" -> ", parent->ifname, parent->rdepends_count);
|
||||
|
||||
printf("\"%s\"", iface->ifname);
|
||||
printf("\"%s (%zu)\"", iface->ifname, iface->rdepends_count);
|
||||
|
||||
printf("\n");
|
||||
|
||||
|
|
@ -80,51 +48,51 @@ print_interface_dot(struct lif_dict *collection, struct lif_interface *iface, st
|
|||
{
|
||||
struct lif_interface *child_if = lif_interface_collection_find(collection, tokenp);
|
||||
|
||||
if (child_if->is_pending)
|
||||
continue;
|
||||
|
||||
child_if->is_pending = true;
|
||||
print_interface_dot(collection, child_if, iface);
|
||||
child_if->is_up = true;
|
||||
child_if->is_pending = false;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ifquery_usage(void)
|
||||
static void
|
||||
print_interface_property(struct lif_interface *iface, const char *property)
|
||||
{
|
||||
fprintf(stderr, "usage: ifquery [options] <interfaces>\n");
|
||||
fprintf(stderr, " ifquery [options] --list\n");
|
||||
struct lif_node *iter;
|
||||
bool printing_address = !strcmp(property, "address");
|
||||
|
||||
fprintf(stderr, "\nOptions:\n");
|
||||
fprintf(stderr, " -h, --help this help\n");
|
||||
fprintf(stderr, " -V, --version show this program's version\n");
|
||||
fprintf(stderr, " -i, --interfaces FILE use FILE for interface definitions\n");
|
||||
fprintf(stderr, " -L, --list list matching interfaces\n");
|
||||
fprintf(stderr, " -a, --auto only match against interfaces hinted as 'auto'\n");
|
||||
fprintf(stderr, " -I, --include PATTERN only match against interfaces matching PATTERN\n");
|
||||
fprintf(stderr, " -X, --exclude PATTERN never match against interfaces matching PATTERN\n");
|
||||
fprintf(stderr, " -P, --pretty-print pretty print the interfaces instead of just listing\n");
|
||||
fprintf(stderr, " -S, --state-file FILE use FILE for state\n");
|
||||
fprintf(stderr, " -s, --state show configured state\n");
|
||||
fprintf(stderr, " -D, --dot generate a dependency graph\n");
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
exit(1);
|
||||
if (strcmp(entry->key, property))
|
||||
continue;
|
||||
|
||||
if (printing_address)
|
||||
{
|
||||
char addr_buf[512];
|
||||
if (!lif_address_format_cidr(iface, entry, addr_buf, sizeof(addr_buf)))
|
||||
continue;
|
||||
|
||||
printf("%s\n", addr_buf);
|
||||
}
|
||||
else
|
||||
printf("%s\n", (const char *) entry->data);
|
||||
}
|
||||
}
|
||||
|
||||
struct match_options {
|
||||
bool is_auto;
|
||||
char *exclude_pattern;
|
||||
char *include_pattern;
|
||||
bool pretty_print;
|
||||
bool dot;
|
||||
};
|
||||
|
||||
void
|
||||
static void
|
||||
list_interfaces(struct lif_dict *collection, struct match_options *opts)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
||||
if (opts->dot)
|
||||
{
|
||||
printf("digraph interfaces {\n");
|
||||
printf("edge [color=blue fontname=Sans fontsize=10]\n");
|
||||
printf("node [fontname=Sans fontsize=10]\n");
|
||||
printf("digraph interfaces {\n"
|
||||
"edge [color=blue fontname=Sans fontsize=10]\n"
|
||||
"node [fontname=Sans fontsize=10]\n");
|
||||
}
|
||||
|
||||
LIF_DICT_FOREACH(iter, collection)
|
||||
|
|
@ -144,7 +112,7 @@ list_interfaces(struct lif_dict *collection, struct match_options *opts)
|
|||
continue;
|
||||
|
||||
if (opts->pretty_print)
|
||||
print_interface(iface);
|
||||
prettyprint_interface_eni(iface);
|
||||
else if (opts->dot)
|
||||
print_interface_dot(collection, iface, NULL);
|
||||
else
|
||||
|
|
@ -155,7 +123,10 @@ list_interfaces(struct lif_dict *collection, struct match_options *opts)
|
|||
printf("}\n");
|
||||
}
|
||||
|
||||
void
|
||||
static bool listing = false, listing_stat = false, listing_running = false;
|
||||
static bool allow_undefined = false;
|
||||
|
||||
static void
|
||||
list_state(struct lif_dict *state, struct match_options *opts)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
|
@ -172,106 +143,132 @@ list_state(struct lif_dict *state, struct match_options *opts)
|
|||
fnmatch(opts->include_pattern, entry->key, 0))
|
||||
continue;
|
||||
|
||||
printf("%s=%s\n", entry->key, (const char *) entry->data);
|
||||
struct lif_state_record *rec = entry->data;
|
||||
|
||||
if (listing_running)
|
||||
printf("%s\n", entry->key);
|
||||
else
|
||||
printf("%s=%s %zu%s\n", entry->key, rec->mapped_if, rec->refcount,
|
||||
rec->is_explicit ? " explicit" : "");
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
static void
|
||||
set_listing(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
listing = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_show_state(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
listing_stat = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_show_running(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
listing_running = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_pretty_print(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
match_opts.pretty_print = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_output_dot(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
match_opts.dot = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_property(const char *opt_arg)
|
||||
{
|
||||
match_opts.property = opt_arg;
|
||||
}
|
||||
|
||||
static void
|
||||
set_allow_undefined(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
allow_undefined = true;
|
||||
}
|
||||
|
||||
static struct if_option local_options[] = {
|
||||
{'r', "running", NULL, "show configured (running) interfaces", false, set_show_running},
|
||||
{'s', "state", NULL, "show configured state", false, set_show_state},
|
||||
{'p', "property", "property PROPERTY", "print values of properties matching PROPERTY", true, set_property},
|
||||
{'D', "dot", NULL, "generate a dependency graph", false, set_output_dot},
|
||||
{'L', "list", NULL, "list matching interfaces", false, set_listing},
|
||||
{'P', "pretty-print", NULL, "pretty print the interfaces instead of just listing", false, set_pretty_print},
|
||||
{'U', "allow-undefined", NULL, "allow querying undefined (virtual) interfaces", false, set_allow_undefined},
|
||||
};
|
||||
|
||||
static struct if_option_group local_option_group = {
|
||||
.desc = "Program-specific options",
|
||||
.group_size = ARRAY_SIZE(local_options),
|
||||
.group = local_options
|
||||
};
|
||||
|
||||
static int
|
||||
ifquery_main(int argc, char *argv[])
|
||||
{
|
||||
struct lif_dict state = {};
|
||||
struct lif_dict collection = {};
|
||||
struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{"interfaces", required_argument, 0, 'i'},
|
||||
{"list", no_argument, 0, 'L'},
|
||||
{"auto", no_argument, 0, 'a'},
|
||||
{"include", required_argument, 0, 'I'},
|
||||
{"exclude", required_argument, 0, 'X'},
|
||||
{"pretty-print", no_argument, 0, 'P'},
|
||||
{"state-file", required_argument, 0, 'S'},
|
||||
{"state", no_argument, 0, 's'},
|
||||
{"dot", no_argument, 0, 'D'},
|
||||
{NULL, 0, 0, 0}
|
||||
struct lif_interface_file_parse_state parse_state = {
|
||||
.collection = &collection,
|
||||
};
|
||||
struct match_options match_opts = {};
|
||||
bool listing = false, listing_stat = false;
|
||||
char *interfaces_file = INTERFACES_FILE;
|
||||
char *state_file = STATE_FILE;
|
||||
|
||||
for (;;)
|
||||
lif_interface_collection_init(&collection);
|
||||
|
||||
if (!lif_state_read_path(&state, exec_opts.state_file))
|
||||
{
|
||||
int c = getopt_long(argc, argv, "hVi:LaI:X:PS:sD", long_options, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
ifquery_usage();
|
||||
break;
|
||||
case 'V':
|
||||
lif_common_version();
|
||||
break;
|
||||
case 'i':
|
||||
interfaces_file = optarg;
|
||||
break;
|
||||
case 'L':
|
||||
listing = true;
|
||||
break;
|
||||
case 'a':
|
||||
match_opts.is_auto = true;
|
||||
break;
|
||||
case 'I':
|
||||
match_opts.include_pattern = optarg;
|
||||
break;
|
||||
case 'X':
|
||||
match_opts.exclude_pattern = optarg;
|
||||
break;
|
||||
case 'P':
|
||||
match_opts.pretty_print = true;
|
||||
break;
|
||||
case 'S':
|
||||
state_file = optarg;
|
||||
break;
|
||||
case 's':
|
||||
listing_stat = true;
|
||||
break;
|
||||
case 'D':
|
||||
match_opts.dot = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lif_state_read_path(&state, state_file))
|
||||
{
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, state_file);
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.state_file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!lif_interface_file_parse(&collection, interfaces_file))
|
||||
if (!lif_interface_file_parse(&parse_state, exec_opts.interfaces_file))
|
||||
{
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, interfaces_file);
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.interfaces_file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (match_opts.property == NULL && lif_lifecycle_count_rdepends(&exec_opts, &collection) == -1)
|
||||
{
|
||||
fprintf(stderr, "%s: could not validate dependency tree\n", argv0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!lif_compat_apply(&collection))
|
||||
{
|
||||
fprintf(stderr, "%s: failed to apply compatibility glue\n", argv0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* --list --state is not allowed */
|
||||
if (listing && listing_stat)
|
||||
ifquery_usage();
|
||||
if (listing && (listing_stat || listing_running))
|
||||
generic_usage(self_applet, EXIT_FAILURE);
|
||||
|
||||
if (listing)
|
||||
{
|
||||
list_interfaces(&collection, &match_opts);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
else if (listing_stat)
|
||||
else if (listing_stat || listing_running)
|
||||
{
|
||||
list_state(&state, &match_opts);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
if (optind >= argc)
|
||||
ifquery_usage();
|
||||
generic_usage(self_applet, EXIT_FAILURE);
|
||||
|
||||
int idx = optind;
|
||||
for (; idx < argc; idx++)
|
||||
|
|
@ -284,6 +281,9 @@ ifquery_main(int argc, char *argv[])
|
|||
|
||||
if (entry != NULL)
|
||||
iface = entry->data;
|
||||
|
||||
if (entry == NULL && allow_undefined)
|
||||
iface = lif_interface_collection_find(&collection, argv[idx]);
|
||||
}
|
||||
|
||||
if (iface == NULL)
|
||||
|
|
@ -292,7 +292,10 @@ ifquery_main(int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
print_interface(iface);
|
||||
if (match_opts.property != NULL)
|
||||
print_interface_property(iface, match_opts.property);
|
||||
else
|
||||
prettyprint_interface_eni(iface);
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
|
|
@ -300,6 +303,9 @@ ifquery_main(int argc, char *argv[])
|
|||
|
||||
struct if_applet ifquery_applet = {
|
||||
.name = "ifquery",
|
||||
.desc = "query interface configuration",
|
||||
.main = ifquery_main,
|
||||
.usage = ifquery_usage
|
||||
.usage = "ifquery [options] <interfaces>\n ifquery [options] --list",
|
||||
.manpage = "8 ifquery",
|
||||
.groups = { &global_option_group, &match_option_group, &exec_option_group, &local_option_group },
|
||||
};
|
||||
|
|
|
|||
308
cmd/ifupdown.c
308
cmd/ifupdown.c
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: bring interfaces up or down
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -18,38 +19,15 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "libifupdown/libifupdown.h"
|
||||
#include "cmd/multicall.h"
|
||||
|
||||
struct match_options {
|
||||
bool is_auto;
|
||||
char *exclude_pattern;
|
||||
char *include_pattern;
|
||||
};
|
||||
|
||||
static bool up;
|
||||
static struct lif_execute_opts exec_opts = {};
|
||||
|
||||
void
|
||||
ifupdown_usage(void)
|
||||
{
|
||||
fprintf(stderr, "usage: %s [options] <interfaces>\n", argv0);
|
||||
|
||||
fprintf(stderr, "\nOptions:\n");
|
||||
fprintf(stderr, " -h, --help this help\n");
|
||||
fprintf(stderr, " -V, --version show this program's version\n");
|
||||
fprintf(stderr, " -i, --interfaces FILE use FILE for interface definitions\n");
|
||||
fprintf(stderr, " -S, --state-file FILE use FILE for state\n");
|
||||
fprintf(stderr, " -a, --auto only match against interfaces hinted as 'auto'\n");
|
||||
fprintf(stderr, " -I, --include PATTERN only match against interfaces matching PATTERN\n");
|
||||
fprintf(stderr, " -X, --exclude PATTERN never match against interfaces matching PATTERN\n");
|
||||
fprintf(stderr, " -n, --no-act do not actually run any commands\n");
|
||||
fprintf(stderr, " -v, --verbose show what commands are being run\n");
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
is_ifdown()
|
||||
{
|
||||
if (strstr(argv0, "ifdown") != NULL)
|
||||
|
|
@ -58,9 +36,135 @@ is_ifdown()
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
change_interface(struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *ifname)
|
||||
static int
|
||||
acquire_state_lock(const char *state_path, const char *lifname)
|
||||
{
|
||||
if (exec_opts.mock || exec_opts.no_lock)
|
||||
return -1;
|
||||
|
||||
char lockpath[4096] = {};
|
||||
|
||||
snprintf(lockpath, sizeof lockpath, "%s.%s.lock", state_path, lifname);
|
||||
|
||||
int fd = open(lockpath, O_CREAT | O_WRONLY | O_TRUNC, 0600);
|
||||
if (fd < 0)
|
||||
{
|
||||
if (exec_opts.verbose)
|
||||
fprintf(stderr, "%s: while opening lockfile %s: %s\n", argv0, lockpath, strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
int flags = fcntl(fd, F_GETFD);
|
||||
if (flags < 0)
|
||||
{
|
||||
close(fd);
|
||||
|
||||
if (exec_opts.verbose)
|
||||
fprintf(stderr, "%s: while getting flags for lockfile: %s\n", argv0, strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
flags |= FD_CLOEXEC;
|
||||
if (fcntl(fd, F_SETFD, flags) == -1)
|
||||
{
|
||||
close(fd);
|
||||
|
||||
if (exec_opts.verbose)
|
||||
fprintf(stderr, "%s: while setting lockfile close-on-exec: %s\n", argv0, strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
struct flock fl = {
|
||||
.l_type = F_WRLCK,
|
||||
.l_whence = SEEK_SET
|
||||
};
|
||||
|
||||
if (exec_opts.verbose)
|
||||
fprintf(stderr, "%s: acquiring lock on %s\n", argv0, lockpath);
|
||||
|
||||
if (fcntl(fd, F_SETLK, &fl) == -1)
|
||||
{
|
||||
close(fd);
|
||||
|
||||
if (exec_opts.verbose)
|
||||
fprintf(stderr, "%s: while locking lockfile: %s\n", argv0, strerror(errno));
|
||||
return -2;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static bool
|
||||
skip_interface(struct lif_interface *iface, const char *ifname, struct lif_dict *state, bool update_state)
|
||||
{
|
||||
if (iface->is_template)
|
||||
{
|
||||
fprintf(stderr, "%s: cannot change state on %s (template interface)\n", argv0, ifname);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iface->has_config_error)
|
||||
{
|
||||
if (exec_opts.force)
|
||||
{
|
||||
fprintf(stderr, "%s: (de)configuring interface %s despite config errors\n", argv0, ifname);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s: skipping interface %s due to config errors\n", argv0, ifname);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (exec_opts.force)
|
||||
return false;
|
||||
|
||||
if (up && iface->refcount > 0)
|
||||
{
|
||||
if (exec_opts.verbose)
|
||||
fprintf(stderr, "%s: skipping %sinterface %s (already configured), use --force to force configuration\n",
|
||||
argv0, iface->is_auto ? "auto " : "", ifname);
|
||||
|
||||
if (update_state)
|
||||
{
|
||||
iface->is_explicit = true;
|
||||
lif_state_upsert(state, ifname, iface);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!up && iface->refcount == 0)
|
||||
{
|
||||
if (exec_opts.verbose)
|
||||
fprintf(stderr, "%s: skipping %sinterface %s (already deconfigured), use --force to force deconfiguration\n",
|
||||
argv0, iface->is_auto ? "auto " : "", ifname);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
change_interface(struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *ifname, bool update_state)
|
||||
{
|
||||
int lockfd = acquire_state_lock(exec_opts.state_file, ifname);
|
||||
|
||||
if (lockfd == -2)
|
||||
{
|
||||
fprintf(stderr, "%s: could not acquire exclusive lock for %s: %s\n", argv0, ifname, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (skip_interface(iface, ifname, state, update_state))
|
||||
{
|
||||
if (lockfd != -1)
|
||||
close(lockfd);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (exec_opts.verbose)
|
||||
{
|
||||
fprintf(stderr, "%s: changing state of interface %s to '%s'\n",
|
||||
|
|
@ -71,13 +175,26 @@ change_interface(struct lif_interface *iface, struct lif_dict *collection, struc
|
|||
{
|
||||
fprintf(stderr, "%s: failed to change interface %s state to '%s'\n",
|
||||
argv0, ifname, up ? "up" : "down");
|
||||
|
||||
if (lockfd != -1)
|
||||
close(lockfd);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lockfd != -1)
|
||||
close(lockfd);
|
||||
|
||||
if (up && update_state)
|
||||
{
|
||||
iface->is_explicit = true;
|
||||
lif_state_upsert(state, ifname, iface);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
static bool
|
||||
change_auto_interfaces(struct lif_dict *collection, struct lif_dict *state, struct match_options *opts)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
|
@ -98,83 +215,68 @@ change_auto_interfaces(struct lif_dict *collection, struct lif_dict *state, stru
|
|||
fnmatch(opts->include_pattern, iface->ifname, 0))
|
||||
continue;
|
||||
|
||||
if (!change_interface(iface, collection, state, iface->ifname))
|
||||
if (!change_interface(iface, collection, state, iface->ifname, false))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
static int
|
||||
update_state_file_and_exit(int rc, struct lif_dict *state)
|
||||
{
|
||||
if (exec_opts.mock)
|
||||
{
|
||||
exit(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (!lif_state_write_path(state, exec_opts.state_file))
|
||||
{
|
||||
fprintf(stderr, "%s: could not update %s\n", argv0, exec_opts.state_file);
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
exit(rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
ifupdown_main(int argc, char *argv[])
|
||||
{
|
||||
up = !is_ifdown();
|
||||
|
||||
struct lif_dict state = {};
|
||||
struct lif_dict collection = {};
|
||||
struct option long_options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"version", no_argument, 0, 'V'},
|
||||
{"interfaces", required_argument, 0, 'i'},
|
||||
{"auto", no_argument, 0, 'a'},
|
||||
{"include", required_argument, 0, 'I'},
|
||||
{"exclude", required_argument, 0, 'X'},
|
||||
{"state-file", required_argument, 0, 'S'},
|
||||
{"no-act", no_argument, 0, 'n'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{NULL, 0, 0, 0}
|
||||
struct lif_interface_file_parse_state parse_state = {
|
||||
.collection = &collection,
|
||||
};
|
||||
struct match_options match_opts = {};
|
||||
char *interfaces_file = INTERFACES_FILE;
|
||||
char *state_file = STATE_FILE;
|
||||
|
||||
for (;;)
|
||||
lif_interface_collection_init(&collection);
|
||||
|
||||
if (!lif_state_read_path(&state, exec_opts.state_file))
|
||||
{
|
||||
int c = getopt_long(argc, argv, "hVi:aI:X:S:nv", long_options, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case 'h':
|
||||
ifupdown_usage();
|
||||
break;
|
||||
case 'V':
|
||||
lif_common_version();
|
||||
break;
|
||||
case 'i':
|
||||
interfaces_file = optarg;
|
||||
break;
|
||||
case 'a':
|
||||
match_opts.is_auto = true;
|
||||
break;
|
||||
case 'I':
|
||||
match_opts.include_pattern = optarg;
|
||||
break;
|
||||
case 'X':
|
||||
match_opts.exclude_pattern = optarg;
|
||||
break;
|
||||
case 'S':
|
||||
state_file = optarg;
|
||||
break;
|
||||
case 'n':
|
||||
exec_opts.mock = true;
|
||||
exec_opts.verbose = true;
|
||||
break;
|
||||
case 'v':
|
||||
exec_opts.verbose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!lif_state_read_path(&state, state_file))
|
||||
{
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, state_file);
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.state_file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (!lif_interface_file_parse(&collection, interfaces_file))
|
||||
if (!lif_interface_file_parse(&parse_state, exec_opts.interfaces_file))
|
||||
{
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, interfaces_file);
|
||||
fprintf(stderr, "%s: could not parse %s\n", argv0, exec_opts.interfaces_file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (lif_lifecycle_count_rdepends(&exec_opts, &collection) == -1)
|
||||
{
|
||||
fprintf(stderr, "%s: could not validate dependency tree\n", argv0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if(!lif_compat_apply(&collection))
|
||||
{
|
||||
fprintf(stderr, "%s: failed to apply compatibility glue\n", argv0);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
|
|
@ -187,12 +289,12 @@ ifupdown_main(int argc, char *argv[])
|
|||
if (match_opts.is_auto)
|
||||
{
|
||||
if (!change_auto_interfaces(&collection, &state, &match_opts))
|
||||
return EXIT_FAILURE;
|
||||
return update_state_file_and_exit(EXIT_FAILURE, &state);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
return update_state_file_and_exit(EXIT_SUCCESS, &state);
|
||||
}
|
||||
else if (optind >= argc)
|
||||
ifupdown_usage();
|
||||
generic_usage(self_applet, EXIT_FAILURE);
|
||||
|
||||
int idx = optind;
|
||||
for (; idx < argc; idx++)
|
||||
|
|
@ -218,33 +320,33 @@ ifupdown_main(int argc, char *argv[])
|
|||
if (entry == NULL)
|
||||
{
|
||||
fprintf(stderr, "%s: unknown interface %s\n", argv0, argv[idx]);
|
||||
return EXIT_FAILURE;
|
||||
return update_state_file_and_exit(EXIT_FAILURE, &state);
|
||||
}
|
||||
|
||||
iface = entry->data;
|
||||
}
|
||||
|
||||
if (!change_interface(iface, &collection, &state, ifname))
|
||||
return EXIT_FAILURE;
|
||||
if (!change_interface(iface, &collection, &state, ifname, true))
|
||||
return update_state_file_and_exit(EXIT_FAILURE, &state);
|
||||
}
|
||||
|
||||
if (!exec_opts.mock && !lif_state_write_path(&state, state_file))
|
||||
{
|
||||
fprintf(stderr, "%s: could not update %s\n", argv0, state_file);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
return update_state_file_and_exit(EXIT_SUCCESS, &state);
|
||||
}
|
||||
|
||||
struct if_applet ifup_applet = {
|
||||
.name = "ifup",
|
||||
.desc = "bring interfaces up",
|
||||
.main = ifupdown_main,
|
||||
.usage = ifupdown_usage
|
||||
.usage = "ifup [options] <interfaces>",
|
||||
.manpage = "8 ifup",
|
||||
.groups = { &global_option_group, &match_option_group, &exec_option_group, },
|
||||
};
|
||||
|
||||
struct if_applet ifdown_applet = {
|
||||
.name = "ifdown",
|
||||
.desc = "take interfaces down",
|
||||
.main = ifupdown_main,
|
||||
.usage = ifupdown_usage
|
||||
.usage = "ifdown [options] <interfaces>",
|
||||
.manpage = "8 ifdown",
|
||||
.groups = { &global_option_group, &match_option_group, &exec_option_group, },
|
||||
};
|
||||
|
|
|
|||
103
cmd/multicall-exec-options.c
Normal file
103
cmd/multicall-exec-options.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* cmd/multicall-exec-options.c
|
||||
* Purpose: multi-call binary frontend -- option handling
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* This software is provided 'as is' and without any warranty, express or
|
||||
* implied. In no event shall the authors be liable for any damages arising
|
||||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include "cmd/multicall.h"
|
||||
|
||||
#define DEFAULT_TIMEOUT 300
|
||||
|
||||
struct lif_execute_opts exec_opts = {
|
||||
.interfaces_file = INTERFACES_FILE,
|
||||
.executor_path = EXECUTOR_PATH,
|
||||
.state_file = STATE_FILE,
|
||||
.timeout = DEFAULT_TIMEOUT,
|
||||
};
|
||||
|
||||
static void
|
||||
set_interfaces_file(const char *opt_arg)
|
||||
{
|
||||
exec_opts.interfaces_file = opt_arg;
|
||||
}
|
||||
|
||||
static void
|
||||
set_state_file(const char *opt_arg)
|
||||
{
|
||||
exec_opts.state_file = opt_arg;
|
||||
}
|
||||
|
||||
static void
|
||||
set_no_act(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
exec_opts.mock = true;
|
||||
exec_opts.verbose = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_verbose(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
exec_opts.verbose = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_executor_path(const char *opt_arg)
|
||||
{
|
||||
exec_opts.executor_path = opt_arg;
|
||||
}
|
||||
|
||||
static void
|
||||
set_no_lock(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
exec_opts.no_lock = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_force(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
exec_opts.force = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_timeout(const char *opt_arg)
|
||||
{
|
||||
exec_opts.timeout = atoi(opt_arg);
|
||||
if (exec_opts.timeout < 0)
|
||||
exec_opts.timeout = DEFAULT_TIMEOUT;
|
||||
}
|
||||
|
||||
static struct if_option exec_options[] = {
|
||||
{'f', "force", NULL, "force (de)configuration", false, set_force},
|
||||
{'i', "interfaces", "interfaces FILE", "use FILE for interface definitions", true, set_interfaces_file},
|
||||
{'l', "no-lock", NULL, "do not use a lockfile to serialize state changes", false, set_no_lock},
|
||||
{'n', "no-act", NULL, "do not actually run any commands", false, set_no_act},
|
||||
{'v', "verbose", NULL, "show what commands are being run", false, set_verbose},
|
||||
{'E', "executor-path", "executor-path PATH", "use PATH for executor directory", true, set_executor_path},
|
||||
{'S', "state-file", "state-file FILE", "use FILE for state", true, set_state_file},
|
||||
{'T', "timeout", "timeout TIMEOUT", "wait TIMEOUT seconds for executors to complete", true, set_timeout},
|
||||
};
|
||||
|
||||
struct if_option_group exec_option_group = {
|
||||
.desc = "Execution",
|
||||
.group_size = ARRAY_SIZE(exec_options),
|
||||
.group = exec_options
|
||||
};
|
||||
55
cmd/multicall-match-options.c
Normal file
55
cmd/multicall-match-options.c
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* cmd/multicall-match-options.c
|
||||
* Purpose: multi-call binary frontend -- option handling
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* This software is provided 'as is' and without any warranty, express or
|
||||
* implied. In no event shall the authors be liable for any damages arising
|
||||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include "cmd/multicall.h"
|
||||
|
||||
struct match_options match_opts = {};
|
||||
|
||||
static void
|
||||
set_auto(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
match_opts.is_auto = true;
|
||||
}
|
||||
|
||||
static void
|
||||
set_include_pattern(const char *opt_arg)
|
||||
{
|
||||
match_opts.include_pattern = opt_arg;
|
||||
}
|
||||
|
||||
static void
|
||||
set_exclude_pattern(const char *opt_arg)
|
||||
{
|
||||
match_opts.exclude_pattern = opt_arg;
|
||||
}
|
||||
|
||||
static struct if_option match_options[] = {
|
||||
{'a', "auto", NULL, "only match against interfaces hinted as 'auto'", false, set_auto},
|
||||
{'I', "include", "include PATTERN", "only match against interfaces matching PATTERN", true, set_include_pattern},
|
||||
{'X', "exclude", "exclude PATTERN", "never match against interfaces matching PATTERN", true, set_exclude_pattern},
|
||||
};
|
||||
|
||||
struct if_option_group match_option_group = {
|
||||
.desc = "Matching interfaces",
|
||||
.group_size = ARRAY_SIZE(match_options),
|
||||
.group = match_options
|
||||
};
|
||||
162
cmd/multicall-options.c
Normal file
162
cmd/multicall-options.c
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
/*
|
||||
* cmd/multicall-options.c
|
||||
* Purpose: multi-call binary frontend -- option handling
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* This software is provided 'as is' and without any warranty, express or
|
||||
* implied. In no event shall the authors be liable for any damages arising
|
||||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include "cmd/multicall.h"
|
||||
|
||||
extern const struct if_applet *self_applet;
|
||||
|
||||
void
|
||||
generic_usage(const struct if_applet *applet, int result)
|
||||
{
|
||||
fprintf(stderr, "%s", applet->name);
|
||||
if (applet->desc != NULL)
|
||||
fprintf(stderr, " - %s", applet->desc);
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
if (applet->usage != NULL)
|
||||
fprintf(stderr, "\nUsage:\n %s\n", applet->usage);
|
||||
|
||||
size_t iter;
|
||||
for (iter = 0; applet->groups[iter] != NULL; iter++)
|
||||
{
|
||||
const struct if_option_group *group = applet->groups[iter];
|
||||
|
||||
fprintf(stderr, "\n%s:\n", group->desc);
|
||||
|
||||
size_t group_iter;
|
||||
for (group_iter = 0; group_iter < group->group_size; group_iter++)
|
||||
{
|
||||
const struct if_option *opt = &group->group[group_iter];
|
||||
|
||||
fprintf(stderr, " ");
|
||||
|
||||
if (opt->short_opt)
|
||||
fprintf(stderr, "-%c", opt->short_opt);
|
||||
else
|
||||
fprintf(stderr, " ");
|
||||
|
||||
if (opt->long_opt)
|
||||
fprintf(stderr, "%c --%-30s", opt->short_opt ? ',' : ' ',
|
||||
opt->long_opt_desc ? opt->long_opt_desc : opt->long_opt);
|
||||
else
|
||||
fprintf(stderr, "%34s", "");
|
||||
|
||||
fprintf(stderr, "%s\n", opt->desc);
|
||||
}
|
||||
}
|
||||
|
||||
if (applet->manpage != NULL)
|
||||
fprintf(stderr, "\nFor more information: man %s\n", applet->manpage);
|
||||
|
||||
exit(result);
|
||||
}
|
||||
|
||||
static void
|
||||
generic_usage_request(const char *opt_arg)
|
||||
{
|
||||
(void) opt_arg;
|
||||
|
||||
generic_usage(self_applet, EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static struct if_option global_options[] = {
|
||||
{'h', "help", NULL, "this help", false, generic_usage_request},
|
||||
{'V', "version", NULL, "show this program's version", false, (void *) lif_common_version},
|
||||
};
|
||||
|
||||
struct if_option_group global_option_group = {
|
||||
.desc = "Global options",
|
||||
.group_size = ARRAY_SIZE(global_options),
|
||||
.group = global_options
|
||||
};
|
||||
|
||||
const struct if_option *
|
||||
lookup_option(const struct if_applet *applet, int opt)
|
||||
{
|
||||
size_t iter;
|
||||
for (iter = 0; applet->groups[iter] != NULL; iter++)
|
||||
{
|
||||
const struct if_option_group *group = applet->groups[iter];
|
||||
size_t group_iter;
|
||||
|
||||
for (group_iter = 0; group_iter < group->group_size; group_iter++)
|
||||
{
|
||||
const struct if_option *option = &group->group[group_iter];
|
||||
|
||||
if (option->short_opt == opt)
|
||||
return option;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
process_options(const struct if_applet *applet, int argc, char *argv[])
|
||||
{
|
||||
char short_opts[256] = {}, *p = short_opts;
|
||||
struct option long_opts[256] = {};
|
||||
|
||||
size_t iter, long_opt_iter = 0;
|
||||
for (iter = 0; applet->groups[iter] != NULL; iter++)
|
||||
{
|
||||
const struct if_option_group *group = applet->groups[iter];
|
||||
size_t group_iter;
|
||||
|
||||
for (group_iter = 0; group_iter < group->group_size; group_iter++)
|
||||
{
|
||||
const struct if_option *opt = &group->group[group_iter];
|
||||
|
||||
if (opt->short_opt)
|
||||
{
|
||||
*p++ = opt->short_opt;
|
||||
if (opt->require_argument)
|
||||
*p++ = ':';
|
||||
}
|
||||
|
||||
if (opt->long_opt)
|
||||
{
|
||||
/* XXX: handle long-opts without short-opts */
|
||||
long_opts[long_opt_iter] = (struct option) {
|
||||
.name = opt->long_opt,
|
||||
.has_arg = opt->require_argument ? required_argument : no_argument,
|
||||
.val = opt->short_opt
|
||||
};
|
||||
|
||||
long_opt_iter++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int c = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
||||
if (c == -1)
|
||||
break;
|
||||
|
||||
const struct if_option *opt = lookup_option(applet, c);
|
||||
if (opt == NULL)
|
||||
break;
|
||||
|
||||
opt->handle(optarg);
|
||||
}
|
||||
}
|
||||
|
|
@ -13,29 +13,56 @@
|
|||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
#include <libgen.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include "cmd/multicall.h"
|
||||
|
||||
char *argv0;
|
||||
|
||||
#ifdef CONFIG_IFQUERY
|
||||
extern struct if_applet ifquery_applet;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IFUPDOWN
|
||||
extern struct if_applet ifup_applet;
|
||||
extern struct if_applet ifdown_applet;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IFCTRSTAT
|
||||
extern struct if_applet ifctrstat_applet;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IFPARSE
|
||||
extern struct if_applet ifparse_applet;
|
||||
#endif
|
||||
|
||||
struct if_applet ifupdown_applet;
|
||||
const struct if_applet *self_applet = NULL;
|
||||
|
||||
struct if_applet *applet_table[] = {
|
||||
#ifdef CONFIG_IFCTRSTAT
|
||||
&ifctrstat_applet,
|
||||
#endif
|
||||
#ifdef CONFIG_IFUPDOWN
|
||||
&ifdown_applet,
|
||||
#endif
|
||||
#ifdef CONFIG_IFPARSE
|
||||
&ifparse_applet,
|
||||
#endif
|
||||
#ifdef CONFIG_IFQUERY
|
||||
&ifquery_applet,
|
||||
#endif
|
||||
#ifdef CONFIG_IFUPDOWN
|
||||
&ifup_applet,
|
||||
#endif
|
||||
&ifupdown_applet,
|
||||
};
|
||||
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
|
||||
|
||||
int
|
||||
static int
|
||||
applet_cmp(const void *a, const void *b)
|
||||
{
|
||||
const char *key = a;
|
||||
|
|
@ -44,13 +71,17 @@ applet_cmp(const void *a, const void *b)
|
|||
return strcmp(key, applet->name);
|
||||
}
|
||||
|
||||
void multicall_usage(void);
|
||||
void multicall_usage(int status) __attribute__((noreturn));
|
||||
|
||||
struct if_applet ifupdown_applet;
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
argv0 = basename(argv[0]);
|
||||
struct if_applet **app;
|
||||
const struct if_applet **app;
|
||||
|
||||
lif_config_load(CONFIG_FILE);
|
||||
|
||||
app = bsearch(argv0, applet_table,
|
||||
ARRAY_SIZE(applet_table), sizeof(*applet_table),
|
||||
|
|
@ -59,42 +90,48 @@ main(int argc, char *argv[])
|
|||
if (app == NULL)
|
||||
{
|
||||
fprintf(stderr, "%s: applet not found\n", argv0);
|
||||
multicall_usage();
|
||||
multicall_usage(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return (*app)->main(argc, argv);
|
||||
self_applet = *app;
|
||||
|
||||
if (self_applet != &ifupdown_applet)
|
||||
process_options(*app, argc, argv);
|
||||
|
||||
return self_applet->main(argc, argv);
|
||||
}
|
||||
|
||||
int
|
||||
static int
|
||||
multicall_main(int argc, char *argv[])
|
||||
{
|
||||
if (argc < 2)
|
||||
multicall_usage();
|
||||
multicall_usage(EXIT_FAILURE);
|
||||
|
||||
return main(argc - 1, argv + 1);
|
||||
}
|
||||
|
||||
void
|
||||
multicall_usage(void)
|
||||
multicall_usage(int status)
|
||||
{
|
||||
fprintf(stderr, "usage: ifupdown <applet> [options]\n");
|
||||
fprintf(stderr,
|
||||
PACKAGE_NAME " " PACKAGE_VERSION "\n"
|
||||
"usage: ifupdown <applet> [options]\n"
|
||||
"\n"
|
||||
"Built-in applets:\n");
|
||||
|
||||
fprintf(stderr, "\nBuilt-in applets:\n\t");
|
||||
for (size_t i = 0; i < ARRAY_SIZE(applet_table); i++)
|
||||
{
|
||||
if (i != 0)
|
||||
fprintf(stderr, ", ");
|
||||
if (applet_table[i] == &ifupdown_applet)
|
||||
continue;
|
||||
|
||||
fprintf(stderr, "%s", applet_table[i]->name);
|
||||
fprintf(stderr, " %-10s %s\n", applet_table[i]->name, applet_table[i]->desc);
|
||||
}
|
||||
|
||||
fprintf(stderr, "\n");
|
||||
|
||||
exit(EXIT_FAILURE);
|
||||
exit(status);
|
||||
}
|
||||
|
||||
struct if_applet ifupdown_applet = {
|
||||
.name = "ifupdown",
|
||||
.main = multicall_main,
|
||||
.usage = multicall_usage,
|
||||
.groups = { &global_option_group, NULL }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -13,17 +13,62 @@
|
|||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifndef IFUPDOWN_CMD_MULTICALL_H__GUARD
|
||||
#define IFUPDOWN_CMD_MULTICALL_H__GUARD
|
||||
|
||||
#include "libifupdown/libifupdown.h"
|
||||
|
||||
struct if_applet;
|
||||
|
||||
struct if_option {
|
||||
char short_opt;
|
||||
const char *long_opt;
|
||||
const char *long_opt_desc;
|
||||
const char *desc;
|
||||
bool require_argument;
|
||||
void (*const handle)(const char *opt_arg);
|
||||
};
|
||||
|
||||
struct if_option_group {
|
||||
const char *desc;
|
||||
size_t group_size;
|
||||
const struct if_option *group;
|
||||
};
|
||||
|
||||
struct if_applet {
|
||||
const char *name;
|
||||
const char *desc;
|
||||
const char *usage;
|
||||
const char *manpage;
|
||||
int (*const main)(int argc, char *argv[]);
|
||||
void (*const usage)(void);
|
||||
const struct if_option_group *groups[16];
|
||||
};
|
||||
|
||||
extern char *argv0;
|
||||
extern const struct if_applet *self_applet;
|
||||
extern struct if_option_group global_option_group;
|
||||
|
||||
struct match_options {
|
||||
bool is_auto;
|
||||
const char *exclude_pattern;
|
||||
const char *include_pattern;
|
||||
bool pretty_print;
|
||||
bool dot;
|
||||
const char *property;
|
||||
};
|
||||
|
||||
extern struct match_options match_opts;
|
||||
|
||||
extern void process_options(const struct if_applet *applet, int argc, char *argv[]);
|
||||
extern const struct if_option *lookup_option(const struct if_applet *applet, int opt);
|
||||
|
||||
extern struct if_option_group match_option_group;
|
||||
|
||||
extern struct lif_execute_opts exec_opts;
|
||||
extern struct if_option_group exec_option_group;
|
||||
|
||||
void generic_usage(const struct if_applet *applet, int result);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
56
cmd/pretty-print-iface.c
Normal file
56
cmd/pretty-print-iface.c
Normal 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
23
cmd/pretty-print-iface.h
Normal 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
|
||||
15
dist/debian/ifupdown-ng.networking.service
vendored
Normal file
15
dist/debian/ifupdown-ng.networking.service
vendored
Normal 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
68
dist/debian/networking
vendored
Normal 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
13
dist/debian/networking.default
vendored
Normal 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
56
dist/ifupdown-ng.conf.example
vendored
Normal 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
8
dist/openrc/networking.confd
vendored
Normal 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
70
dist/openrc/networking.initd
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
#!/sbin/openrc-run
|
||||
|
||||
: ${cfgfile:="/etc/network/interfaces"}
|
||||
ifstate=/run/ifstate
|
||||
|
||||
single_iface="${RC_SVCNAME#*.}"
|
||||
if [ "$single_iface" = "$RC_SVCNAME" ]; then
|
||||
single_iface=
|
||||
fi
|
||||
|
||||
depend() {
|
||||
need localmount
|
||||
want dev-settle
|
||||
after bootmisc hwdrivers modules
|
||||
provide net
|
||||
keyword -jail -prefix -vserver -docker
|
||||
}
|
||||
|
||||
# find interfaces we want to start
|
||||
find_ifaces() {
|
||||
if [ -n "$single_iface" ]; then
|
||||
echo $single_iface
|
||||
else
|
||||
ifquery -L -a -i "$cfgfile"
|
||||
fi
|
||||
}
|
||||
|
||||
# return the list of interfaces we should try stop
|
||||
find_running_ifaces() {
|
||||
if [ -n "$single_iface" ]; then
|
||||
echo $single_iface
|
||||
else
|
||||
ifquery -r -i "$cfgfile" -S "$ifstate"
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
local iface= ret=1
|
||||
ebegin "Starting networking"
|
||||
eindent
|
||||
for iface in $(find_ifaces); do
|
||||
local r=0
|
||||
ebegin "$iface"
|
||||
if ! ifup -i "$cfgfile" -S "$ifstate" $iface >/dev/null; then
|
||||
ifdown -f -i "$cfgfile" -S "$ifstate" $iface >/dev/null 2>&1
|
||||
r=1
|
||||
fi
|
||||
# atleast one interface needs to be started for action
|
||||
# to be success
|
||||
eend $r && ret=0
|
||||
done
|
||||
eoutdent
|
||||
return $ret
|
||||
}
|
||||
|
||||
stop() {
|
||||
local iface=
|
||||
# Don't stop the network at shutdown.
|
||||
yesno ${keep_network:-YES} && yesno $RC_GOINGDOWN && return 0
|
||||
|
||||
ebegin "Stopping networking"
|
||||
eindent
|
||||
for iface in $(find_running_ifaces); do
|
||||
ebegin "$iface"
|
||||
ifdown -i "$cfgfile" -S "$ifstate" -f $iface >/dev/null
|
||||
eend $?
|
||||
done
|
||||
eoutdent
|
||||
return 0
|
||||
}
|
||||
275
doc/ADMIN-GUIDE.md
Normal file
275
doc/ADMIN-GUIDE.md
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
# ifupdown-ng for system administrators
|
||||
|
||||
ifupdown-ng is a network device manager which is backwards
|
||||
compatible with traditional ifup and ifdown as used on Debian
|
||||
and Alpine systems, while solving many design deficits with
|
||||
the original approach through robust error handling and the
|
||||
use of a dependency-solver to determine interface bring-up
|
||||
order.
|
||||
|
||||
This guide is intended to walk users through using the
|
||||
ifupdown-ng system without any assumption of familiarity
|
||||
with the legacy ifupdown system.
|
||||
|
||||
## Important Filesystem Paths
|
||||
|
||||
The ifupdown-ng system uses the following paths, ranked
|
||||
in order of importance:
|
||||
|
||||
* `/etc/network/interfaces`: the interface configuration
|
||||
database, which contains information about what
|
||||
interfaces should be configured.
|
||||
|
||||
* `/etc/network/ifupdown-ng.conf`: the main configuration
|
||||
file which controls ifupdown-ng's behaviour. See the
|
||||
*ifupdown-ng Configuration* section below.
|
||||
|
||||
* `/run/ifstate`: the interface state file, which denotes
|
||||
what physical interfaces are configured, and what
|
||||
interface definition they are configured as.
|
||||
|
||||
* `/usr/libexec/ifupdown-ng`: this directory contains the
|
||||
native ifupdown-ng executors, which are run as necessary
|
||||
to configure an interface. See the ifupdown-executor(7)
|
||||
manual page for more information on how these programs
|
||||
are written.
|
||||
|
||||
* `/etc/network/if-{up|down|pre-up|post-down}.d`:
|
||||
these directories contain scripts that are run when an
|
||||
interface is brought up or down. In general, they follow
|
||||
the same contract described in ifupdown-executor(7).
|
||||
|
||||
All configuration examples in this guide concern the
|
||||
`/etc/network/interfaces` file.
|
||||
|
||||
## ifupdown-ng Configuration
|
||||
|
||||
ifupdown-ng allows to configure some parts of it's behaviour.
|
||||
Currently the following settings are supported in
|
||||
`/etc/network/ifupdown-ng.conf`:
|
||||
|
||||
* `allow_addon_scripts`: Enable support for /etc/if-X.d addon scripts.
|
||||
These are used for compatibility with legacy setups, and may be
|
||||
disabled for performance improvements in setups where only
|
||||
ifupdown-ng executors are used. Valid values are `0` and `1`,
|
||||
default is `1`.
|
||||
|
||||
* `allow_any_iface_as_template`: Enable any interface to act as a
|
||||
template for another interface. This is presently the default,
|
||||
but is deprecated. An admin may choose to disable this setting
|
||||
in order to require inheritance from specified templates.
|
||||
Valid values are `0` and `1`, the default is `1`.
|
||||
|
||||
* `auto_executor_selection`: Automatically select executors based
|
||||
on the presence of their config options. An admin may choose to
|
||||
disable this setting in order to require explicitly enabling
|
||||
executors through `use` statements. Valid values are `0` and `1`,
|
||||
the default is `1`.
|
||||
|
||||
* `compat_create_interfaces`:
|
||||
Denotes where or not to create interfaces when compat\_* settings are
|
||||
active and it would be necessary to create an interface to be fully
|
||||
compliant. This could happen when inheriting bridge VLAN settings to
|
||||
an interface within a bridges bridge-ports setting but no interface
|
||||
stanza is found. Valid values are `0` and `1`, the default is `1`.
|
||||
|
||||
* `compat_ifupdown2_bridge_ports_inherit_vlans`: In ifupdown2 `bridge-vids`
|
||||
as well as the <bridge-pvid> set on a bridge interface will be inherited
|
||||
by all member ports if not set explicitly. When set to `1` ifupdown-ng
|
||||
behaves the same way and will internally copy both options from the
|
||||
bridge member ports if they are not set on the member port.
|
||||
Valid values are `0` and `1`, the default is `1`.
|
||||
|
||||
* `implicit_template_conversion`: In some legacy configs, a template
|
||||
may be declared as an iface, and ifupdown-ng automatically converts
|
||||
those declarations to a proper template. If this setting is
|
||||
disabled, inheritance will continue to work against non-template
|
||||
interfaces without converting them to a template. Valid values
|
||||
are `0` and `1`, the default is `1`.
|
||||
|
||||
* `use_hostname_for_dhcp`: A common configuration pattern with DHCP
|
||||
interfaces is to use `hostname $(hostname)`. If this setting is
|
||||
enabled, the `hostname` property will default to the system
|
||||
hostname. Valid values are `0` and `1`, the default is `1`.
|
||||
|
||||
## Interface Configuration
|
||||
|
||||
### Basic Configuration
|
||||
|
||||
To begin with, lets look at a basic configuration for a
|
||||
desktop computer. This scenario involves using the DHCP
|
||||
helper to learn an IPv4 address dynamically.
|
||||
|
||||
In this case, the `/etc/network/interfaces` file would
|
||||
look like:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
use dhcp
|
||||
```
|
||||
|
||||
These configuration statements do two things: designate
|
||||
that `eth0` should be started automatically with the `auto`
|
||||
keyword, and designate that the `dhcp` executor should be
|
||||
used to configure the interface.
|
||||
|
||||
As a more detailed explanation, here is a commented version:
|
||||
|
||||
```
|
||||
# Start eth0 automatically.
|
||||
auto eth0
|
||||
|
||||
# Begin an interface definition for eth0.
|
||||
iface eth0
|
||||
|
||||
# Use the dhcp executor to configure eth0.
|
||||
use dhcp
|
||||
```
|
||||
|
||||
### IPv6 RA Configuration
|
||||
|
||||
With IPv6, stateless auto-configuration is typically used to
|
||||
configure network interfaces. If you are not interested in
|
||||
using IPv4 at all, you can simply use the `ipv6-ra` executor
|
||||
to ensure that an interface is configured to accept IPv6 RA
|
||||
advertisements:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
use ipv6-ra
|
||||
```
|
||||
|
||||
### Static Configuration
|
||||
|
||||
We can use the `static` executor to configure static IPv4 and
|
||||
IPv6 addresses. If you use the `address` keyword, the `static`
|
||||
executor will automatically be used to configure the interface:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
address 203.0.113.2/24
|
||||
gateway 203.0.113.1
|
||||
```
|
||||
|
||||
#### Multiple Addresses
|
||||
|
||||
A typical scenario on servers is where a server has multiple
|
||||
IP addresses on a single interface. In this case you simply
|
||||
add additional `address` lines like this:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
address 203.0.113.2/24
|
||||
address 203.0.113.3/24
|
||||
address 203.0.113.4/24
|
||||
gateway 203.0.113.1
|
||||
```
|
||||
|
||||
#### Dual-stack configurations
|
||||
|
||||
Another typical scenario for servers is to run a dual-stack
|
||||
configuration, where interfaces have both an IPv4 and an IPv6
|
||||
address. This is accomplished in a similar way as multi-homing.
|
||||
You specify the IPv4 and IPv6 addresses you want, followed by
|
||||
gateways for each:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
address 203.0.113.2/24
|
||||
address 203.0.113.3/24
|
||||
address 203.0.113.4/24
|
||||
gateway 203.0.113.1
|
||||
|
||||
address 2001:db8:1000:2::2/64
|
||||
address 2001:db8:1000:2::3/64
|
||||
address 2001:db8:1000:2::4/64
|
||||
gateway 2001:db8:1000:2::1
|
||||
```
|
||||
|
||||
## Relationships
|
||||
|
||||
As previously mentioned, ifupdown-ng features a dependency
|
||||
resolver that allows for determining the interface configuration
|
||||
order.
|
||||
|
||||

|
||||
|
||||
In order to make use of this, dependencies can be managed in one
|
||||
of two ways:
|
||||
|
||||
### Explicit dependency management using `requires`
|
||||
|
||||
The `requires` keyword can be used to manage explicit
|
||||
dependencies:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
use dhcp
|
||||
|
||||
auto gre0
|
||||
iface gre0
|
||||
requires eth0
|
||||
|
||||
use gre
|
||||
gre-endpoint 203.0.113.2
|
||||
gre-ttl 255
|
||||
gre-flags ignore-df
|
||||
|
||||
address 203.0.113.194/30
|
||||
gateway 203.0.113.193
|
||||
```
|
||||
|
||||
### Implicit dependency management using executors
|
||||
|
||||
Executors can declare implicit dependencies which work the same
|
||||
way as explicit dependencies, but are learned at run-time, for
|
||||
example:
|
||||
|
||||
```
|
||||
auto bond0
|
||||
iface bond0
|
||||
use bond
|
||||
|
||||
bond-members eth0 eth1
|
||||
[...]
|
||||
```
|
||||
|
||||
Is with respect to dependency equivalent to:
|
||||
|
||||
```
|
||||
auto bond0
|
||||
iface bond0
|
||||
use bond
|
||||
|
||||
requires eth0 eth1
|
||||
[...]
|
||||
```
|
||||
|
||||
## Executors
|
||||
|
||||
The ifupdown-ng system is expanded with additional features via
|
||||
executors. Executors are selected on a per-interface basis using
|
||||
`use` statements, for example:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
use dhcp
|
||||
```
|
||||
|
||||
Executors are run in the order specified by the `use` statements.
|
||||
Some executors are automatically added based on other statements
|
||||
in an interface definition. To see the full list of executors
|
||||
used for an interface, use the ifquery(8) command.
|
||||
|
||||
## Questions
|
||||
|
||||
If you have further questions about how to use ifupdown-ng to
|
||||
configure a specific scenario, drop by the
|
||||
[ifupdown-ng IRC channel](irc://irc.oftc.net/#ifupdown-ng).
|
||||
39
doc/ifctrstat.scd
Normal file
39
doc/ifctrstat.scd
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
ifctrstat(8)
|
||||
|
||||
# NAME
|
||||
|
||||
ifctrstat - display interface statistics
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*ifctrstat* [<_options_>...] <_interface_> <_counter_>
|
||||
|
||||
*ifctrstat* [<_options_>...] -L|--list
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*ifctrstat* is used to query interface statistic counters in
|
||||
a kernel-agnostic way. This is useful for heterogenous
|
||||
environments where multiple kernels may be in use.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
*-h, --help*
|
||||
Display supported options to ifctrstat.
|
||||
|
||||
*-n, --no-label*
|
||||
Display the requested counter without its label.
|
||||
|
||||
*-L, --list*
|
||||
List available counters on this system.
|
||||
|
||||
*-V, --version*
|
||||
Print the ifupdown-ng version and exit.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*ifquery*(8)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
A. Wilcox <awilfox@adelielinux.org>
|
||||
68
doc/ifdown.scd
Normal file
68
doc/ifdown.scd
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
ifdown(8)
|
||||
|
||||
# NAME
|
||||
|
||||
ifdown - take interfaces down
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
ifdown [<_options_>...] <_interfaces_>
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*ifdown* is used to deconfigure interfaces according to how they are
|
||||
configured in the configuration database.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
*-a, --auto*
|
||||
Only match interfaces that are marked as _auto_.
|
||||
|
||||
*-f, --force*
|
||||
Force deconfiguration of the interface.
|
||||
This option exists for compatibility with other implementations.
|
||||
|
||||
*-h, --help*
|
||||
Display supported options to ifquery.
|
||||
|
||||
*-i, --interfaces* _FILE_
|
||||
Use _FILE_ as the config database.
|
||||
|
||||
*-n, --no-act*
|
||||
Show what commands would be run instead of actually running
|
||||
them. Useful for testing configuration changes.
|
||||
|
||||
*-v, --verbose*
|
||||
Show what commands are being run as they are executed.
|
||||
|
||||
*-E, --executor-path* _PATH_
|
||||
Look for executors in the given _PATH_.
|
||||
|
||||
*-I, --include* _PATTERN_
|
||||
Include _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
*-S, --state-file* _FILE_
|
||||
Use _FILE_ as the state database.
|
||||
|
||||
*-T, --timeout* _TIMEOUT_
|
||||
Wait up to _TIMEOUT_ seconds for executors to complete before
|
||||
raising an error.
|
||||
|
||||
*-V, --version*
|
||||
Print the ifupdown-ng version and exit.
|
||||
|
||||
*-X, --exclude* _PATTERN_
|
||||
Exclude _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*ifupdown-ng.conf*(5)
|
||||
*ifup*(8)
|
||||
*ifquery*(8)
|
||||
*interfaces*(5)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Ariadne Conill <ariadne@dereferenced.org>
|
||||
66
doc/ifparse.scd
Normal file
66
doc/ifparse.scd
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
ifparse(8)
|
||||
|
||||
# NAME
|
||||
|
||||
ifparse - redisplay interface configuration in different formats
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*ifparse* [<_options_>...] <_interfaces_...>
|
||||
|
||||
*ifparse* -A|--all
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*ifparse* is used to extract information from the interface configuration
|
||||
file. It is intended to be used to translate the interface configuration
|
||||
stanzas between different formats.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
*-a, --auto*
|
||||
Only match interfaces that are marked as _auto_.
|
||||
|
||||
*-h, --help*
|
||||
Display supported options to ifquery.
|
||||
|
||||
*-i, --interfaces* _FILE_
|
||||
Use _FILE_ as the config database.
|
||||
|
||||
*-F, --format* _FORMAT_
|
||||
Use _FORMAT_ to determine what format to use. *ifupdown* and
|
||||
*yaml-raw* formats are available.
|
||||
|
||||
*-I, --include* _PATTERN_
|
||||
Include _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
*-U, --allow-undefined*
|
||||
Create virtual interfaces for any interfaces not explicitly
|
||||
defined in the configuration file. This is primarily useful
|
||||
for property queries.
|
||||
|
||||
*-S, --state-file* _FILE_
|
||||
Use _FILE_ as the state database.
|
||||
|
||||
*-T, --timeout* _TIMEOUT_
|
||||
Wait up to _TIMEOUT_ seconds for executors to complete before
|
||||
raising an error.
|
||||
|
||||
*-V, --version*
|
||||
Print the ifupdown-ng version and exit.
|
||||
|
||||
*-X, --exclude* _PATTERN_
|
||||
Exclude _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*ifup*(8)++
|
||||
*ifdown*(8)++
|
||||
*ifquery*(8)++
|
||||
*interfaces*(5)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Ariadne Conill <ariadne@dereferenced.org>
|
||||
84
doc/ifquery.scd
Normal file
84
doc/ifquery.scd
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
ifquery(8)
|
||||
|
||||
# NAME
|
||||
|
||||
ifquery - query interface configuration and state
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
*ifquery* [<_options_>...] <_interfaces_...>
|
||||
|
||||
*ifquery* -L|--list
|
||||
|
||||
*ifquery* -s|--state
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*ifquery* is used to extract information from the interface configuration
|
||||
file. It can also be used to convert from old versions of the interface
|
||||
configuration file to the current format.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
*-a, --auto*
|
||||
Only match interfaces that are marked as _auto_.
|
||||
|
||||
*-h, --help*
|
||||
Display supported options to ifquery.
|
||||
|
||||
*-i, --interfaces* _FILE_
|
||||
Use _FILE_ as the config database.
|
||||
|
||||
*-p, --property* _PROPERTY_
|
||||
Print the values of matching properties for an interface.
|
||||
|
||||
*-r, --running*
|
||||
Print the interface names that are marked as running in
|
||||
the state database.
|
||||
|
||||
*-s, --state*
|
||||
Query the state database instead of the config database.
|
||||
|
||||
*-D, --dot*
|
||||
Generate a dependency graph that can be used with GraphViz
|
||||
*dot*(1). Used with *--list*.
|
||||
|
||||
*-I, --include* _PATTERN_
|
||||
Include _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
*-L, --list*
|
||||
List interfaces which exist in the configuration database.
|
||||
|
||||
*-P, --pretty-print*
|
||||
When listing interfaces, print their configuration in a format
|
||||
that is compatible with *interfaces*(5) files.
|
||||
|
||||
*-U, --allow-undefined*
|
||||
Create virtual interfaces for any interfaces not explicitly
|
||||
defined in the configuration file. This is primarily useful
|
||||
for property queries.
|
||||
|
||||
*-S, --state-file* _FILE_
|
||||
Use _FILE_ as the state database.
|
||||
|
||||
*-T, --timeout* _TIMEOUT_
|
||||
Wait up to _TIMEOUT_ seconds for executors to complete before
|
||||
raising an error.
|
||||
|
||||
*-V, --version*
|
||||
Print the ifupdown-ng version and exit.
|
||||
|
||||
*-X, --exclude* _PATTERN_
|
||||
Exclude _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*ifup*(8)++
|
||||
*ifdown*(8)++
|
||||
*interfaces*(5)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Ariadne Conill <ariadne@dereferenced.org>
|
||||
57
doc/ifstate.scd
Normal file
57
doc/ifstate.scd
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
ifstate(5)
|
||||
|
||||
# NAME
|
||||
|
||||
*/run/ifstate* - interface state database
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The */run/ifstate* file describes the present state of the interface
|
||||
configuration -- namely, how many interfaces are up, how many
|
||||
dependencies each interface has which are up (the refcount), and
|
||||
whether or not an interface was explicitly brought up due to request
|
||||
or configuration.
|
||||
|
||||
# FILE SYNTAX
|
||||
|
||||
At a minimum, the */run/ifstate* file contains at least one column,
|
||||
the physical to logical interface mapping. This column is formatted
|
||||
as such:
|
||||
|
||||
```
|
||||
lo=lo
|
||||
eth0=eth0
|
||||
wlan0=work
|
||||
```
|
||||
|
||||
The left side of the mapping is the physical interface, while the right
|
||||
side is the logical interface. This field is required to be present.
|
||||
|
||||
The next field is the reference count. This is a number that reflects
|
||||
the number of *active* dependencies an interface has. As interfaces
|
||||
are brought up and down, the refcount may change. This field is
|
||||
optional.
|
||||
|
||||
The final field denotes whether or not an interface was brought up
|
||||
explicitly -- either by being marked as _auto_ or brought up manually
|
||||
using *ifup*(8). The contents of this field if present is the
|
||||
_explicit_ keyword.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
An example from a typical system with localhost, eth0 and a wireguard
|
||||
VPN:
|
||||
|
||||
```
|
||||
lo=lo 1 explicit
|
||||
eth0=eth0 2 explicit
|
||||
wg0=wg0 1 explicit
|
||||
```
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*interfaces*(5)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Ariadne Conill <ariadne@dereferenced.org>
|
||||
71
doc/ifup.scd
Normal file
71
doc/ifup.scd
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
ifup(8)
|
||||
|
||||
# NAME
|
||||
|
||||
ifup - bring interfaces up
|
||||
|
||||
# SYNOPSIS
|
||||
|
||||
ifup [<_options_>...] <_interfaces_>
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
*ifup* is used to configure interfaces according to how they are
|
||||
configured in the configuration database.
|
||||
|
||||
# OPTIONS
|
||||
|
||||
*-a, --auto*
|
||||
Only match interfaces that are marked as _auto_.
|
||||
|
||||
*-f, --force*
|
||||
Force configuration of the interface.
|
||||
This option exists for compatibility with other implementations.
|
||||
|
||||
*-h, --help*
|
||||
Display supported options to ifquery.
|
||||
|
||||
*-i, --interfaces* _FILE_
|
||||
Use _FILE_ as the config database.
|
||||
|
||||
*-n, --no-act*
|
||||
Show what commands would be run instead of actually running
|
||||
them. Useful for testing configuration changes.
|
||||
|
||||
*-v, --verbose*
|
||||
Show what commands are being run as they are executed.
|
||||
|
||||
*-E, --executor-path* _PATH_
|
||||
Look for executors in the given _PATH_.
|
||||
|
||||
*-I, --include* _PATTERN_
|
||||
Include _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
*-L, --no-lock*
|
||||
Do not use a lockfile to serialize state changes.
|
||||
|
||||
*-S, --state-file* _FILE_
|
||||
Use _FILE_ as the state database.
|
||||
|
||||
*-T, --timeout* _TIMEOUT_
|
||||
Wait up to _TIMEOUT_ seconds for executors to complete before
|
||||
raising an error.
|
||||
|
||||
*-V, --version*
|
||||
Print the ifupdown-ng version and exit.
|
||||
|
||||
*-X, --exclude* _PATTERN_
|
||||
Exclude _PATTERN_ when matching against the config or state
|
||||
database.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*ifupdown-ng.conf*(5)
|
||||
*ifdown*(8)
|
||||
*ifquery*(8)
|
||||
*interfaces*(5)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Ariadne Conill <ariadne@dereferenced.org>
|
||||
92
doc/ifupdown-executor.scd
Normal file
92
doc/ifupdown-executor.scd
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
ifupdown-executor(7)
|
||||
|
||||
# NAME
|
||||
|
||||
*/usr/libexec/ifupdown-ng/program* - ifupdown executor protocol
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The ifupdown executors are programs that are typically installed
|
||||
into the ifupdown-ng executor path. They follow a specific
|
||||
protocol documented in this man page.
|
||||
|
||||
# PHASES
|
||||
|
||||
Executors are run to react to nine different phases and are not
|
||||
required to take any specific action. These phases are:
|
||||
|
||||
*depend*
|
||||
Called to determine if the executor wishes to change
|
||||
the dependency graph. The executor should write a
|
||||
space-delimited list of interface names it is dependent
|
||||
upon to _stdout_. Those interface names will be merged
|
||||
into the dependency graph. If an executor does not have
|
||||
any dependencies, it may simply exit 0 without doing
|
||||
anything.
|
||||
|
||||
*create*
|
||||
Called before *pre-up*, to explicitly allow for interface
|
||||
creation if necessary.
|
||||
|
||||
*pre-up*
|
||||
Called before the interface is going to be brought up.
|
||||
|
||||
*up*
|
||||
Called when the interface is being brought up.
|
||||
|
||||
*post-up*
|
||||
Called after the interface was successfully brought up.
|
||||
|
||||
*pre-down*
|
||||
Called before the interface is going to be taken down.
|
||||
|
||||
*down*
|
||||
Called when the interface is being taken down.
|
||||
|
||||
*post-down*
|
||||
Called after the interface was successfully taken down.
|
||||
|
||||
*destroy*
|
||||
Called after *post-down* to allow for explicitly
|
||||
destroying an interface if necessary.
|
||||
|
||||
# ENVIRONMENT
|
||||
|
||||
Executors are guaranteed to run with a core set of environment
|
||||
variables:
|
||||
|
||||
*IFACE*
|
||||
The name of the interface being configured.
|
||||
|
||||
*INTERFACES_FILE*
|
||||
The path to the interfaces database file being used.
|
||||
|
||||
*MODE*
|
||||
Either _start_, _stop_ or _depend_ depending on phase.
|
||||
This environment variable is present for compatibility
|
||||
with legacy ifupdown scripts and should not be used in
|
||||
ifupdown-ng executors.
|
||||
|
||||
*PHASE*
|
||||
The phase being executed. See the phases section for
|
||||
more information about phases.
|
||||
|
||||
*VERBOSE*
|
||||
If present, verbose output is expected from the
|
||||
executor.
|
||||
|
||||
Additionally, the properties associated with an interface are
|
||||
provided to executors. The keys are rewritten to begin with
|
||||
IF_ and are capitalized with dashes converted to underscores.
|
||||
For example, the property _bridge-ports_ will be rewritten as
|
||||
_IF_BRIDGE_PORTS_.
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
ifup(8)++
|
||||
ifdown(8)++
|
||||
interfaces(5)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Ariadne Conill <ariadne@dereferenced.org>
|
||||
76
doc/ifupdown-ng.conf.scd
Normal file
76
doc/ifupdown-ng.conf.scd
Normal 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>
|
||||
BIN
doc/img/dependency-resolution.png
Normal file
BIN
doc/img/dependency-resolution.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 10 KiB |
87
doc/interfaces-batman.scd
Normal file
87
doc/interfaces-batman.scd
Normal 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
204
doc/interfaces-bond.scd
Normal 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
173
doc/interfaces-bridge.scd
Normal 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>
|
||||
48
doc/interfaces-forward.scd
Normal file
48
doc/interfaces-forward.scd
Normal 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
40
doc/interfaces-mpls.scd
Normal 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
37
doc/interfaces-ppp.scd
Normal 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
161
doc/interfaces-tunnel.scd
Normal 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
58
doc/interfaces-vrf.scd
Normal 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
131
doc/interfaces-vxlan.scd
Normal 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 RFC 7348.
|
||||
|
||||
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
63
doc/interfaces-wifi.scd
Normal 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>
|
||||
56
doc/interfaces-wireguard.scd
Normal file
56
doc/interfaces-wireguard.scd
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
interfaces-wireguard(5)
|
||||
|
||||
# NAME
|
||||
|
||||
*interfaces-wireguard* - Wireguard extensions for the interfaces(5) file format
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
Wireguard is a comtemporary in-Kernel layer 3 VPN protocol implementation
|
||||
which aims to provide fast and secure tunnels. The following options
|
||||
allow to set up Wireguard VPN tunnels.
|
||||
|
||||
# WIREGUARD-RELATED OPTIONS
|
||||
|
||||
*wireguard-config-path* _path_
|
||||
Denotes the absolute _path_ to the Wireguard configuration file.
|
||||
If no path is given, _/etc/wireguard/<interface>.conf_ will be
|
||||
used. In the latter case _use wireguard_ has to be explicitly
|
||||
set to the interface configuration.
|
||||
|
||||
Be aware that the given configuration file will be loaded using
|
||||
*wg setconf* and not with *wg-quick*. The file format for both
|
||||
tools isn't compatible so you have to make sure you provide a
|
||||
valid configuration file for the *wg* tool. If you already have
|
||||
a configuration file for *wg-quick* you can set up the tunnel
|
||||
manually once and then dump the configuration using *wg showconf*
|
||||
and save this to _path_.
|
||||
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
A Wireguard VPN tunnel with explicit configuration file specified
|
||||
|
||||
```
|
||||
auto wg-foo
|
||||
iface wg-foo
|
||||
wireguard-config-path /etc/wireguard/foo.conf
|
||||
#
|
||||
address 192.0.2.23/42
|
||||
address 2001:db8::23/64
|
||||
```
|
||||
|
||||
A Wireguard VPN tunnel with implicit configuration file:
|
||||
|
||||
```
|
||||
auto wg-bar
|
||||
iface wg-bar
|
||||
use wireguard
|
||||
#
|
||||
address 192.0.2.23/42
|
||||
address 2001:db8::23/64
|
||||
```
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Maximilian Wilhelm <max@sdn.clinic>
|
||||
262
doc/interfaces.scd
Normal file
262
doc/interfaces.scd
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
interfaces(5)
|
||||
|
||||
# NAME
|
||||
|
||||
*/etc/network/interfaces* - interface configuration database
|
||||
|
||||
# DESCRIPTION
|
||||
|
||||
The */etc/network/interfaces* file is used to specify how network
|
||||
interfaces are configured. The file is processed by *ifquery*(8),
|
||||
*ifup*(8) and *ifdown*(8) to introspect and change system state.
|
||||
|
||||
In most cases, syntax from legacy implementations is supported as
|
||||
well, but that syntax is not discussed in detail here.
|
||||
|
||||
# FILE SYNTAX
|
||||
|
||||
The interface configuration database is composed of a series of
|
||||
stanzas. Hash symbols designate comments, which are ignored by
|
||||
the system.
|
||||
|
||||
A stanza is a collection of triples, where a triple is a key and
|
||||
value combination that is related to an *object*. Triples which
|
||||
are not associated with an *object* are considered to be part
|
||||
of the root of the configuration tree.
|
||||
|
||||
All keywords are case-sensitive and are expected to be lower-case.
|
||||
|
||||
The following is a simple example of a stanza:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
address 203.0.113.2/24
|
||||
gateway 203.0.113.1
|
||||
```
|
||||
|
||||
This stanza defines an interface named *eth0* which is configured
|
||||
with an address of *203.0.113.2* and gateway of *203.0.113.1*.
|
||||
|
||||
# SUPPORTED KEYWORDS FOR UNASSOCIATED TRIPLES
|
||||
|
||||
*auto* _object_
|
||||
Designates that _object_ should be automatically configured
|
||||
by the system when appropriate.
|
||||
|
||||
*iface* _object_
|
||||
Begins a new declaration for _object_. Any child keyword
|
||||
associated with the declaration will be stored inside
|
||||
_object_.
|
||||
|
||||
*source* _filename_
|
||||
Includes the file _filename_ as configuration data.
|
||||
|
||||
*source-directory* _directory_
|
||||
Includes the files in _directory_ as configuration data.
|
||||
|
||||
*template* _object_
|
||||
Begins a new declaration for _object_, like *iface*, except
|
||||
that _object_ is defined as a *template*.
|
||||
|
||||
# SUPPORTED KEYWORDS FOR OBJECT TRIPLES
|
||||
|
||||
Any keyword may be used inside an interface declaration block, but
|
||||
the system will only respond to certain keywords by default:
|
||||
|
||||
*address* _address_
|
||||
Associates an IPv4 or IPv6 address in CIDR notation with
|
||||
the parent interface. If an IP address without a prefix
|
||||
length is given a given _netmask_ attribute is used if present.
|
||||
If neither a prefix length nor a _netmask_ are given a /24 or /64
|
||||
prefix length is presumed for IPv4 / IPv6 as of compatibility
|
||||
reasons to classic ifupdown.
|
||||
|
||||
*netmask* _netmask_
|
||||
Associates a fallback netmask with the parent interface for
|
||||
addresses which do not have a CIDR length set. This option
|
||||
is for backwards compatibility and should not be used in new
|
||||
deployments.
|
||||
|
||||
*point-to-point* _address_
|
||||
Sets the given IPv4 _address_ as the peer address on the
|
||||
interface. This setting only takes effect for the IPv4 address
|
||||
familiy and only makes sense in combination with a /32 netmask.
|
||||
For compatiblity with ifupdown and ifupdown2, _pointopoint_ is
|
||||
an alias for this parameter.
|
||||
|
||||
*gateway* _address_
|
||||
Associates an IPv4 or IPv6 address with the parent interface
|
||||
for use as a default route (gateway). This usually is given
|
||||
once for IPv4 and once for IPv6 (in a Dual-Stack setup).
|
||||
|
||||
*link-type* _link-type_
|
||||
Denotes the link-type of the interface. When set to _dummy_,
|
||||
the interface is created as a virtual dummy interfaces.
|
||||
When set to _veth_ the interface is created as virtual veth
|
||||
interface (pair).
|
||||
|
||||
*veth-peer-name* _peer-name_
|
||||
Denotes the name of the veth peer interfaces. If not set
|
||||
the kernel will name the veth peer interface as _vethN_
|
||||
with N being an integer number.
|
||||
|
||||
*alias* _alias_
|
||||
Sets the given alias on the interface.
|
||||
|
||||
*requires* _interfaces_...
|
||||
Designates one or more required interfaces that must be
|
||||
brought up before configuration of the parent interface.
|
||||
Interfaces associated with the parent are taken down at
|
||||
the same time as the parent.
|
||||
|
||||
*inherit* _object_
|
||||
Designates that the configured interface should inherit
|
||||
configuration data from _object_. Normally _object_
|
||||
must be a *template*.
|
||||
|
||||
*use* _executor_
|
||||
Designates that an executor should be used. See _EXECUTORS_
|
||||
section for more information on executors.
|
||||
|
||||
*pre-down* _command_
|
||||
Runs _command_ before taking the interface down.
|
||||
|
||||
*down* _command_
|
||||
Runs _command_ when the interface is taken down.
|
||||
|
||||
*post-down* _command_
|
||||
Runs _command_ after taking the interface down.
|
||||
|
||||
*pre-up* _command_
|
||||
Runs _command_ before bringing the interface up.
|
||||
|
||||
*up* _command_
|
||||
Runs _command_ when the interface is brought up.
|
||||
|
||||
*post-up* _command_
|
||||
Runs _command_ after bringing the interface up.
|
||||
|
||||
Additional packages such as *bonding*, *bridge*, *tunnel*, *vrf* and
|
||||
*vxlan* add additional keywords to this vocabulary.
|
||||
|
||||
# EXECUTORS
|
||||
|
||||
The *use* keyword designates that an _executor_ should be used.
|
||||
This system is extendable by additional packages, but the
|
||||
most common executors are:
|
||||
|
||||
*batman*
|
||||
The interface is a B.A.T.M.A.N. adv. mesh interface.
|
||||
Configuration of B.A.T.M.A.N. adv. interfaces requires the
|
||||
*batctl* untiliy to be installed.
|
||||
|
||||
*bond*
|
||||
The interface is a bonded interface. Configuration
|
||||
of bonded interfaces requires the *bonding* package
|
||||
to be installed.
|
||||
|
||||
*bridge*
|
||||
The interface is an ethernet bridge. Configuration
|
||||
of ethernet bridges requires the *bridge* package
|
||||
to be installed.
|
||||
|
||||
*dhcp*
|
||||
Use a DHCP client to learn the IPv4 address of an
|
||||
interface.
|
||||
|
||||
*forward*
|
||||
Configures forwarding settings on the interface.
|
||||
|
||||
*loopback*
|
||||
Designates the interface as a loopback device.
|
||||
|
||||
*ppp*
|
||||
Designates the interface as a PPP device. Configuration
|
||||
of PPP interfaces require the *ppp* and probably the *pppoe*
|
||||
packages to be installed.
|
||||
|
||||
*tunnel*
|
||||
The interface is a tunnel. Configuration of tunnels
|
||||
requires the *tunnel* package to be installed on Alpine
|
||||
Linux.
|
||||
|
||||
*vrf*
|
||||
The interface is a VRF. Configuration of VRFs requires
|
||||
the *vrf* package to be installed.
|
||||
|
||||
*vxlan*
|
||||
The interface is a Virtual Extensible LAN (VXLAN) tunnel
|
||||
endpoint.
|
||||
|
||||
*wifi*
|
||||
The interface is a Wi-Fi (IEEE 802.11) client interface.
|
||||
Configuration of the WiFi client interface requires the
|
||||
*wireless-tools* package to be installed.
|
||||
The *wpa_supplicant* package must also be installed to
|
||||
connect to hotspots using WPA-based security.
|
||||
|
||||
*wireguard*
|
||||
The interface is a Wireguard VPN tunnel endpoint.
|
||||
|
||||
Check *interfaces-<executor>(5)* for further informaton about a given
|
||||
executor and available configuration parameters.
|
||||
|
||||
If the _auto\_executor\_selection_ ifupdown-ng.conf option is enabled,
|
||||
*use* statements will automatically be added for executors when their
|
||||
configuration statements are present in the interfaces file.
|
||||
|
||||
# EXAMPLES
|
||||
|
||||
Configure a bridge interface *br0* with *bond0* attached to it,
|
||||
which is a failover between *eth0* and *eth1*. This requires
|
||||
the *bonding* and *bridge* packages to be installed:
|
||||
|
||||
```
|
||||
auto br0
|
||||
iface br0
|
||||
use bridge
|
||||
requires bond0
|
||||
address 203.0.113.2/24
|
||||
gateway 203.0.113.1
|
||||
|
||||
iface bond0
|
||||
use bond
|
||||
requires eth0 eth1
|
||||
bond-mode 802.3ad
|
||||
bond-xmit-hash-policy layer2+3
|
||||
```
|
||||
|
||||
Configure a network interface to use DHCP to learn its IPv4
|
||||
address:
|
||||
|
||||
```
|
||||
auto eth0
|
||||
iface eth0
|
||||
use dhcp
|
||||
```
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*ifstate*(5)
|
||||
*ifupdown-ng.conf*(5)
|
||||
*ifup*(8)
|
||||
*ifdown*(8)
|
||||
*ifquery*(8)
|
||||
*ifctrstat*(8)
|
||||
*interfaces-batman*(5)
|
||||
*interfaces-bond*(5)
|
||||
*interfaces-bridge*(5)
|
||||
*interfaces-forward*(5)
|
||||
*interfaces-mpls*(5)
|
||||
*interfaces-ppp*(5)
|
||||
*interfaces-tunnel*(5)
|
||||
*interfaces-vrf*(5)
|
||||
*interfaces-vxlan*(5)
|
||||
*interfaces-wifi*(5)
|
||||
*interfaces-wireguard*(5)
|
||||
|
||||
# AUTHORS
|
||||
|
||||
Ariadne Conill <ariadne@dereferenced.org>++
|
||||
Maximilian Wilhelm <max@sdn.clinic>
|
||||
126
executor-scripts/linux/batman
Normal file
126
executor-scripts/linux/batman
Normal 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
57
executor-scripts/linux/bond
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# This executor is responsible for setting up bond/LAG interfaces.
|
||||
#
|
||||
# Sat, 03 Oct 2020 20:42:19 +0200
|
||||
# -- Maximilian Wilhelm <max@sdn.clinic>
|
||||
#
|
||||
[ -n "$VERBOSE" ] && set -x
|
||||
|
||||
get_bond_options() {
|
||||
# We only care for options of format IF_BOND_<OPTION_NAME>
|
||||
env | grep '^IF_BOND_[A-Z0-9_]\+' | while IFS="=" read opt value; do
|
||||
# Members are handled seperately
|
||||
if [ "${opt}" = "IF_BOND_MEMBERS" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
# Convert options for the actual name
|
||||
real_opt=$(echo "${opt}" | sed -e 's/^IF_BOND_\([A-Z0-9_]\+\)/\1/' | tr '[A-Z]' '[a-z]')
|
||||
|
||||
echo -n " ${real_opt} ${value}"
|
||||
done
|
||||
}
|
||||
|
||||
case "$PHASE" in
|
||||
depend)
|
||||
echo "${IF_BOND_MEMBERS}"
|
||||
;;
|
||||
|
||||
create)
|
||||
if [ -d "/sys/class/net/${IFACE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Gather bonding options for this interface
|
||||
options=$(get_bond_options)
|
||||
|
||||
# Create bond
|
||||
${MOCK} ip link add "${IFACE}" type bond ${options}
|
||||
|
||||
# Add members to bundle
|
||||
for member_iface in ${IF_BOND_MEMBERS}; do
|
||||
# Work around the current execution order
|
||||
${MOCK} ip link set "${member_iface}" down
|
||||
${MOCK} ip link set master "${IFACE}" "${member_iface}"
|
||||
${MOCK} ip link set "${member_iface}" up
|
||||
done
|
||||
;;
|
||||
|
||||
destroy)
|
||||
if [ -z "${MOCK}" -a ! -d "/sys/class/net/${IFACE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
${MOCK} ip link del "${IFACE}"
|
||||
;;
|
||||
esac
|
||||
302
executor-scripts/linux/bridge
Executable file
302
executor-scripts/linux/bridge
Executable file
|
|
@ -0,0 +1,302 @@
|
|||
#!/bin/sh
|
||||
[ -n "$VERBOSE" ] && set -x
|
||||
|
||||
# Copyright (C) 2012, 2020 Natanael Copa <ncopa@alpinelinux.org>
|
||||
# Copyright (C) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
# Copyright (C) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
#
|
||||
# Permission to use, copy, modify, and/or distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# This software is provided 'as is' and without any warranty, express or
|
||||
# implied. In no event shall the authors be liable for any damages arising
|
||||
# from the use of this software.
|
||||
|
||||
################################################################################
|
||||
# Bridge management functions #
|
||||
################################################################################
|
||||
|
||||
all_ports_exist() {
|
||||
local i=
|
||||
for i in "$@"; do
|
||||
[ -d /sys/class/net/$i ] || return 1
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
wait_ports() {
|
||||
local timeout= waitports=
|
||||
[ -z "$IF_BRIDGE_WAITPORT" ] && return 0
|
||||
set -- $IF_BRIDGE_WAITPORT
|
||||
timeout="$1"
|
||||
shift
|
||||
waitports="$@"
|
||||
[ -z "$waitports" ] && waitports="$PORTS"
|
||||
while ! all_ports_exist $waitports; do
|
||||
[ "$timeout" -eq 0 ] && return 0
|
||||
timeout=$(($timeout - 1))
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
all_ports() {
|
||||
local i=
|
||||
for i in /sys/class/net/*/ifindex; do
|
||||
i=${i%/*}
|
||||
i=${i##*/}
|
||||
case "$i" in
|
||||
lo|$IFACE) continue;;
|
||||
*) echo $i;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
add_ports() {
|
||||
local port=
|
||||
for port in $PORTS; do
|
||||
if [ -n "$IF_BRIDGE_HW" ]; then
|
||||
ip link set dev $port addr $IF_BRIDGE_HW
|
||||
fi
|
||||
ip link set dev $port master $IFACE && ip link set dev $port up
|
||||
done
|
||||
}
|
||||
|
||||
del_ports() {
|
||||
local port=
|
||||
for port in $PORTS; do
|
||||
ip link set dev $port down
|
||||
ip link set dev $port nomaster
|
||||
done
|
||||
}
|
||||
|
||||
set_bridge_opts_brctl() {
|
||||
[ -n "$IF_BRIDGE_AGEING" ] \
|
||||
&& brctl setageing $IFACE $IF_BRIDGE_AGEING
|
||||
[ -n "$IF_BRIDGE_BRIDGEPRIO" ] \
|
||||
&& brctl setbridgeprio $IFACE $IF_BRIDGE_BRIDGEPRIO
|
||||
[ -n "$IF_BRIDGE_FD" ] \
|
||||
&& brctl setfd $IFACE $IF_BRIDGE_FD
|
||||
[ -n "$IF_BRIDGE_HELLO" ] \
|
||||
&& brctl sethello $IFACE $IF_BRIDGE_HELLO
|
||||
[ -n "$IF_BRIDGE_MAXAGE" ] \
|
||||
&& brctl setmaxage $IFACE $IF_BRIDGE_MAXAGE
|
||||
[ -n "$IF_BRIDGE_PATHCOST" ] \
|
||||
&& brctl setpathcost $IFACE $IF_BRIDGE_PATHCOST
|
||||
[ -n "$IF_BRIDGE_PORTPRIO" ] \
|
||||
&& brctl setportprio $IFACE $IF_BRIDGE_PORTPRIO
|
||||
[ -n "$IF_BRIDGE_STP" ] \
|
||||
&& brctl stp $IFACE $IF_BRIDGE_STP
|
||||
}
|
||||
|
||||
yesno() {
|
||||
case "$1" in
|
||||
yes|YES|true|TRUE|1)
|
||||
echo 1
|
||||
;;
|
||||
*)
|
||||
echo 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
set_bridge_opts_iproute2() {
|
||||
[ -n "$IF_BRIDGE_AGEING" ] \
|
||||
&& ip link set dev $IFACE type bridge ageing_time $IF_BRIDGE_AGEING
|
||||
[ -n "$IF_BRIDGE_BRIDGEPRIO" ] \
|
||||
&& ip link set dev $IFACE type bridge priority $IF_BRIDGE_BRIDGEPRIO
|
||||
[ -n "$IF_BRIDGE_FD" ] \
|
||||
&& ip link set dev $IFACE type bridge forward_delay $IF_BRIDGE_FD
|
||||
[ -n "$IF_BRIDGE_HELLO" ] \
|
||||
&& ip link set dev $IFACE type bridge hello_time $IF_BRIDGE_HELLO
|
||||
[ -n "$IF_BRIDGE_MAXAGE" ] \
|
||||
&& ip link set dev $IFACE type bridge max_age $IF_BRIDGE_MAXAGE
|
||||
[ -n "$IF_BRIDGE_PATHCOST" ] \
|
||||
&& bridge link set dev $IFACE cost $IF_BRIDGE_PATHCOST
|
||||
[ -n "$IF_BRIDGE_PORTPRIO" ] \
|
||||
&& bridge link set dev $IFACE priority $IF_BRIDGE_PORTPRIO
|
||||
[ -n "$IF_BRIDGE_STP" ] \
|
||||
&& ip link set dev $IFACE type bridge stp $(yesno $IF_BRIDGE_STP)
|
||||
[ -n "$IF_BRIDGE_VLAN_AWARE" ] \
|
||||
&& ip link set dev $IFACE type bridge vlan_filtering $(yesno $IF_BRIDGE_VLAN_AWARE)
|
||||
}
|
||||
|
||||
set_bridge_opts() {
|
||||
[ -x /sbin/bridge ] && set_bridge_opts_iproute2 && return 0
|
||||
[ -x /sbin/brctl ] && set_bridge_opts_brctl && return 0
|
||||
}
|
||||
|
||||
|
||||
all_ports_ready() {
|
||||
local port=
|
||||
for port in $PORTS; do
|
||||
case $(cat /sys/class/net/$IFACE/brif/$port/state) in
|
||||
""|0|3) ;; # 0 = disabled, 3 = forwarding
|
||||
[12]) return 1;;
|
||||
esac
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
find_maxwait() {
|
||||
awk '{printf("%.f\n", 2 * $0 / 100); }' \
|
||||
/sys/class/net/$IFACE/bridge/forward_delay
|
||||
}
|
||||
|
||||
wait_bridge() {
|
||||
local timeout=$IF_BRIDGE_MAXWAIT
|
||||
if [ -z "$timeout" ]; then
|
||||
timeout=$(find_maxwait)
|
||||
fi
|
||||
ip link set dev $IFACE up
|
||||
while ! all_ports_ready; do
|
||||
[ $timeout -eq 0 ] && break
|
||||
timeout=$(($timeout - 1))
|
||||
sleep 1
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
################################################################################
|
||||
# Bridge port management functions #
|
||||
################################################################################
|
||||
|
||||
configure_access_port() {
|
||||
port="$1"
|
||||
vlan="$2"
|
||||
self="$3"
|
||||
|
||||
# Cleans all existing VLANs (probably at least VLAN 1)
|
||||
bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | while read vid flags; do
|
||||
bridge vlan del vid "${vid}" dev "${port}" ${self}
|
||||
done
|
||||
|
||||
bridge vlan add vid "${vlan}" pvid untagged dev "${port}" ${self}
|
||||
}
|
||||
|
||||
configure_trunk_port() {
|
||||
port="$1"
|
||||
self="$2"
|
||||
|
||||
# Working on the bridge itself?
|
||||
if [ "${self}" ]; then
|
||||
allow_untagged="${IF_BRIDGE_ALLOW_UNTAGGED}"
|
||||
pvid="${IF_BRIDGE_PVID}"
|
||||
vids="${IF_BRIDGE_VIDS}"
|
||||
else
|
||||
allow_untagged=$(ifquery -p bridge-allow-untagged ${port} 2>/dev/null || true)
|
||||
pvid=$(ifquery -p bridge-pvid ${port} 2>/dev/null || true)
|
||||
vids=$(ifquery -p bridge-vids ${port} 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
# If bridge-allow-untagged if set to off, remove untagged VLAN. If it's
|
||||
# one of our bridge-vids, it will be set again later.
|
||||
if [ "${allow_untagged}" -a "$(yesno ${allow_untagged})" = 0 ]; then
|
||||
untagged_vid=$(bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | awk '/Untagged/ { print $1 }')
|
||||
if [ "${untagged_vid}" ]; then
|
||||
bridge vlan del vid "${untagged_vid}" dev "${port}" ${self}
|
||||
fi
|
||||
fi
|
||||
|
||||
# The vlan specified is to be considered a PVID at ingress.
|
||||
# Any untagged frames will be assigned to this VLAN.
|
||||
if [ "${pvid}" ]; then
|
||||
cur_pvid=$(bridge vlan show dev ${port} | tail -n +2 | grep -v '^$' | sed -e "s/^${port}//" | awk '/PVID/ { print $1 }')
|
||||
if [ "${cur_pvid}" ]; then
|
||||
bridge vlan del vid ${cur_pvid} dev "${port}" ${self}
|
||||
fi
|
||||
|
||||
bridge vlan add vid "${pvid}" dev "${port}" pvid untagged ${self}
|
||||
fi
|
||||
|
||||
# Add regular tagged VLANs
|
||||
for vid in ${vids}; do
|
||||
bridge vlan add vid $vid dev "${port}" ${self}
|
||||
done
|
||||
}
|
||||
|
||||
# Configure VLANs on the bridge interface itself
|
||||
set_bridge_vlans() {
|
||||
# Shall the bridge interface be an untagged port?
|
||||
if [ "${IF_BRIDGE_ACCESS}" ]; then
|
||||
configure_access_port "${IFACE}" "${IF_BRIDGE_ACCESS}" "self"
|
||||
|
||||
# Configure bridge interface as trunk port
|
||||
else
|
||||
configure_trunk_port "${IFACE}" "self"
|
||||
fi
|
||||
}
|
||||
|
||||
# Configure VLANs on the bridge-ports
|
||||
set_bridge_port_vlans() {
|
||||
for port in ${PORTS}; do
|
||||
access_vlan=$(ifquery -p bridge-access ${port} 2>/dev/null || true)
|
||||
|
||||
# Shall this prot interface be an untagged port?
|
||||
if [ "${access_vlan}" ]; then
|
||||
configure_access_port "${port}" "${access_vlan}"
|
||||
|
||||
# Configure port as trunk
|
||||
else
|
||||
configure_trunk_port "${port}"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
case "$IF_BRIDGE_PORTS" in
|
||||
"") ;;
|
||||
none) PORTS="";;
|
||||
all) PORTS=$(all_ports);;
|
||||
*) PORTS="$IF_BRIDGE_PORTS";;
|
||||
esac
|
||||
|
||||
case "$PHASE" in
|
||||
depend)
|
||||
# Called for the bridge interface
|
||||
if [ "${IF_BRIDGE_PORTS}" ]; then
|
||||
echo "$PORTS"
|
||||
fi
|
||||
;;
|
||||
|
||||
create)
|
||||
# Called for the bridge interface
|
||||
if [ "${IF_BRIDGE_PORTS}" -a ! -d "/sys/class/net/${IFACE}" ]; then
|
||||
ip link add "${IFACE}" type bridge
|
||||
fi
|
||||
;;
|
||||
|
||||
pre-up)
|
||||
# Called for the bridge interface
|
||||
if [ "${IF_BRIDGE_PORTS}" ]; then
|
||||
wait_ports
|
||||
set_bridge_opts
|
||||
set_bridge_vlans
|
||||
add_ports
|
||||
set_bridge_port_vlans
|
||||
wait_bridge
|
||||
|
||||
# Called for a bridge member port
|
||||
elif [ "${IF_BRIDGE_VIDS}" -o "${IF_BRIDGE_PVID}" -o "${IF_BRIDGE_ACCESS}" -o "${IF_BRIDGE_ALLOW_UNTAGGED}" ]; then
|
||||
# Eventually we want to configure VLAN settings of member ports here.
|
||||
# The current execution model does not allow this, so this is a no-op
|
||||
# for now and we work around this by configuring ports while configuring
|
||||
# the bridge.
|
||||
true
|
||||
fi
|
||||
;;
|
||||
|
||||
post-down)
|
||||
# Called for the bridge interface
|
||||
if [ "${IF_BRIDGE_PORTS}" ]; then
|
||||
del_ports
|
||||
ip link set dev $IFACE down
|
||||
fi
|
||||
;;
|
||||
|
||||
destroy)
|
||||
# Called for the bridge interface
|
||||
if [ "${IF_BRIDGE_PORTS}" -a -d "/sys/class/net/${IFACE}" ]; then
|
||||
ip link del "${IFACE}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
67
executor-scripts/linux/dhcp
Executable file
67
executor-scripts/linux/dhcp
Executable file
|
|
@ -0,0 +1,67 @@
|
|||
#!/bin/sh
|
||||
# some users provide a shell fragment for the hostname property.
|
||||
[ -n "$IF_DHCP_HOSTNAME" ] && IF_DHCP_HOSTNAME=$(eval echo $IF_DHCP_HOSTNAME)
|
||||
|
||||
determine_implementation() {
|
||||
[ -n "$IF_DHCP_PROGRAM" ] && echo "$IF_DHCP_PROGRAM" && return
|
||||
[ -x /sbin/dhcpcd ] && echo "dhcpcd" && return
|
||||
[ -x /usr/sbin/dhclient ] && echo "dhclient" && return
|
||||
[ -x /sbin/udhcpc ] && echo "udhcpc" && return
|
||||
echo "could not find a supported DHCP implementation"
|
||||
exit 1
|
||||
}
|
||||
|
||||
start() {
|
||||
case "$1" in
|
||||
dhcpcd)
|
||||
[ -n "$IF_DHCP_HOSTNAME" ] && optargs="$optargs -h $IF_DHCP_HOSTNAME"
|
||||
[ -n "$IF_DHCP_VENDOR" ] && optargs="$optargs -i $IF_DHCP_VENDOR"
|
||||
[ -n "$IF_DHCP_CLIENT_ID" ] && optargs="$optargs -i $IF_DHCP_CLIENT_ID"
|
||||
[ -n "$IF_DHCP_LEASETIME" ] && optargs="$optargs -l $IF_DHCP_LEASETIME"
|
||||
${MOCK} /sbin/dhcpcd $optargs $IFACE
|
||||
;;
|
||||
dhclient)
|
||||
# Specific config file given?
|
||||
if [ -n "$IF_DHCP_CONFIG" ]; then
|
||||
optargs="$optargs -cf $IF_DHCP_CONFIG"
|
||||
fi
|
||||
|
||||
${MOCK} /usr/sbin/dhclient -pf /var/run/dhclient.$IFACE.pid $optargs $IFACE
|
||||
;;
|
||||
udhcpc)
|
||||
optargs=$(eval echo $IF_UDHCPC_OPTS)
|
||||
[ -n "$IF_DHCP_HOSTNAME" ] && optargs="$optargs -x hostname:$IF_DHCP_HOSTNAME"
|
||||
[ -n "$IF_DHCP_CLIENT_ID" ] && optargs="$optargs -c $IF_DHCP_CLIENT_ID"
|
||||
[ -n "$IF_DHCP_SCRIPT" ] && optargs="$optargs -s $IF_DHCP_SCRIPT"
|
||||
${MOCK} /sbin/udhcpc -b -R -p /var/run/udhcpc.$IFACE.pid -i $IFACE $optargs
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
stop() {
|
||||
case "$1" in
|
||||
dhcpcd)
|
||||
${MOCK} /sbin/dhcpcd -k $IFACE
|
||||
;;
|
||||
dhclient)
|
||||
${MOCK} kill -9 $(cat /var/run/dhclient.$IFACE.pid) 2>/dev/null
|
||||
;;
|
||||
udhcpc)
|
||||
${MOCK} kill $(cat /var/run/udhcpc.$IFACE.pid)
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
impl=$(determine_implementation)
|
||||
|
||||
[ -z "$VERBOSE" ] || set -x
|
||||
|
||||
case "$PHASE" in
|
||||
up) start $impl ;;
|
||||
down) stop $impl ;;
|
||||
*) ;;
|
||||
esac
|
||||
58
executor-scripts/linux/ethtool
Executable file
58
executor-scripts/linux/ethtool
Executable 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
19
executor-scripts/linux/forward
Executable file
|
|
@ -0,0 +1,19 @@
|
|||
#!/bin/sh
|
||||
|
||||
yesno() {
|
||||
case "$1" in
|
||||
yes|1) echo 1 ;;
|
||||
*) echo 0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
[ "$PHASE" != "up" ] && exit 0
|
||||
[ -z "$VERBOSE" ] || set -x
|
||||
|
||||
[ -n "$IF_FORWARD_IPV4" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV4) > /proc/sys/net/ipv4/conf/$IFACE/forwarding"
|
||||
[ -n "$IF_FORWARD_IPV6" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV6) > /proc/sys/net/ipv6/conf/$IFACE/forwarding"
|
||||
|
||||
[ -n "$IF_FORWARD_IPV4_MC" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV4_MC) > /proc/sys/net/ipv4/conf/$IFACE/mc_forwarding"
|
||||
[ -n "$IF_FORWARD_IPV6_MC" ] && ${MOCK} /bin/sh -c "echo $(yesno $IF_FORWARD_IPV6_MC) > /proc/sys/net/ipv6/conf/$IFACE/mc_forwarding"
|
||||
|
||||
exit 0
|
||||
28
executor-scripts/linux/gre
Executable file
28
executor-scripts/linux/gre
Executable file
|
|
@ -0,0 +1,28 @@
|
|||
#!/bin/sh
|
||||
# Executor for advanced GRE tunnel management.
|
||||
|
||||
[ -z "$IF_GRE_LOCAL" ] && exit 1
|
||||
[ -z "$IF_GRE_REMOTE" ] && exit 1
|
||||
[ -z "$IF_GRE_MODE" ] && IF_GRE_MODE="gre"
|
||||
|
||||
COMMAND="link"
|
||||
FAMILY="-4"
|
||||
[ "$IF_GRE_MODE" = "ip6gre" ] && FAMILY="-6"
|
||||
|
||||
PARAMS="mode $IF_GRE_MODE local '$IF_GRE_LOCAL' remote '$IF_GRE_REMOTE'"
|
||||
[ -n "$IF_GRE_TTL" ] && PARAMS="$PARAMS ttl '$IF_GRE_TTL'"
|
||||
[ -n "$IF_GRE_FLAGS" ] && PARAMS="$PARAMS $IF_GRE_FLAGS"
|
||||
|
||||
[ -n "$PARAMS" ] || exit 0
|
||||
|
||||
case "$PHASE" in
|
||||
create)
|
||||
${MOCK} eval ip $FAMILY $COMMAND add $IFACE $PARAMS
|
||||
;;
|
||||
destroy)
|
||||
${MOCK} ip $FAMILY $COMMAND del $IFACE
|
||||
;;
|
||||
depend)
|
||||
echo "$IF_GRE_DEV"
|
||||
;;
|
||||
esac
|
||||
16
executor-scripts/linux/ipv6-ra
Executable file
16
executor-scripts/linux/ipv6-ra
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/bin/sh
|
||||
start() {
|
||||
${MOCK} /bin/sh -c "echo 1 > /proc/sys/net/ipv6/conf/$IFACE/accept_ra"
|
||||
}
|
||||
|
||||
stop() {
|
||||
${MOCK} /bin/sh -c "echo 0 > /proc/sys/net/ipv6/conf/$IFACE/accept_ra"
|
||||
}
|
||||
|
||||
[ -z "$VERBOSE" ] || set -x
|
||||
|
||||
case "$PHASE" in
|
||||
up) start $impl ;;
|
||||
down) stop $impl ;;
|
||||
*) ;;
|
||||
esac
|
||||
112
executor-scripts/linux/link
Executable file
112
executor-scripts/linux/link
Executable file
|
|
@ -0,0 +1,112 @@
|
|||
#!/bin/sh
|
||||
[ -n "$VERBOSE" ] && set -x
|
||||
|
||||
is_vlan() {
|
||||
case "$IFACE" in
|
||||
*#*) return 1 ;;
|
||||
*:*) return 1 ;;
|
||||
vlan*.*) return 1 ;;
|
||||
vlan*)
|
||||
IF_VLAN_ID="${IFACE#vlan}"
|
||||
[ -n "${IF_VLAN_RAW_DEVICE:-}" ] && return 0
|
||||
return 1
|
||||
;;
|
||||
*.*)
|
||||
IF_VLAN_RAW_DEVICE="${IFACE%.*}"
|
||||
IF_VLAN_ID="${IFACE##*.}"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
[ -z "${IF_VLAN_ID:-}" ] && return 1
|
||||
[ -z "${IF_VLAN_RAW_DEVICE:-}" ] && return 1
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
case "$PHASE" in
|
||||
depend)
|
||||
# vlan-raw-device
|
||||
if is_vlan; then
|
||||
echo "$IF_VLAN_RAW_DEVICE"
|
||||
|
||||
# veth-peer-name
|
||||
elif [ "${IF_LINK_TYPE}" = "veth" -a "${IF_VETH_PEER_NAME}" ]; then
|
||||
echo "${IF_VETH_PEER_NAME}"
|
||||
fi
|
||||
;;
|
||||
|
||||
create)
|
||||
if [ "${IF_LINK_TYPE}" = "dummy" ]; then
|
||||
if [ -d "/sys/class/net/${IFACE}" ]; then
|
||||
iface_type=$(ip -d link show dev "${IFACE}" | head -n3 | tail -n1 | awk '{ print $1 }')
|
||||
if [ "${iface_type}" != 'dummy' ]; then
|
||||
echo "Interface ${IFACE} exists but is of type ${iface_type} instead of dummy"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit 0
|
||||
fi
|
||||
|
||||
${MOCK} ip link add "${IFACE}" type dummy
|
||||
|
||||
elif [ "${IF_LINK_TYPE}" = "veth" ]; then
|
||||
if [ ! -d "/sys/class/net/${IFACE}" ]; then
|
||||
ARGS=""
|
||||
if [ "${IF_VETH_PEER_NAME}" ]; then
|
||||
ARGS="peer ${IF_VETH_PEER_NAME}"
|
||||
fi
|
||||
|
||||
${MOCK} ip link add "${IFACE}" type veth ${ARGS}
|
||||
fi
|
||||
|
||||
elif is_vlan; then
|
||||
if [ -d "/sys/class/net/${IFACE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "${MOCK}" ]; then
|
||||
if [ ! -d "/sys/class/net/${IF_VLAN_RAW_DEVICE}" ]; then
|
||||
echo "Underlay device ${IF_VLAN_RAW_DEVICE} for ${IFACE} does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [ -d /proc/net/vlan ]; then
|
||||
echo "Loading 8021q kernel module for VLAN support"
|
||||
${MOCK} modprobe 8021q
|
||||
fi
|
||||
fi
|
||||
|
||||
${MOCK} ip link add link "${IF_VLAN_RAW_DEVICE}" name "${IFACE}" type vlan id "${IF_VLAN_ID}"
|
||||
fi
|
||||
;;
|
||||
up)
|
||||
IF_LINK_OPTIONS="$IF_LINK_OPTIONS"
|
||||
[ -n "$IF_MTU" ] && IF_LINK_OPTIONS="$IF_LINK_OPTIONS mtu $IF_MTU"
|
||||
[ -n "$IF_HWADDRESS" ] && IF_LINK_OPTIONS="$IF_LINK_OPTIONS address $IF_HWADDRESS"
|
||||
|
||||
${MOCK} ip link set up dev "${IFACE}" ${IF_LINK_OPTIONS}
|
||||
|
||||
# Set alias is configured
|
||||
if [ "${IF_ALIAS}" ]; then
|
||||
${MOCK} ip link set alias "${IF_ALIAS}" dev "${IFACE}"
|
||||
fi
|
||||
;;
|
||||
down)
|
||||
# Don't complain about a vanished interface when downing it
|
||||
if [ -z "${MOCK}" -a ! -d "/sys/class/net/${IFACE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
${MOCK} ip link set down dev "${IFACE}"
|
||||
;;
|
||||
destroy)
|
||||
if [ "${IF_LINK_TYPE}" = "dummy" ] || [ "${IF_LINK_TYPE}" = "veth" ] || is_vlan; then
|
||||
if [ -z "${MOCK}" -a ! -d "/sys/class/net/${IFACE}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
${MOCK} ip link del "${IFACE}"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
36
executor-scripts/linux/mpls
Executable file
36
executor-scripts/linux/mpls
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# Maximilian Wilhelm <max@sdn.clinic>
|
||||
# -- Thu, 17 Dec 2020 03:02:10 +0100
|
||||
#
|
||||
# This executor is responsible for setting up MPLS decapsulation on a given interface.
|
||||
#
|
||||
# See interfaces-mpls(5) for a list of supported options.
|
||||
#
|
||||
|
||||
yesno() {
|
||||
case "$1" in
|
||||
yes|1) echo 1 ;;
|
||||
*) echo 0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
[ -z "$VERBOSE" ] || set -x
|
||||
|
||||
# We only operate in pre-up phase
|
||||
[ "$PHASE" != "pre-up" ] && exit 0
|
||||
|
||||
|
||||
if [ "$IF_MPLS_ENABLE" ]; then
|
||||
value=$(yesno $IF_MPLS_ENABLE)
|
||||
|
||||
# Load mpls module if we should enable MPLS decap on (at least) one interface
|
||||
if [ "${value}" = 1 ]; then
|
||||
${MOCK} modprobe mpls_iptunnel
|
||||
fi
|
||||
|
||||
# If MPLS support isn't loaded and we are not MOCKing, carry on
|
||||
if [ -f "/proc/sys/net/mpls/conf/$IFACE/input" -o "${MOCK}" ]; then
|
||||
${MOCK} /bin/sh -c "echo ${value} > /proc/sys/net/mpls/conf/$IFACE/input"
|
||||
fi
|
||||
fi
|
||||
14
executor-scripts/linux/ppp
Executable file
14
executor-scripts/linux/ppp
Executable file
|
|
@ -0,0 +1,14 @@
|
|||
#!/bin/sh
|
||||
[ -z "$IF_PPP_PROVIDER" ] && exit 0
|
||||
|
||||
case "$PHASE" in
|
||||
create)
|
||||
${MOCK} pon $IF_PPP_PROVIDER
|
||||
;;
|
||||
destroy)
|
||||
${MOCK} poff $IF_PPP_PROVIDER
|
||||
;;
|
||||
depend)
|
||||
echo "$IF_PPP_PHYSDEV"
|
||||
;;
|
||||
esac
|
||||
56
executor-scripts/linux/static
Executable file
56
executor-scripts/linux/static
Executable file
|
|
@ -0,0 +1,56 @@
|
|||
#!/bin/sh
|
||||
[ -z "${VERBOSE}" ] || set -x
|
||||
|
||||
[ -z "${IF_METRIC}" ] && IF_METRIC="1"
|
||||
[ -n "${IF_VRF_TABLE}" ] && VRF_TABLE="table ${IF_VRF_TABLE}"
|
||||
[ -n "${IF_VRF_MEMBER}" ] && VRF_TABLE="vrf ${IF_VRF_MEMBER}"
|
||||
[ -n "${IF_METRIC}" ] && METRIC="metric ${IF_METRIC}"
|
||||
|
||||
|
||||
addr_family() {
|
||||
if [ "$1" != "${1#*[0-9].[0-9]}" ]; then
|
||||
echo "-4"
|
||||
elif [ "$1" != "${1#*:[0-9a-fA-F]}" ]; then
|
||||
echo "-6"
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
configure_addresses() {
|
||||
for addr in ${IF_ADDRESSES}; do
|
||||
addrfam=$(addr_family ${addr})
|
||||
if [ "${IF_POINT_TO_POINT}" -a "${addrfam}" = "-4" ]; then
|
||||
PEER="peer ${IF_POINT_TO_POINT}"
|
||||
else
|
||||
PEER=""
|
||||
fi
|
||||
|
||||
${MOCK} ip "${addrfam}" addr add "${addr}" ${PEER} dev "${IFACE}"
|
||||
done
|
||||
}
|
||||
|
||||
configure_gateways() {
|
||||
for gw in ${IF_GATEWAYS}; do
|
||||
addrfam=$(addr_family ${gw})
|
||||
${MOCK} ip "${addrfam}" route add default via "${gw}" ${VRF_TABLE} ${METRIC} dev "${IFACE}"
|
||||
done
|
||||
}
|
||||
|
||||
flush() {
|
||||
cmd="addr"
|
||||
arg="dev ${IFACE}"
|
||||
|
||||
${MOCK} ip ${cmd} flush ${arg}
|
||||
}
|
||||
|
||||
case "$PHASE" in
|
||||
up)
|
||||
configure_addresses add
|
||||
configure_gateways add
|
||||
;;
|
||||
down)
|
||||
flush
|
||||
;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
128
executor-scripts/linux/tunnel
Executable file
128
executor-scripts/linux/tunnel
Executable file
|
|
@ -0,0 +1,128 @@
|
|||
#!/bin/sh
|
||||
# Based on alpine's tunnel configuration script.
|
||||
# Copyright (c) 2017 Kaarle Ritvanen
|
||||
# Copyright (c) 2020 Ariadne Conill (extended for ifupdown-ng)
|
||||
# Copyright (c) 2021 Maximilian Wilhelm (make sure mode/type is 1st parameter, add more options)
|
||||
|
||||
[ -z "$IF_TUNNEL_LOCAL" -a -z "$IF_TUNNEL_LOCAL_DEV" ] && exit 1
|
||||
[ -z "$IF_TUNNEL_REMOTE" ] && exit 1
|
||||
[ -z "$IF_TUNNEL_MODE" ] && exit 1
|
||||
|
||||
[ -n "$VERBOSE" ] && set -x
|
||||
|
||||
yesno() {
|
||||
case "$1" in
|
||||
yes|1) echo 1 ;;
|
||||
*) echo 0 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Figure out address familiy
|
||||
FAMILY="4"
|
||||
|
||||
case "$IF_TUNNEL_MODE" in
|
||||
vti6|ipip6|ip6*)
|
||||
FAMILY="6"
|
||||
;;
|
||||
esac
|
||||
|
||||
# Figure out object type - gretap tunnels have to create using ip link
|
||||
# and therefor require 'type' keyword instead of 'mode'
|
||||
OBJECT="tunnel"
|
||||
TYPE_KW="mode"
|
||||
case "${IF_TUNNEL_MODE}" in
|
||||
*gretap)
|
||||
OBJECT="link"
|
||||
TYPE_KW="type"
|
||||
|
||||
# Strip possible "ip6" from tunnel mode
|
||||
TUNNEL_MODE="gretap"
|
||||
;;
|
||||
|
||||
*)
|
||||
# Store tunnel type/mode separaltely and unset input variable to exclude it
|
||||
# from PARAMS below
|
||||
TUNNEL_MODE="$IF_TUNNEL_MODE"
|
||||
unset IF_TUNNEL_MODE
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
# If 'tunnel-local <IP>' was not given but 'tunnel-local-dev <iface>' is given try
|
||||
# to figure out the IP of the underlay device (wrt the address family used for this
|
||||
# tunnel) and use this to set up the tunnel
|
||||
if [ ${PHASE} = "create" -a ! "${IF_TUNNEL_LOCAL}" -a "${IF_TUNNEL_LOCAL_DEV}" ]; then
|
||||
if [ "${FAMILY}" = "4" ]; then
|
||||
ip=$(ip -4 -brief addr show dev "${IF_TUNNEL_LOCAL_DEV}" 2>/dev/null | awk '{ print $3 }' | cut -d/ -f1)
|
||||
|
||||
# For IPv6 we try to use a non-temporary address (-> privacy extensions)
|
||||
else
|
||||
# Get all IPv6 addres configured on $IF_TUNNEL_LOCAL_DEV which are not
|
||||
# temporary (due to privacy extensions). We do not filter for "mgmtaddr"
|
||||
# "scope global" etc. as we don't want to make further assumptions on
|
||||
# whether a user wants to use a link local address configured to this interface.
|
||||
#
|
||||
# The assumption that a temporary address configured by PE isn't suited
|
||||
# to terminate a tunnel should hold in nearly all setups, I hope.
|
||||
ip=$(ip -6 addr show dev "${IF_TUNNEL_LOCAL_DEV}" -temporary | grep inet6 | head -n1 | awk '{ print $2 }' | cut -d/ -f1)
|
||||
fi
|
||||
|
||||
if [ ! "${ip}" ]; then
|
||||
echo "Unable to determine the IPv${FAMILIY} address of tunnel-local-dev ${IF_TUNNEL_LOCAL_DEV}!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
unset IF_TUNNEL_LOCAL_DEV
|
||||
export IF_TUNNEL_LOCAL="${ip}"
|
||||
fi
|
||||
|
||||
|
||||
# Handle boolean switches
|
||||
MORE_PARAMS=""
|
||||
if [ "${IF_TUNNEL_IGNORE_DF}" ]; then
|
||||
if $(yesno "${IF_TUNNEL_IGNORE_DF}"); then
|
||||
MORE_PARAMS="ignore-df"
|
||||
else
|
||||
MORE_PARAMS="noignore-df"
|
||||
fi
|
||||
|
||||
unset IF_TUNNEL_IGNORE_DF
|
||||
fi
|
||||
|
||||
if [ "${IF_TUNNEL_PMTUDISC}" ]; then
|
||||
if $(yesno "${IF_TUNNEL_PMTUDISC}"); then
|
||||
MORE_PARAMS="pmtudisc"
|
||||
else
|
||||
MORE_PARAMS="nopmtudisc"
|
||||
fi
|
||||
|
||||
unset IF_TUNNEL_PMTUDISC
|
||||
fi
|
||||
|
||||
|
||||
# Mangle residual IF_TUNNEL_* params into single string
|
||||
PARAMS=$(set | sed -E '
|
||||
s/^IF_TUNNEL_([A-Z0-9_]+)=(.+)/\1\n\2/
|
||||
ta
|
||||
d
|
||||
:a
|
||||
h
|
||||
y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/
|
||||
P
|
||||
g
|
||||
s/.*\n//
|
||||
')
|
||||
|
||||
[ "$PARAMS" ] || exit 0
|
||||
|
||||
case "$PHASE" in
|
||||
create)
|
||||
${MOCK} eval ip -$FAMILY $OBJECT add $IFACE $TYPE_KW $TUNNEL_MODE $PARAMS $MORE_PARAMS
|
||||
;;
|
||||
destroy)
|
||||
${MOCK} ip -$FAMILY $OBJECT del $IFACE
|
||||
;;
|
||||
depend)
|
||||
echo "${IF_TUNNEL_DEV}" "${IF_TUNNEL_LOCAL_DEV}"
|
||||
;;
|
||||
esac
|
||||
30
executor-scripts/linux/vrf
Executable file
30
executor-scripts/linux/vrf
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
handle_init() {
|
||||
${MOCK} /sbin/ip link $1 $IFACE type vrf table $IF_VRF_TABLE
|
||||
${MOCK} /sbin/ip rule $1 iif $IFACE table $IF_VRF_TABLE
|
||||
${MOCK} /sbin/ip rule $1 oif $IFACE table $IF_VRF_TABLE
|
||||
}
|
||||
|
||||
handle_member() {
|
||||
${MOCK} /sbin/ip link set $IFACE master $IF_VRF_MEMBER
|
||||
}
|
||||
|
||||
[ -n "$VERBOSE" ] && set -x
|
||||
|
||||
case "$PHASE" in
|
||||
create)
|
||||
[ -n "$IF_VRF_TABLE" ] && handle_init "add"
|
||||
;;
|
||||
pre-up)
|
||||
[ -n "$IF_VRF_MEMBER" ] && handle_member
|
||||
;;
|
||||
destroy)
|
||||
[ -n "$IF_VRF_TABLE" ] && handle_init "del"
|
||||
;;
|
||||
depend)
|
||||
echo "$IF_VRF_MEMBER"
|
||||
;;
|
||||
*)
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
96
executor-scripts/linux/vxlan
Executable file
96
executor-scripts/linux/vxlan
Executable 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
119
executor-scripts/linux/wifi
Executable 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
|
||||
15
executor-scripts/linux/wireguard
Executable file
15
executor-scripts/linux/wireguard
Executable file
|
|
@ -0,0 +1,15 @@
|
|||
#!/bin/sh
|
||||
[ -n "$VERBOSE" ] && set -x
|
||||
[ -z "$IF_WIREGUARD_CONFIG_PATH" ] && IF_WIREGUARD_CONFIG_PATH="/etc/wireguard/$IFACE.conf"
|
||||
|
||||
case "$PHASE" in
|
||||
create)
|
||||
${MOCK} ip link add $IFACE type wireguard
|
||||
;;
|
||||
pre-up)
|
||||
${MOCK} wg setconf $IFACE $IF_WIREGUARD_CONFIG_PATH
|
||||
;;
|
||||
destroy)
|
||||
${MOCK} ip link delete dev $IFACE
|
||||
;;
|
||||
esac
|
||||
5
executor-scripts/stub/bond
Executable file
5
executor-scripts/stub/bond
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/sh
|
||||
[ -z "$IF_BOND_MEMBERS" ] && IF_BOND_MEMBERS="$IF_BOND_SLAVES"
|
||||
case "$PHASE" in
|
||||
depend) echo "$IF_BOND_MEMBERS" ;;
|
||||
esac
|
||||
8
executor-scripts/stub/bridge
Executable file
8
executor-scripts/stub/bridge
Executable file
|
|
@ -0,0 +1,8 @@
|
|||
#!/bin/sh
|
||||
case "$PHASE" in
|
||||
depend)
|
||||
if [ "$IF_BRIDGE_PORTS" != "none" ]; then
|
||||
echo "$IF_BRIDGE_PORTS"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
114
libifupdown/compat.c
Normal file
114
libifupdown/compat.c
Normal 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
24
libifupdown/compat.h
Normal 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
75
libifupdown/config-file.c
Normal 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
35
libifupdown/config-file.h
Normal 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
|
||||
87
libifupdown/config-parser.c
Normal file
87
libifupdown/config-parser.c
Normal 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);
|
||||
}
|
||||
32
libifupdown/config-parser.h
Normal file
32
libifupdown/config-parser.h
Normal 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
|
||||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: wrapping linked lists to provide a naive dictionary
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -13,6 +14,7 @@
|
|||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "libifupdown/dict.h"
|
||||
|
|
@ -50,7 +52,41 @@ lif_dict_add(struct lif_dict *dict, const char *key, void *data)
|
|||
}
|
||||
|
||||
struct lif_dict_entry *
|
||||
lif_dict_find(struct lif_dict *dict, const char *key)
|
||||
lif_dict_add_once(struct lif_dict *dict, const char *key, void *data,
|
||||
lif_dict_cmp_t compar)
|
||||
{
|
||||
struct lif_list *existing = lif_dict_find_all(dict, key);
|
||||
if (existing != NULL)
|
||||
{
|
||||
bool found = false;
|
||||
struct lif_node *iter;
|
||||
LIF_LIST_FOREACH(iter, existing->head)
|
||||
{
|
||||
if (!compar(data, iter->data))
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
lif_list_free_nodes(existing);
|
||||
|
||||
if (found)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct lif_dict_entry *entry = calloc(1, sizeof *entry);
|
||||
|
||||
entry->key = strdup(key);
|
||||
entry->data = data;
|
||||
|
||||
lif_node_insert_tail(&entry->node, entry, &dict->list);
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
struct lif_dict_entry *
|
||||
lif_dict_find(const struct lif_dict *dict, const char *key)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
||||
|
|
@ -65,6 +101,31 @@ lif_dict_find(struct lif_dict *dict, const char *key)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct lif_list *
|
||||
lif_dict_find_all(const struct lif_dict *dict, const char *key)
|
||||
{
|
||||
struct lif_list *entries = calloc(1, sizeof *entries);
|
||||
struct lif_node *iter;
|
||||
|
||||
LIF_LIST_FOREACH(iter, dict->list.head)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
if (!strcmp(entry->key, key))
|
||||
{
|
||||
struct lif_node *new = calloc(1, sizeof *new);
|
||||
lif_node_insert_tail(new, entry->data, entries);
|
||||
}
|
||||
}
|
||||
|
||||
if (entries->length == 0)
|
||||
{
|
||||
free(entries);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
void
|
||||
lif_dict_delete(struct lif_dict *dict, const char *key)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: wrapping linked lists to provide a naive dictionary
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -34,10 +35,17 @@ struct lif_dict_entry {
|
|||
#define LIF_DICT_FOREACH_SAFE(iter, iter_next, dict) \
|
||||
LIF_LIST_FOREACH_SAFE((iter), (iter_next), (dict)->list.head)
|
||||
|
||||
#define LIF_DICT_FOREACH_REVERSE(iter, dict) \
|
||||
LIF_LIST_FOREACH_REVERSE((iter), (dict)->list.tail)
|
||||
|
||||
typedef int (*lif_dict_cmp_t)(const void *, const void *);
|
||||
|
||||
extern void lif_dict_init(struct lif_dict *dict);
|
||||
extern void lif_dict_fini(struct lif_dict *dict);
|
||||
extern struct lif_dict_entry *lif_dict_add(struct lif_dict *dict, const char *key, void *data);
|
||||
extern struct lif_dict_entry *lif_dict_find(struct lif_dict *dict, const char *key);
|
||||
extern struct lif_dict_entry *lif_dict_add_once(struct lif_dict *dict, const char *key, void *data, lif_dict_cmp_t compar);
|
||||
extern struct lif_dict_entry *lif_dict_find(const struct lif_dict *dict, const char *key);
|
||||
extern struct lif_list *lif_dict_find_all(const struct lif_dict *dict, const char *key);
|
||||
extern void lif_dict_delete(struct lif_dict *dict, const char *key);
|
||||
extern void lif_dict_delete_entry(struct lif_dict *dict, struct lif_dict_entry *entry);
|
||||
|
||||
|
|
|
|||
|
|
@ -20,14 +20,96 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <spawn.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <poll.h>
|
||||
|
||||
#include "libifupdown/execute.h"
|
||||
|
||||
#define SHELL "/bin/sh"
|
||||
|
||||
#if defined(__linux__)
|
||||
# include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
/* POSIX compatible fallback using waitpid(2) and usleep(3) */
|
||||
static inline bool
|
||||
lif_process_monitor_busyloop(pid_t child, int timeout_sec, int *status)
|
||||
{
|
||||
int ticks = 0;
|
||||
|
||||
while (ticks < timeout_sec * 20)
|
||||
{
|
||||
/* Ugly hack: most executors finish very quickly,
|
||||
* so give them a chance to finish before sleeping.
|
||||
*/
|
||||
usleep(50);
|
||||
|
||||
if (waitpid(child, status, WNOHANG) == child)
|
||||
return true;
|
||||
|
||||
usleep(49950);
|
||||
ticks++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(__linux__) && defined(__NR_pidfd_open)
|
||||
|
||||
/* TODO: remove this wrapper once musl and glibc gain pidfd_open() directly. */
|
||||
static inline int
|
||||
lif_pidfd_open(pid_t pid, unsigned int flags)
|
||||
{
|
||||
return syscall(__NR_pidfd_open, pid, flags);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
lif_process_monitor_procdesc(pid_t child, int timeout_sec, int *status)
|
||||
{
|
||||
int pidfd = lif_pidfd_open(child, 0);
|
||||
|
||||
/* pidfd_open() not available, fall back to busyloop */
|
||||
if (pidfd == -1 && errno == ENOSYS)
|
||||
return lif_process_monitor_busyloop(child, timeout_sec, status);
|
||||
|
||||
struct pollfd pfd = {
|
||||
.fd = pidfd,
|
||||
.events = POLLIN,
|
||||
};
|
||||
|
||||
if (poll(&pfd, 1, timeout_sec * 1000) < 1)
|
||||
return false;
|
||||
|
||||
waitpid(child, status, 0);
|
||||
close(pidfd);
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static inline bool
|
||||
lif_process_monitor(const char *cmdbuf, pid_t child, int timeout_sec)
|
||||
{
|
||||
int status;
|
||||
|
||||
#if defined(__linux__) && defined(__NR_pidfd_open)
|
||||
if (lif_process_monitor_procdesc(child, timeout_sec, &status))
|
||||
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
#else
|
||||
if (lif_process_monitor_busyloop(child, timeout_sec, &status))
|
||||
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
#endif
|
||||
|
||||
fprintf(stderr, "execution of '%s': timeout after %d seconds\n", cmdbuf, timeout_sec);
|
||||
kill(child, SIGKILL);
|
||||
waitpid(child, &status, 0);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...)
|
||||
{
|
||||
|
|
@ -53,8 +135,112 @@ lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const c
|
|||
return false;
|
||||
}
|
||||
|
||||
int status;
|
||||
waitpid(child, &status, 0);
|
||||
|
||||
return WIFEXITED(status) && WEXITSTATUS(status) == 0;
|
||||
return lif_process_monitor(cmdbuf, child, opts->timeout);
|
||||
}
|
||||
|
||||
bool
|
||||
lif_execute_fmt_with_result(const struct lif_execute_opts *opts, char *buf, size_t bufsize, char *const envp[], const char *fmt, ...)
|
||||
{
|
||||
char cmdbuf[4096];
|
||||
va_list va;
|
||||
|
||||
va_start(va, fmt);
|
||||
vsnprintf(cmdbuf, sizeof cmdbuf, fmt, va);
|
||||
va_end(va);
|
||||
|
||||
pid_t child;
|
||||
char *argv[] = { SHELL, "-c", cmdbuf, NULL };
|
||||
|
||||
if (opts->verbose)
|
||||
puts(cmdbuf);
|
||||
|
||||
if (opts->mock)
|
||||
return true;
|
||||
|
||||
int pipefds[2];
|
||||
if (pipe(pipefds) < 0)
|
||||
{
|
||||
fprintf(stderr, "execute '%s': %s\n", cmdbuf, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
posix_spawn_file_actions_t file_actions;
|
||||
|
||||
posix_spawn_file_actions_init(&file_actions);
|
||||
posix_spawn_file_actions_addclose(&file_actions, pipefds[0]);
|
||||
posix_spawn_file_actions_adddup2(&file_actions, pipefds[1], 1);
|
||||
posix_spawn_file_actions_addclose(&file_actions, pipefds[1]);
|
||||
|
||||
if (posix_spawn(&child, SHELL, &file_actions, NULL, argv, envp) != 0)
|
||||
{
|
||||
fprintf(stderr, "execute '%s': %s\n", cmdbuf, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
posix_spawn_file_actions_destroy(&file_actions);
|
||||
|
||||
close(pipefds[1]);
|
||||
|
||||
struct pollfd pfd = {
|
||||
.fd = pipefds[0],
|
||||
.events = POLLIN
|
||||
};
|
||||
|
||||
if (poll(&pfd, 1, -1) < 1)
|
||||
goto no_result;
|
||||
|
||||
if (read(pipefds[0], buf, bufsize) < 0)
|
||||
{
|
||||
fprintf(stderr, "reading from pipe: %s\n", strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
no_result:
|
||||
return lif_process_monitor(cmdbuf, child, opts->timeout);
|
||||
}
|
||||
|
||||
bool
|
||||
lif_file_is_executable(const char *path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st))
|
||||
return false;
|
||||
|
||||
if (!S_ISREG(st.st_mode))
|
||||
return false;
|
||||
|
||||
return !access(path, X_OK);
|
||||
}
|
||||
|
||||
bool
|
||||
lif_maybe_run_executor(const struct lif_execute_opts *opts, char *const envp[], const char *executor, const char *phase, const char *lifname)
|
||||
{
|
||||
if (opts->verbose)
|
||||
fprintf(stderr, "ifupdown: %s: attempting to run %s executor for phase %s\n", lifname, executor, phase);
|
||||
|
||||
char pathbuf[4096];
|
||||
|
||||
snprintf(pathbuf, sizeof pathbuf, "%s/%s", opts->executor_path, executor);
|
||||
|
||||
if (!lif_file_is_executable(pathbuf))
|
||||
return true;
|
||||
|
||||
return lif_execute_fmt(opts, envp, "%s", pathbuf);
|
||||
}
|
||||
|
||||
bool
|
||||
lif_maybe_run_executor_with_result(const struct lif_execute_opts *opts, char *const envp[], const char *executor, char *buf, size_t bufsize, const char *phase, const char *lifname)
|
||||
{
|
||||
if (opts->verbose)
|
||||
fprintf(stderr, "ifupdown: %s: attempting to run %s executor for phase %s\n", lifname, executor, phase);
|
||||
|
||||
char pathbuf[4096];
|
||||
|
||||
snprintf(pathbuf, sizeof pathbuf, "%s/%s", opts->executor_path, executor);
|
||||
|
||||
if (!lif_file_is_executable(pathbuf))
|
||||
return true;
|
||||
|
||||
return lif_execute_fmt_with_result(opts, buf, bufsize, envp, "%s", pathbuf);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,8 +22,18 @@
|
|||
struct lif_execute_opts {
|
||||
bool verbose;
|
||||
bool mock;
|
||||
bool no_lock;
|
||||
bool force;
|
||||
const char *executor_path;
|
||||
const char *interfaces_file;
|
||||
const char *state_file;
|
||||
int timeout;
|
||||
};
|
||||
|
||||
extern bool lif_execute_fmt(const struct lif_execute_opts *opts, char *const envp[], const char *fmt, ...);
|
||||
extern bool lif_execute_fmt_with_result(const struct lif_execute_opts *opts, char *buf, size_t bufsize, char *const envp[], const char *fmt, ...);
|
||||
extern bool lif_file_is_executable(const char *path);
|
||||
extern bool lif_maybe_run_executor(const struct lif_execute_opts *opts, char *const envp[], const char *executor, const char *phase, const char *lifname);
|
||||
extern bool lif_maybe_run_executor_with_result(const struct lif_execute_opts *opts, char *const envp[], const char *executor, char *buf, size_t bufsize, const char *phase, const char *lifname);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: /etc/network/interfaces parser
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -14,128 +15,519 @@
|
|||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "libifupdown/interface-file.h"
|
||||
#include "libifupdown/fgetline.h"
|
||||
#include "libifupdown/tokenize.h"
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include "libifupdown/libifupdown.h"
|
||||
|
||||
/* internally rewrite problematic ifupdown2 tokens to ifupdown-ng equivalents */
|
||||
struct remap_token {
|
||||
const char *token;
|
||||
const char *alternative;
|
||||
};
|
||||
|
||||
/* this list must be in alphabetical order for bsearch */
|
||||
static const struct remap_token tokens[] = {
|
||||
{"bond-ad-sys-priority", "bond-ad-actor-sys-prio"}, /* ifupdown2 */
|
||||
{"bond-slaves", "bond-members"}, /* legacy ifupdown, ifupdown2 */
|
||||
{"client", "dhcp-client-id"}, /* legacy ifupdown */
|
||||
{"driver-message-level", "ethtool-msglvl"}, /* Debian ethtool integration */
|
||||
{"endpoint", "tunnel-remote"}, /* legacy ifupdown */
|
||||
{"ethernet-autoneg", "ethtool-ethernet-autoneg"}, /* Debian ethtool integration */
|
||||
{"ethernet-pause-autoneg", "ethtool-pause-autoneg"}, /* Debian ethtool integration */
|
||||
{"ethernet-pause-rx", "ethtool-pause-rx"}, /* Debian ethtool integration */
|
||||
{"ethernet-pause-tx", "ethtool-pause-tx"}, /* Debian ethtool integration */
|
||||
{"ethernet-port", "ethtool-ethernet-port"}, /* Debian ethtool integration */
|
||||
{"ethernet-wol", "ethtool-ethernet-wol"}, /* Debian ethtool integration */
|
||||
{"gro-offload", "ethtool-offload-gro"}, /* ifupdown2 */
|
||||
{"gso-offload", "ethtool-offload-gso"}, /* ifupdown2 */
|
||||
{"hardware-dma-ring-rx", "ethtool-dma-ring-rx"}, /* Debian ethtool integration */
|
||||
{"hardware-dma-ring-rx-jumbo", "ethtool-dma-ring-rx-jumbo"}, /* Debian ethtool integration */
|
||||
{"hardware-dma-ring-rx-mini", "ethtool-dma-ring-rx-mini"}, /* Debian ethtool integration */
|
||||
{"hardware-dma-ring-tx", "ethtool-dma-ring-tx"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-adaptive-rx", "ethtool-coalesce-adaptive-rx"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-adaptive-tx", "ethtool-coalesce-adaptive-tx"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-pkt-rate-high", "ethtool-coalesce-pkt-rate-high"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-pkt-rate-low", "ethtool-coalesce-pkt-rate-low"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-frames", "ethtool-coalesce-rx-frames"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-frames-high", "ethtool-coalesce-rx-frames-high"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-frames-irq", "ethtool-coalesce-rx-frames-irq"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-frames-low", "ethtool-coalesce-rx-frames-low"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-usecs", "ethtool-coalesce-rx-usecs"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-usecs-high", "ethtool-coalesce-rx-usecs-high"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-usecs-irq", "ethtool-coalesce-rx-usecs-irq"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-rx-usecs-low", "ethtool-coalesce-rx-usecs-low"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-sample-interval", "ethtool-coalesce-sample-interval"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-stats-block-usecs", "ethtool-coalesce-stats-block-usecs"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-frames", "ethtool-coalesce-tx-frames"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-frames-high", "ethtool-coalesce-tx-frames-high"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-frames-irq", "ethtool-coalesce-tx-frames-irq"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-frames-low", "ethtool-coalesce-tx-frames-low"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-usecs", "ethtool-coalesce-tx-usecs"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-usecs-high", "ethtool-coalesce-tx-usecs-high"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-usecs-irq", "ethtool-coalesce-tx-usecs-irq"}, /* Debian ethtool integration */
|
||||
{"hardware-irq-coalesce-tx-usecs-low", "ethtool-coalesce-tx-usecs-low"}, /* Debian ethtool integration */
|
||||
{"hostname", "dhcp-hostname"}, /* legacy ifupdown */
|
||||
{"key", "tunnel-key"}, /* legacy ifupdown */
|
||||
{"leasetime", "dhcp-leasetime"}, /* legacy ifupdown */
|
||||
{"link-autoneg", "ethtool-ethernet-autoneg"}, /* ifupdown2 */
|
||||
{"link-duplex", "ethtool-link-duplex"}, /* Debian ethtool integration */
|
||||
{"link-fec", "ethtool-link-fec"}, /* ifupdown2 */
|
||||
{"link-speed", "ethtool-link-speed"}, /* Debian ethtool integration */
|
||||
{"local", "tunnel-local"}, /* legacy ifupdown */
|
||||
{"lro-offload", "ethtool-offload-lro"}, /* ifupdown2 */
|
||||
{"mode", "tunnel-mode"}, /* legacy ifupdown */
|
||||
{"offload-gro", "ethtool-offload-gro"}, /* Debian ethtool integration */
|
||||
{"offload-gso", "ethtool-offload-gso"}, /* Debian ethtool integration */
|
||||
{"offload-lro", "ethtool-offload-lro"}, /* Debian ethtool integration */
|
||||
{"offload-rx", "ethtool-offload-rx"}, /* Debian ethtool integration */
|
||||
{"offload-sg", "ethtool-offload-sg"}, /* Debian ethtool integration */
|
||||
{"offload-tso", "ethtool-offload-tso"}, /* Debian ethtool integration */
|
||||
{"offload-tx", "ethtool-offload-tx"}, /* Debian ethtool integration */
|
||||
{"offload-ufo", "ethtool-offload-ufo"}, /* Debian ethtool integration */
|
||||
{"pointopoint", "point-to-point"}, /* legacy ifupdown, ifupdown2 */
|
||||
{"provider", "ppp-provider"}, /* legacy ifupdown, ifupdown2 */
|
||||
{"script", "dhcp-script"}, /* legacy ifupdown */
|
||||
{"rx-offload", "ethtool-offload-rx"}, /* ifupdown2 */
|
||||
{"tso-offload", "ethtool-offload-tso"}, /* ifupdown2 */
|
||||
{"ttl", "tunnel-ttl"}, /* legacy ifupdown */
|
||||
{"tunnel-endpoint", "tunnel-remote"}, /* ifupdown2 */
|
||||
{"tunnel-physdev", "tunnel-dev"}, /* ifupdown2 */
|
||||
{"tx-offload", "ethtool-offload-tx"}, /* ifupdown2 */
|
||||
{"ufo-offload", "ethtool-offload-ufo"}, /* ifupdown2 */
|
||||
{"vendor", "dhcp-vendor"}, /* legacy ifupdown */
|
||||
{"vrf", "vrf-member"}, /* ifupdown2 */
|
||||
{"vxlan-local-tunnelip", "vxlan-local-ip"}, /* ifupdown2 */
|
||||
{"vxlan-remote-group", "vxlan-peer-group"}, /* ifupdown-ng */
|
||||
{"vxlan-remoteip", "vxlan-peer-ips"}, /* ifupdown2 */
|
||||
{"vxlan-remote-ip", "vxlan-peer-ips"}, /* ifupdown-ng */
|
||||
{"vxlan-svcnodeip", "vxlan-peer-group"}, /* ifupdown2 */
|
||||
};
|
||||
|
||||
static int
|
||||
token_cmp(const void *a, const void *b)
|
||||
{
|
||||
const char *key = a;
|
||||
const struct remap_token *token = b;
|
||||
|
||||
return strcmp(key, token->token);
|
||||
}
|
||||
|
||||
static char *
|
||||
maybe_remap_token(const char *token)
|
||||
{
|
||||
const struct remap_token *tok = NULL;
|
||||
static char tokbuf[4096];
|
||||
|
||||
tok = bsearch(token, tokens, ARRAY_SIZE(tokens), sizeof(*tokens), token_cmp);
|
||||
strlcpy(tokbuf, tok != NULL ? tok->alternative : token, sizeof tokbuf);
|
||||
|
||||
return tokbuf;
|
||||
}
|
||||
|
||||
static void
|
||||
report_error(struct lif_interface_file_parse_state *state, const char *errfmt, ...)
|
||||
{
|
||||
char errbuf[4096];
|
||||
|
||||
va_list va;
|
||||
va_start(va, errfmt);
|
||||
vsnprintf(errbuf, sizeof errbuf, errfmt, va);
|
||||
va_end(va);
|
||||
|
||||
fprintf(stderr, "%s:%zu: %s\n", state->cur_filename, state->cur_lineno, errbuf);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_address(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
(void) token;
|
||||
|
||||
char *addr = lif_next_token(&bufp);
|
||||
|
||||
if (state->cur_iface == NULL)
|
||||
{
|
||||
report_error(state, "%s '%s' without interface", token, addr);
|
||||
/* Ignore this address, but don't fail hard */
|
||||
return true;
|
||||
}
|
||||
|
||||
lif_interface_address_add(state->cur_iface, addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_auto(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
(void) token;
|
||||
|
||||
char *ifname = lif_next_token(&bufp);
|
||||
if (!*ifname && state->cur_iface == NULL)
|
||||
{
|
||||
report_error(state, "auto without interface");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
state->cur_iface = lif_interface_collection_find(state->collection, ifname);
|
||||
if (state->cur_iface == NULL)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!state->cur_iface->is_template)
|
||||
state->cur_iface->is_auto = true;
|
||||
|
||||
if (state->cur_iface->is_auto)
|
||||
state->cur_iface->is_explicit = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_gateway(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
(void) token;
|
||||
|
||||
char *addr = lif_next_token(&bufp);
|
||||
|
||||
if (state->cur_iface == NULL)
|
||||
{
|
||||
report_error(state, "%s '%s' without interface", token, addr);
|
||||
/* Ignore this gateway, but don't fail hard */
|
||||
return true;
|
||||
}
|
||||
|
||||
lif_interface_use_executor(state->cur_iface, "static");
|
||||
lif_dict_add(&state->cur_iface->vars, token, strdup(addr));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_generic(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
if (state->cur_iface == NULL)
|
||||
return true;
|
||||
|
||||
token = maybe_remap_token(token);
|
||||
|
||||
/* This smells like a bridge */
|
||||
if (strcmp(token, "bridge-ports") == 0)
|
||||
state->cur_iface->is_bridge = true;
|
||||
|
||||
/* Skip any leading whitespaces in value for <token> */
|
||||
while (isspace (*bufp))
|
||||
bufp++;
|
||||
|
||||
lif_dict_add(&state->cur_iface->vars, token, strdup(bufp));
|
||||
|
||||
if (!lif_config.auto_executor_selection)
|
||||
return true;
|
||||
|
||||
/* Check if token looks like <word1>-<word*> and assume <word1> is an addon */
|
||||
char *word_end = strchr(token, '-');
|
||||
if (word_end != NULL)
|
||||
{
|
||||
/* Copy word1 to not mangle *token */
|
||||
char *addon = strndup(token, word_end - token);
|
||||
lif_interface_use_executor(state->cur_iface, addon);
|
||||
free(addon);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_hostname(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
char *hostname = lif_next_token(&bufp);
|
||||
|
||||
if (state->cur_iface == NULL)
|
||||
{
|
||||
report_error(state, "%s '%s' without interface", token, hostname);
|
||||
/* Ignore this hostname, but don't fail hard */
|
||||
return true;
|
||||
}
|
||||
|
||||
lif_dict_delete(&state->cur_iface->vars, "dhcp-hostname");
|
||||
lif_dict_add(&state->cur_iface->vars, "dhcp-hostname", strdup(hostname));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool handle_inherit(struct lif_interface_file_parse_state *state, char *token, char *bufp);
|
||||
|
||||
static bool
|
||||
handle_iface(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
char *ifname = lif_next_token(&bufp);
|
||||
if (!*ifname)
|
||||
{
|
||||
report_error(state, "%s without any other tokens", token);
|
||||
/* This is broken but not fatal */
|
||||
return true;
|
||||
}
|
||||
|
||||
/* if we have a current interface, call lif_interface_finalize to finalize any
|
||||
* address properties by converting them to CIDR and flushing the netmask property.
|
||||
*/
|
||||
if (state->cur_iface != NULL)
|
||||
lif_interface_finalize(state->cur_iface);
|
||||
|
||||
state->cur_iface = lif_interface_collection_find(state->collection, ifname);
|
||||
if (state->cur_iface == NULL)
|
||||
{
|
||||
report_error(state, "could not upsert interface %s", ifname);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* mark the state->cur_iface as a template iface if `template` keyword
|
||||
* is used.
|
||||
*/
|
||||
if (!strcmp(token, "template"))
|
||||
{
|
||||
state->cur_iface->is_auto = false;
|
||||
state->cur_iface->is_template = true;
|
||||
}
|
||||
|
||||
/* in original ifupdown config, we can have "inet loopback"
|
||||
* or "inet dhcp" or such to designate hints. lets pick up
|
||||
* those hints here.
|
||||
*/
|
||||
token = lif_next_token(&bufp);
|
||||
while (*token)
|
||||
{
|
||||
if (!strcmp(token, "dhcp"))
|
||||
lif_interface_use_executor(state->cur_iface, "dhcp");
|
||||
else if (!strcmp(token, "ppp"))
|
||||
lif_interface_use_executor(state->cur_iface, "ppp");
|
||||
else if (!strcmp(token, "inherits"))
|
||||
{
|
||||
if (!handle_inherit(state, token, bufp))
|
||||
return false;
|
||||
}
|
||||
|
||||
token = lif_next_token(&bufp);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_inherit(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
char *target = lif_next_token(&bufp);
|
||||
|
||||
if (state->cur_iface == NULL)
|
||||
{
|
||||
report_error(state, "%s '%s' without interface", token, target);
|
||||
/* This is broken but not fatal */
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!*target)
|
||||
{
|
||||
report_error(state, "iface %s: unspecified inherit target", state->cur_iface->ifname);
|
||||
/* Mark this interface as errornous but carry on */
|
||||
state->cur_iface->has_config_error = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
struct lif_interface *parent = lif_interface_collection_find(state->collection, target);
|
||||
if (parent == NULL)
|
||||
{
|
||||
report_error(state, "iface %s: could not inherit from %s: not found",
|
||||
state->cur_iface->ifname, target);
|
||||
/* Mark this interface as errornous but carry on */
|
||||
state->cur_iface->has_config_error = true;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
if (!lif_config.allow_any_iface_as_template && !parent->is_template)
|
||||
{
|
||||
report_error(state, "iface %s: could not inherit from %ss: inheritence from non-template interface not allowed",
|
||||
state->cur_iface->ifname, target);
|
||||
/* Mark this interface as errornous but carry on */
|
||||
state->cur_iface->has_config_error = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!lif_interface_collection_inherit(state->cur_iface, parent))
|
||||
{
|
||||
report_error(state, "iface %s: could not inherit from %s", state->cur_iface->ifname, target);
|
||||
/* Mark this interface as errornous but carry on */
|
||||
state->cur_iface->has_config_error = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_source(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
(void) token;
|
||||
|
||||
char *source_filename = lif_next_token(&bufp);
|
||||
if (!*source_filename)
|
||||
{
|
||||
report_error(state, "missing filename to source");
|
||||
/* Broken but not fatal */
|
||||
return true;
|
||||
}
|
||||
|
||||
return lif_interface_file_parse(state, source_filename);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_source_directory(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
(void) token;
|
||||
|
||||
char *source_directory = lif_next_token(&bufp);
|
||||
if (!*source_directory)
|
||||
{
|
||||
report_error(state, "missing directory to source");
|
||||
/* Broken but not fatal */
|
||||
return true;
|
||||
}
|
||||
|
||||
DIR *source_dir = opendir(source_directory);
|
||||
if (source_dir == NULL)
|
||||
{
|
||||
report_error(state, "while opening directory %s: %s", source_directory, strerror(errno));
|
||||
/* Broken but not fatal */
|
||||
return true;
|
||||
}
|
||||
|
||||
struct dirent *dirent_p;
|
||||
for (dirent_p = readdir(source_dir); dirent_p != NULL; dirent_p = readdir(source_dir))
|
||||
{
|
||||
if (dirent_p->d_type != DT_REG)
|
||||
continue;
|
||||
|
||||
char pathbuf[4096];
|
||||
snprintf(pathbuf, sizeof pathbuf, "%s/%s", source_directory, dirent_p->d_name);
|
||||
|
||||
if (!lif_interface_file_parse(state, pathbuf))
|
||||
{
|
||||
closedir(source_dir);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(source_dir);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_use(struct lif_interface_file_parse_state *state, char *token, char *bufp)
|
||||
{
|
||||
char *executor = lif_next_token(&bufp);
|
||||
|
||||
if (state->cur_iface == NULL)
|
||||
{
|
||||
report_error(state, "%s '%s' without interface", token, executor);
|
||||
/* Broken but not fatal */
|
||||
return true;
|
||||
}
|
||||
|
||||
lif_interface_use_executor(state->cur_iface, executor);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* map keywords to parser functions */
|
||||
struct parser_keyword {
|
||||
const char *token;
|
||||
bool (*handle)(struct lif_interface_file_parse_state *state, char *token, char *bufp);
|
||||
};
|
||||
|
||||
static const struct parser_keyword keywords[] = {
|
||||
{"address", handle_address},
|
||||
{"auto", handle_auto},
|
||||
{"dhcp-hostname", handle_hostname},
|
||||
{"gateway", handle_gateway},
|
||||
{"hostname", handle_hostname},
|
||||
{"iface", handle_iface},
|
||||
{"inherit", handle_inherit},
|
||||
{"interface", handle_iface},
|
||||
{"source", handle_source},
|
||||
{"source-directory", handle_source_directory},
|
||||
{"template", handle_iface},
|
||||
{"use", handle_use},
|
||||
};
|
||||
|
||||
static int
|
||||
keyword_cmp(const void *a, const void *b)
|
||||
{
|
||||
const char *key = a;
|
||||
const struct parser_keyword *token = b;
|
||||
|
||||
return strcmp(key, token->token);
|
||||
}
|
||||
|
||||
bool
|
||||
lif_interface_file_parse(struct lif_dict *collection, const char *filename)
|
||||
lif_interface_file_parse(struct lif_interface_file_parse_state *state, const char *filename)
|
||||
{
|
||||
lif_interface_collection_init(collection);
|
||||
struct lif_interface *cur_iface = NULL;
|
||||
struct lif_dict_entry *entry = lif_dict_find(&state->loaded, filename);
|
||||
if (entry != NULL)
|
||||
{
|
||||
report_error(state, "skipping already included file %s", filename);
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE *f = fopen(filename, "r");
|
||||
if (f == NULL)
|
||||
return false;
|
||||
|
||||
const char *old_filename = state->cur_filename;
|
||||
state->cur_filename = filename;
|
||||
|
||||
size_t old_lineno = state->cur_lineno;
|
||||
state->cur_lineno = 0;
|
||||
|
||||
lif_dict_add(&state->loaded, filename, NULL);
|
||||
|
||||
char linebuf[4096];
|
||||
while (lif_fgetline(linebuf, sizeof linebuf, f) != NULL)
|
||||
{
|
||||
state->cur_lineno++;
|
||||
|
||||
char *bufp = linebuf;
|
||||
char *token = lif_next_token(&bufp);
|
||||
|
||||
if (!*token || !isalpha(*token))
|
||||
continue;
|
||||
|
||||
if (!strcmp(token, "source"))
|
||||
const struct parser_keyword *parserkw =
|
||||
bsearch(token, keywords, ARRAY_SIZE(keywords), sizeof(*keywords), keyword_cmp);
|
||||
|
||||
if (parserkw != NULL)
|
||||
{
|
||||
char *source_filename = lif_next_token(&bufp);
|
||||
if (!*source_filename)
|
||||
if (!parserkw->handle(state, token, bufp))
|
||||
goto parse_error;
|
||||
|
||||
if (!strcmp(filename, source_filename))
|
||||
{
|
||||
fprintf(stderr, "%s: attempt to source %s would create infinite loop\n",
|
||||
filename, source_filename);
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
lif_interface_file_parse(collection, source_filename);
|
||||
}
|
||||
else if (!strcmp(token, "auto"))
|
||||
{
|
||||
char *ifname = lif_next_token(&bufp);
|
||||
if (!*ifname && cur_iface == NULL)
|
||||
goto parse_error;
|
||||
else
|
||||
{
|
||||
cur_iface = lif_interface_collection_find(collection, ifname);
|
||||
if (cur_iface == NULL)
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
cur_iface->is_auto = true;
|
||||
}
|
||||
else if (!strcmp(token, "iface"))
|
||||
{
|
||||
char *ifname = lif_next_token(&bufp);
|
||||
if (!*ifname)
|
||||
goto parse_error;
|
||||
|
||||
cur_iface = lif_interface_collection_find(collection, ifname);
|
||||
if (cur_iface == NULL)
|
||||
goto parse_error;
|
||||
|
||||
/* in original ifupdown config, we can have "inet loopback"
|
||||
* or "inet dhcp" or such to designate hints. lets pick up
|
||||
* those hints here.
|
||||
*/
|
||||
char *inet_type = lif_next_token(&bufp);
|
||||
if (!*inet_type)
|
||||
continue;
|
||||
|
||||
char *hint = lif_next_token(&bufp);
|
||||
if (!*hint)
|
||||
continue;
|
||||
|
||||
if (!strcmp(hint, "dhcp"))
|
||||
{
|
||||
cur_iface->is_dhcp = true;
|
||||
lif_dict_add(&cur_iface->vars, "use", strdup("dhcp"));
|
||||
}
|
||||
}
|
||||
else if (!strcmp(token, "use"))
|
||||
{
|
||||
char *executor = lif_next_token(&bufp);
|
||||
|
||||
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
|
||||
if (!strcmp(executor, "dhcp"))
|
||||
cur_iface->is_dhcp = true;
|
||||
else if (!strcmp(executor, "loopback"))
|
||||
cur_iface->is_loopback = true;
|
||||
else if (!strcmp(executor, "bridge"))
|
||||
cur_iface->is_bridge = true;
|
||||
else if (!strcmp(executor, "bond"))
|
||||
cur_iface->is_bond = true;
|
||||
|
||||
lif_dict_add(&cur_iface->vars, token, strdup(executor));
|
||||
}
|
||||
else if (!strcmp(token, "address"))
|
||||
{
|
||||
char *addr = lif_next_token(&bufp);
|
||||
|
||||
if (cur_iface == NULL)
|
||||
{
|
||||
fprintf(stderr, "%s: address '%s' without interface\n", filename, addr);
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
lif_interface_address_add(cur_iface, addr);
|
||||
}
|
||||
else if (cur_iface != NULL)
|
||||
{
|
||||
lif_dict_add(&cur_iface->vars, token, strdup(bufp));
|
||||
}
|
||||
else if (!handle_generic(state, token, bufp))
|
||||
goto parse_error;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
|
||||
/* finalize any open interface */
|
||||
if (state->cur_iface != NULL)
|
||||
lif_interface_finalize(state->cur_iface);
|
||||
|
||||
state->cur_filename = old_filename;
|
||||
state->cur_lineno = old_lineno;
|
||||
return true;
|
||||
|
||||
parse_error:
|
||||
fprintf(stderr, "libifupdown: %s: failed to parse line \"%s\"\n",
|
||||
filename, linebuf);
|
||||
fclose(f);
|
||||
state->cur_filename = old_filename;
|
||||
state->cur_lineno = old_lineno;
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: interface management
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -15,7 +16,9 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/utsname.h>
|
||||
#include "libifupdown/interface.h"
|
||||
#include "libifupdown/config-file.h"
|
||||
|
||||
bool
|
||||
lif_address_parse(struct lif_address *address, const char *presentation)
|
||||
|
|
@ -56,12 +59,75 @@ lif_address_unparse(const struct lif_address *address, char *buf, size_t buflen,
|
|||
return true;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
count_set_bits(const char *netmask)
|
||||
{
|
||||
/* netmask set to CIDR length */
|
||||
if (strchr(netmask, '.') == NULL)
|
||||
return strtol(netmask, NULL, 10);
|
||||
|
||||
size_t r = 0;
|
||||
struct in_addr in;
|
||||
|
||||
if (inet_pton(AF_INET, netmask, &in) == 0)
|
||||
return r;
|
||||
|
||||
/* take the IP, put it in host endian order, and
|
||||
* flip it so that all the set bits are set to the right.
|
||||
* then we can simply count down from 32 and right-shift
|
||||
* until the bit field is all zero.
|
||||
*/
|
||||
unsigned int bits = htonl(in.s_addr);
|
||||
for (bits = ~bits, r = 32; bits; bits >>= 1, r--)
|
||||
;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
determine_interface_netmask(const struct lif_interface *iface, const struct lif_address *addr)
|
||||
{
|
||||
/* if netmask is not set, default to /24 or /64, ifupdown does so too */
|
||||
size_t netmask = addr->domain == AF_INET6 ? 64 : 24;
|
||||
|
||||
struct lif_dict_entry *entry = lif_dict_find(&iface->vars, "netmask");
|
||||
if (entry != NULL)
|
||||
netmask = count_set_bits(entry->data);
|
||||
|
||||
return netmask;
|
||||
}
|
||||
|
||||
bool
|
||||
lif_address_format_cidr(const struct lif_interface *iface, struct lif_dict_entry *entry, char *buf, size_t buflen)
|
||||
{
|
||||
struct lif_address *addr = entry->data;
|
||||
size_t orig_netmask = addr->netmask;
|
||||
|
||||
if (!addr->netmask)
|
||||
addr->netmask = determine_interface_netmask(iface, addr);
|
||||
|
||||
if (!lif_address_unparse(addr, buf, buflen, true))
|
||||
{
|
||||
addr->netmask = orig_netmask;
|
||||
return false;
|
||||
}
|
||||
|
||||
addr->netmask = orig_netmask;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
lif_interface_init(struct lif_interface *interface, const char *ifname)
|
||||
{
|
||||
memset(interface, '\0', sizeof *interface);
|
||||
|
||||
interface->ifname = strdup(ifname);
|
||||
|
||||
lif_interface_use_executor(interface, "link");
|
||||
|
||||
/* keep the 'vlan' executor as a config hint for backwards compatibility */
|
||||
if (strchr(ifname, '.') != NULL)
|
||||
lif_interface_use_executor(interface, "vlan");
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -75,7 +141,10 @@ lif_interface_address_add(struct lif_interface *interface, const char *address)
|
|||
return false;
|
||||
}
|
||||
|
||||
lif_interface_use_executor(interface, "static");
|
||||
|
||||
lif_dict_add(&interface->vars, "address", addr);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -125,6 +194,61 @@ lif_interface_fini(struct lif_interface *interface)
|
|||
free(interface->ifname);
|
||||
}
|
||||
|
||||
void
|
||||
lif_interface_use_executor(struct lif_interface *interface, const char *executor)
|
||||
{
|
||||
char *exec_addon = strdup(executor);
|
||||
|
||||
if (lif_dict_add_once(&interface->vars, "use", exec_addon, (lif_dict_cmp_t) strcmp) == NULL)
|
||||
free(exec_addon);
|
||||
|
||||
/* pass requires as compatibility env vars to appropriate executors (bridge, bond) */
|
||||
if (!strcmp(executor, "bridge"))
|
||||
interface->is_bridge = true;
|
||||
else if (!strcmp(executor, "bond"))
|
||||
interface->is_bond = true;
|
||||
|
||||
if (strcmp(executor, "dhcp") || !lif_config.use_hostname_for_dhcp)
|
||||
return;
|
||||
|
||||
/* learn a reasonable default hostname */
|
||||
struct utsname un;
|
||||
if (uname(&un) < 0)
|
||||
return;
|
||||
|
||||
lif_dict_add(&interface->vars, "dhcp-hostname", strdup(un.nodename));
|
||||
}
|
||||
|
||||
void
|
||||
lif_interface_finalize(struct lif_interface *interface)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
||||
/* convert all addresses to CIDR notation. */
|
||||
LIF_DICT_FOREACH(iter, &interface->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
if (strcmp(entry->key, "address"))
|
||||
continue;
|
||||
|
||||
struct lif_address *addr = entry->data;
|
||||
|
||||
if (!addr->netmask)
|
||||
addr->netmask = determine_interface_netmask(interface, addr);
|
||||
}
|
||||
|
||||
/* with all addresses converted to CIDR, netmask property is no longer needed. */
|
||||
struct lif_dict_entry *entry = lif_dict_find(&interface->vars, "netmask");
|
||||
|
||||
if (entry != NULL)
|
||||
{
|
||||
free(entry->data);
|
||||
|
||||
lif_dict_delete_entry(&interface->vars, entry);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
lif_interface_collection_init(struct lif_dict *collection)
|
||||
{
|
||||
|
|
@ -134,10 +258,9 @@ lif_interface_collection_init(struct lif_dict *collection)
|
|||
|
||||
/* always enable loopback interface as part of a collection */
|
||||
if_lo = lif_interface_collection_find(collection, "lo");
|
||||
if_lo->is_auto = if_lo->is_loopback = true;
|
||||
lif_dict_add(&if_lo->vars, "use", strdup("loopback"));
|
||||
|
||||
lif_interface_address_add(if_lo, "127.0.0.1/8");
|
||||
if_lo->is_auto = true;
|
||||
if_lo->is_explicit = true;
|
||||
lif_interface_use_executor(if_lo, "loopback");
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -208,3 +331,41 @@ lif_interface_collection_delete(struct lif_dict *collection, struct lif_interfac
|
|||
|
||||
lif_dict_delete_entry(collection, entry);
|
||||
}
|
||||
|
||||
bool
|
||||
lif_interface_collection_inherit(struct lif_interface *interface, struct lif_interface *parent)
|
||||
{
|
||||
/* maybe convert any interface we are inheriting from into a template */
|
||||
if (lif_config.implicit_template_conversion)
|
||||
parent->is_template = true;
|
||||
|
||||
lif_dict_add(&interface->vars, "inherit", strdup(parent->ifname));
|
||||
interface->is_bond = parent->is_bond;
|
||||
interface->is_bridge = parent->is_bridge;
|
||||
|
||||
/* copy the variables */
|
||||
struct lif_node *iter;
|
||||
LIF_DICT_FOREACH(iter, &parent->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
if (!strcmp(entry->key, "address"))
|
||||
{
|
||||
struct lif_address *addr = calloc(1, sizeof *addr);
|
||||
struct lif_address *other_addr = entry->data;
|
||||
|
||||
memcpy(addr, other_addr, sizeof *addr);
|
||||
|
||||
lif_dict_add(&interface->vars, entry->key, addr);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *value = strdup(entry->data);
|
||||
|
||||
if (lif_dict_add_once(&interface->vars, entry->key, value, (lif_dict_cmp_t) strcmp) == NULL)
|
||||
free(value);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: interface management
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -31,9 +32,6 @@ struct lif_address {
|
|||
int domain;
|
||||
};
|
||||
|
||||
extern bool lif_address_parse(struct lif_address *address, const char *presentation);
|
||||
extern bool lif_address_unparse(const struct lif_address *address, char *buf, size_t buflen, bool with_netmask);
|
||||
|
||||
/*
|
||||
* Interfaces are contained in a dictionary, with the interfaces mapped by
|
||||
* interface name to their `struct lif_interface`.
|
||||
|
|
@ -47,15 +45,19 @@ extern bool lif_address_unparse(const struct lif_address *address, char *buf, si
|
|||
struct lif_interface {
|
||||
char *ifname;
|
||||
|
||||
bool is_dhcp;
|
||||
bool is_loopback;
|
||||
bool is_auto;
|
||||
bool is_bridge;
|
||||
bool is_bond;
|
||||
bool is_template;
|
||||
bool is_pending;
|
||||
bool is_explicit;
|
||||
|
||||
bool has_config_error; /* error found in interface configuration */
|
||||
|
||||
struct lif_dict vars;
|
||||
|
||||
bool is_up;
|
||||
size_t refcount; /* > 0 if up, else 0 */
|
||||
size_t rdepends_count; /* > 0 if any reverse dependency */
|
||||
};
|
||||
|
||||
#define LIF_INTERFACE_COLLECTION_FOREACH(iter, collection) \
|
||||
|
|
@ -64,15 +66,22 @@ struct lif_interface {
|
|||
#define LIF_INTERFACE_COLLECTION_FOREACH_SAFE(iter, iter_next, collection) \
|
||||
LIF_DICT_FOREACH_SAFE((iter), (iter_next), (collection))
|
||||
|
||||
extern bool lif_address_parse(struct lif_address *address, const char *presentation);
|
||||
extern bool lif_address_unparse(const struct lif_address *address, char *buf, size_t buflen, bool with_netmask);
|
||||
extern bool lif_address_format_cidr(const struct lif_interface *iface, struct lif_dict_entry *entry, char *buf, size_t buflen);
|
||||
|
||||
extern void lif_interface_init(struct lif_interface *interface, const char *ifname);
|
||||
extern bool lif_interface_address_add(struct lif_interface *interface, const char *address);
|
||||
extern void lif_interface_address_delete(struct lif_interface *interface, const char *address);
|
||||
extern void lif_interface_fini(struct lif_interface *interface);
|
||||
extern void lif_interface_use_executor(struct lif_interface *interface, const char *executor);
|
||||
extern void lif_interface_finalize(struct lif_interface *interface);
|
||||
|
||||
extern void lif_interface_collection_init(struct lif_dict *collection);
|
||||
extern void lif_interface_collection_fini(struct lif_dict *collection);
|
||||
extern struct lif_interface *lif_interface_collection_find(struct lif_dict *collection, const char *ifname);
|
||||
extern struct lif_interface *lif_interface_collection_upsert(struct lif_dict *collection, struct lif_interface *interface);
|
||||
extern bool lif_interface_collection_inherit(struct lif_interface *interface, struct lif_interface *parent);
|
||||
extern void lif_interface_collection_delete(struct lif_dict *collection, struct lif_interface *interface);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -27,5 +27,12 @@
|
|||
#include "libifupdown/execute.h"
|
||||
#include "libifupdown/lifecycle.h"
|
||||
#include "libifupdown/tokenize.h"
|
||||
#include "libifupdown/config-file.h"
|
||||
#include "libifupdown/config-parser.h"
|
||||
#include "libifupdown/compat.h"
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
# define ARRAY_SIZE(x) (sizeof(x) / sizeof(*x))
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: management of interface lifecycle (bring up, takedown, reload)
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -15,6 +16,9 @@
|
|||
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "libifupdown/environment.h"
|
||||
#include "libifupdown/execute.h"
|
||||
|
|
@ -22,17 +26,18 @@
|
|||
#include "libifupdown/lifecycle.h"
|
||||
#include "libifupdown/state.h"
|
||||
#include "libifupdown/tokenize.h"
|
||||
#include "libifupdown/config-file.h"
|
||||
|
||||
#define BUFFER_LEN 4096
|
||||
|
||||
static bool
|
||||
handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[], struct lif_interface *iface, const char *lifname, const char *phase)
|
||||
handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[], const struct lif_interface *iface, const char *phase)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
||||
(void) lifname;
|
||||
const struct lif_node *iter;
|
||||
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
const struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
if (strcmp(entry->key, phase))
|
||||
continue;
|
||||
|
|
@ -45,171 +50,162 @@ handle_commands_for_phase(const struct lif_execute_opts *opts, char *const envp[
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_address(const struct lif_execute_opts *opts, struct lif_address *addr, const char *cmd, const char *lifname)
|
||||
static inline bool
|
||||
handle_single_executor_for_phase(const struct lif_dict_entry *entry, const struct lif_execute_opts *opts, char *const envp[], const char *phase, const char *lifname)
|
||||
{
|
||||
char addrbuf[4096];
|
||||
|
||||
if (!lif_address_unparse(addr, addrbuf, sizeof addrbuf, true))
|
||||
return false;
|
||||
|
||||
return lif_execute_fmt(opts, NULL, "/sbin/ip -%d addr %s %s dev %s",
|
||||
addr->domain == AF_INET ? 4 : 6, cmd, addrbuf, lifname);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_gateway(const struct lif_execute_opts *opts, const char *gateway, const char *cmd)
|
||||
{
|
||||
int ipver = strchr(gateway, ':') ? 6 : 4;
|
||||
|
||||
return lif_execute_fmt(opts, NULL, "/sbin/ip -%d route %s default via %s",
|
||||
ipver, cmd, gateway);
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_pre_up(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
|
||||
{
|
||||
(void) opts;
|
||||
(void) iface;
|
||||
(void) lifname;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_up(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
||||
if (!lif_execute_fmt(opts, NULL, "/sbin/ip link set up dev %s", lifname))
|
||||
return false;
|
||||
|
||||
if (iface->is_loopback)
|
||||
if (strcmp(entry->key, "use"))
|
||||
return true;
|
||||
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
if (!strcmp(entry->key, "address"))
|
||||
{
|
||||
struct lif_address *addr = entry->data;
|
||||
|
||||
if (!handle_address(opts, addr, "add", lifname))
|
||||
return false;
|
||||
}
|
||||
else if (!strcmp(entry->key, "gateway"))
|
||||
{
|
||||
if (!handle_gateway(opts, entry->data, "add"))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (iface->is_dhcp)
|
||||
{
|
||||
/* XXX: determine which dhcp client we should use */
|
||||
if (!lif_execute_fmt(opts, NULL, "/sbin/udhcpc -b -R -p /var/run/udhcpc.%s.pid -i %s", lifname, lifname))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_down(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
||||
if (iface->is_loopback)
|
||||
goto skip_addresses;
|
||||
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
if (!strcmp(entry->key, "address"))
|
||||
{
|
||||
struct lif_address *addr = entry->data;
|
||||
|
||||
if (!handle_address(opts, addr, "del", lifname))
|
||||
return false;
|
||||
}
|
||||
else if (!strcmp(entry->key, "gateway"))
|
||||
{
|
||||
if (!handle_gateway(opts, entry->data, "del"))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (iface->is_dhcp)
|
||||
{
|
||||
/* XXX: determine which dhcp client we should use */
|
||||
if (!lif_execute_fmt(opts, NULL, "/bin/kill $(cat /var/run/udhcpc.%s.pid)", lifname))
|
||||
return false;
|
||||
}
|
||||
|
||||
skip_addresses:
|
||||
if (!lif_execute_fmt(opts, NULL, "/sbin/ip link set down dev %s", lifname))
|
||||
const char *cmd = entry->data;
|
||||
if (!lif_maybe_run_executor(opts, envp, cmd, phase, lifname))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_post_down(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
|
||||
handle_executors_for_phase(const struct lif_execute_opts *opts, char *const envp[], const struct lif_interface *iface, bool up, const char *phase)
|
||||
{
|
||||
(void) opts;
|
||||
(void) iface;
|
||||
(void) lifname;
|
||||
const struct lif_node *iter;
|
||||
|
||||
if (up)
|
||||
{
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
handle_single_executor_for_phase(iter->data, opts, envp, phase, iface->ifname);
|
||||
}
|
||||
else
|
||||
{
|
||||
LIF_DICT_FOREACH_REVERSE(iter, &iface->vars)
|
||||
handle_single_executor_for_phase(iter->data, opts, envp, phase, iface->ifname);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up)
|
||||
static bool
|
||||
query_dependents_from_executors(const struct lif_execute_opts *opts, char *const envp[], const struct lif_interface *iface, char *buf, size_t bufsize, const char *phase)
|
||||
{
|
||||
char **envp = NULL;
|
||||
const struct lif_node *iter;
|
||||
|
||||
lif_environment_push(&envp, "IFACE", iface->ifname);
|
||||
lif_environment_push(&envp, "PHASE", phase);
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
{
|
||||
char resbuf[1024] = {};
|
||||
const struct lif_dict_entry *entry = iter->data;
|
||||
struct lif_execute_opts exec_opts = {
|
||||
.verbose = opts->verbose,
|
||||
.executor_path = opts->executor_path,
|
||||
.interfaces_file = opts->interfaces_file,
|
||||
.timeout = opts->timeout,
|
||||
};
|
||||
|
||||
/* try to provide $METHOD for ifupdown1 scripts if we can */
|
||||
if (iface->is_loopback)
|
||||
lif_environment_push(&envp, "METHOD", "loopback");
|
||||
else if (iface->is_dhcp)
|
||||
lif_environment_push(&envp, "METHOD", "dhcp");
|
||||
if (strcmp(entry->key, "use"))
|
||||
continue;
|
||||
|
||||
/* same for $MODE */
|
||||
if (up)
|
||||
lif_environment_push(&envp, "MODE", "start");
|
||||
else
|
||||
lif_environment_push(&envp, "MODE", "stop");
|
||||
const char *cmd = entry->data;
|
||||
if (!lif_maybe_run_executor_with_result(&exec_opts, envp, cmd, resbuf, sizeof resbuf, phase, iface->ifname))
|
||||
return false;
|
||||
|
||||
struct lif_node *iter;
|
||||
if (!*resbuf)
|
||||
continue;
|
||||
|
||||
strlcat(buf, " ", bufsize);
|
||||
strlcat(buf, resbuf, bufsize);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
append_to_buffer(char **buffer, size_t *buffer_len, char **end, const char *value)
|
||||
{
|
||||
size_t value_len = strlen (value);
|
||||
|
||||
/* Make sure there is enough room to add the value to the buffer */
|
||||
if (*buffer_len < strlen (*buffer) + value_len + 2)
|
||||
{
|
||||
size_t end_offset = *end - *buffer;
|
||||
char *tmp = realloc (*buffer, *buffer_len * 2);
|
||||
|
||||
if (tmp != NULL)
|
||||
{
|
||||
*buffer = tmp;
|
||||
*end = tmp + end_offset;
|
||||
*buffer_len = *buffer_len * 2;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Append value to buffer */
|
||||
size_t printed = snprintf (*end, value_len + 2, "%s ", value);
|
||||
if (printed < value_len + 1)
|
||||
/* Here be dragons */
|
||||
return false;
|
||||
|
||||
/* Move end pointer to last printed byte */
|
||||
*end += printed;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
build_environment(char **envp[], const struct lif_execute_opts *opts, const struct lif_interface *iface, const char *lifname, const char *phase, const char *mode)
|
||||
{
|
||||
if (lifname == NULL)
|
||||
lifname = iface->ifname;
|
||||
|
||||
lif_environment_push(envp, "IFACE", lifname);
|
||||
lif_environment_push(envp, "PHASE", phase);
|
||||
lif_environment_push(envp, "MODE", mode);
|
||||
lif_environment_push(envp, "METHOD", "none");
|
||||
|
||||
if (opts->verbose)
|
||||
lif_environment_push(envp, "VERBOSE", "1");
|
||||
|
||||
if (opts->interfaces_file)
|
||||
lif_environment_push(envp, "INTERFACES_FILE", opts->interfaces_file);
|
||||
|
||||
const struct lif_node *iter;
|
||||
bool did_address = false, did_gateway = false;
|
||||
|
||||
/* Allocate a buffer for all possible addresses, if any */
|
||||
char *addresses = calloc (BUFFER_LEN, 1);
|
||||
size_t addresses_size = BUFFER_LEN;
|
||||
char *addresses_end = addresses;
|
||||
|
||||
/* Allocate a buffer for all possible gateways, if any */
|
||||
char *gateways = calloc (BUFFER_LEN, 1);
|
||||
size_t gateways_size = BUFFER_LEN;
|
||||
char *gateways_end = gateways;
|
||||
|
||||
LIF_DICT_FOREACH(iter, &iface->vars)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
|
||||
if (!strcmp(entry->key, "address"))
|
||||
{
|
||||
char addrbuf[4096];
|
||||
|
||||
if (!lif_address_format_cidr(iface, entry, addrbuf, sizeof(addrbuf)))
|
||||
continue;
|
||||
|
||||
/* Append address to buffer */
|
||||
append_to_buffer(&addresses, &addresses_size, &addresses_end, addrbuf);
|
||||
|
||||
/* Only print IF_ADDRESS once */
|
||||
if (did_address)
|
||||
continue;
|
||||
|
||||
struct lif_address *addr = entry->data;
|
||||
char addrbuf[4096];
|
||||
|
||||
if (!lif_address_unparse(addr, addrbuf, sizeof addrbuf, true))
|
||||
continue;
|
||||
|
||||
lif_environment_push(&envp, "IF_ADDRESS", addrbuf);
|
||||
lif_environment_push(envp, "IF_ADDRESS", addrbuf);
|
||||
did_address = true;
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (!strcmp(entry->key, "gateway"))
|
||||
{
|
||||
/* Append address to buffer */
|
||||
append_to_buffer(&gateways, &gateways_size, &gateways_end, entry->data);
|
||||
|
||||
if (did_gateway)
|
||||
continue;
|
||||
|
||||
|
|
@ -218,10 +214,7 @@ lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interfac
|
|||
else if (!strcmp(entry->key, "requires"))
|
||||
{
|
||||
if (iface->is_bridge)
|
||||
lif_environment_push(&envp, "IF_BRIDGE_PORTS", (const char *) entry->data);
|
||||
|
||||
if (iface->is_bond)
|
||||
lif_environment_push(&envp, "IF_BOND_SLAVES", (const char *) entry->data);
|
||||
lif_environment_push(envp, "IF_BRIDGE_PORTS", (const char *) entry->data);
|
||||
}
|
||||
|
||||
char envkey[4096] = "IF_";
|
||||
|
|
@ -236,43 +229,132 @@ lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interfac
|
|||
*ep = '_';
|
||||
}
|
||||
|
||||
lif_environment_push(&envp, envkey, (const char *) entry->data);
|
||||
lif_environment_push(envp, envkey, (const char *) entry->data);
|
||||
}
|
||||
|
||||
if (!strcmp(phase, "pre-up"))
|
||||
if (addresses != NULL)
|
||||
lif_environment_push(envp, "IF_ADDRESSES", addresses);
|
||||
if (gateways != NULL)
|
||||
lif_environment_push(envp, "IF_GATEWAYS", gateways);
|
||||
|
||||
/* Clean up */
|
||||
free (addresses);
|
||||
free (gateways);
|
||||
}
|
||||
|
||||
bool
|
||||
lif_lifecycle_query_dependents(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname)
|
||||
{
|
||||
char deps[4096] = {};
|
||||
char final_deps[4096] = {};
|
||||
|
||||
if (lifname == NULL)
|
||||
lifname = iface->ifname;
|
||||
|
||||
char **envp = NULL;
|
||||
|
||||
build_environment(&envp, opts, iface, lifname, "depend", "depend");
|
||||
|
||||
struct lif_dict_entry *entry = lif_dict_find(&iface->vars, "requires");
|
||||
if (entry != NULL)
|
||||
strlcpy(deps, entry->data, sizeof deps);
|
||||
|
||||
if (!query_dependents_from_executors(opts, envp, iface, deps, sizeof deps, "depend"))
|
||||
return false;
|
||||
|
||||
char *p = deps;
|
||||
while (*p)
|
||||
{
|
||||
if (!handle_pre_up(opts, iface, lifname))
|
||||
goto on_error;
|
||||
}
|
||||
else if (!strcmp(phase, "up"))
|
||||
{
|
||||
if (!handle_up(opts, iface, lifname))
|
||||
goto on_error;
|
||||
}
|
||||
else if (!strcmp(phase, "down"))
|
||||
{
|
||||
if (!handle_down(opts, iface, lifname))
|
||||
goto on_error;
|
||||
}
|
||||
else if (!strcmp(phase, "post-down"))
|
||||
{
|
||||
if (!handle_post_down(opts, iface, lifname))
|
||||
goto on_error;
|
||||
char *token = lif_next_token(&p);
|
||||
|
||||
if (strstr(final_deps, token) != NULL)
|
||||
continue;
|
||||
|
||||
strlcat(final_deps, token, sizeof final_deps);
|
||||
strlcat(final_deps, " ", sizeof final_deps);
|
||||
}
|
||||
|
||||
handle_commands_for_phase(opts, envp, iface, lifname, phase);
|
||||
if (entry != NULL)
|
||||
{
|
||||
free(entry->data);
|
||||
entry->data = strdup(final_deps);
|
||||
}
|
||||
else if (*final_deps)
|
||||
lif_dict_add(&iface->vars, "requires", strdup(final_deps));
|
||||
|
||||
lif_environment_free(&envp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up)
|
||||
{
|
||||
char **envp = NULL;
|
||||
|
||||
build_environment(&envp, opts, iface, lifname, phase, up ? "start" : "stop");
|
||||
|
||||
if (!handle_executors_for_phase(opts, envp, iface, up, phase))
|
||||
goto handle_error;
|
||||
|
||||
if (!handle_commands_for_phase(opts, envp, iface, phase))
|
||||
goto handle_error;
|
||||
|
||||
/* if we don't need to support /etc/if-X.d we're done here */
|
||||
if (!lif_config.allow_addon_scripts)
|
||||
goto out_free;
|
||||
|
||||
/* Check if scripts dir for this phase is present and bail out if it isn't */
|
||||
struct stat dir_stat;
|
||||
char dir_path[4096];
|
||||
snprintf (dir_path, 4096, "/etc/network/if-%s.d", phase);
|
||||
|
||||
if (stat (dir_path, &dir_stat) != 0 || S_ISDIR (dir_stat.st_mode) == 0) {
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
/* we should do error handling here, but ifupdown1 doesn't */
|
||||
lif_execute_fmt(opts, envp, "/bin/run-parts /etc/network/if-%s.d", phase);
|
||||
lif_execute_fmt(opts, envp, "/bin/run-parts %s", dir_path);
|
||||
|
||||
out_free:
|
||||
lif_environment_free(&envp);
|
||||
return true;
|
||||
|
||||
on_error:
|
||||
handle_error:
|
||||
lif_environment_free(&envp);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* this function returns true if we can skip processing the interface for now,
|
||||
* otherwise false.
|
||||
*/
|
||||
static bool
|
||||
handle_refcounting(struct lif_dict *state, struct lif_interface *iface, bool up)
|
||||
{
|
||||
size_t orig_refcount = iface->refcount;
|
||||
|
||||
if (up)
|
||||
lif_state_ref_if(state, iface->ifname, iface);
|
||||
else
|
||||
lif_state_unref_if(state, iface->ifname, iface);
|
||||
|
||||
#ifdef DEBUG_REFCOUNTING
|
||||
fprintf(stderr, "handle_refcounting(): orig_refcount=%zu, refcount=%zu, direction=%s\n",
|
||||
orig_refcount, iface->refcount, up ? "UP" : "DOWN");
|
||||
#endif
|
||||
|
||||
/* if going up and orig_refcount > 0 -- we're already configured. */
|
||||
if (up && orig_refcount > 0)
|
||||
return true;
|
||||
|
||||
/* if going down and iface->refcount > 1 -- we still have other dependents. */
|
||||
if (!up && iface->refcount > 1)
|
||||
return true;
|
||||
|
||||
/* we can change this interface -- no blocking dependents. */
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *parent, struct lif_dict *collection, struct lif_dict *state, bool up)
|
||||
{
|
||||
|
|
@ -282,6 +364,9 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par
|
|||
if (requires == NULL)
|
||||
return true;
|
||||
|
||||
/* set the parent's pending flag to break dependency cycles */
|
||||
parent->is_pending = true;
|
||||
|
||||
char require_ifs[4096] = {};
|
||||
strlcpy(require_ifs, requires->data, sizeof require_ifs);
|
||||
char *bufp = require_ifs;
|
||||
|
|
@ -290,24 +375,67 @@ handle_dependents(const struct lif_execute_opts *opts, struct lif_interface *par
|
|||
{
|
||||
struct lif_interface *iface = lif_interface_collection_find(collection, tokenp);
|
||||
|
||||
/* already up or down, skip */
|
||||
if (up == iface->is_up)
|
||||
if (iface->has_config_error)
|
||||
{
|
||||
if (opts->force)
|
||||
fprintf (stderr, "ifupdown: (de)configuring dependent interface %s (of %s) despite config errors\n",
|
||||
iface->ifname, parent->ifname);
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "ifupdown: skipping dependent interface %s (of %s) as it has config errors\n",
|
||||
iface->ifname, parent->ifname);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* if handle_refcounting returns true, it means we've already
|
||||
* configured the interface, or it is too soon to deconfigure
|
||||
* the interface.
|
||||
*/
|
||||
if (handle_refcounting(state, iface, up))
|
||||
{
|
||||
if (opts->verbose)
|
||||
fprintf(stderr, "ifupdown: skipping dependent interface %s (of %s) -- %s\n",
|
||||
iface->ifname, parent->ifname,
|
||||
up ? "already configured" : "transient dependencies still exist");
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!up && iface->is_explicit)
|
||||
{
|
||||
if (opts->verbose)
|
||||
fprintf(stderr, "ifupdown: skipping dependent interface %s (of %s) -- interface is marked as explicitly configured\n",
|
||||
iface->ifname, parent->ifname);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (opts->verbose)
|
||||
fprintf(stderr, "ifupdown: changing state of dependent interface %s (of %s) to %s\n",
|
||||
iface->ifname, parent->ifname, up ? "up" : "down");
|
||||
|
||||
if (!lif_lifecycle_run(opts, iface, collection, state, iface->ifname, up))
|
||||
{
|
||||
parent->is_pending = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
parent->is_pending = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *lifname, bool up)
|
||||
{
|
||||
/* if we're already pending, exit */
|
||||
if (iface->is_pending)
|
||||
return true;
|
||||
|
||||
if (iface->is_template)
|
||||
return false;
|
||||
|
||||
if (lifname == NULL)
|
||||
lifname = iface->ifname;
|
||||
|
||||
|
|
@ -321,6 +449,9 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
|
|||
* but, right now neither debian ifupdown or busybox ifupdown do any recovery,
|
||||
* so we wont right now.
|
||||
*/
|
||||
if (!lif_lifecycle_run_phase(opts, iface, "create", lifname, up))
|
||||
return false;
|
||||
|
||||
if (!lif_lifecycle_run_phase(opts, iface, "pre-up", lifname, up))
|
||||
return false;
|
||||
|
||||
|
|
@ -330,9 +461,7 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
|
|||
if (!lif_lifecycle_run_phase(opts, iface, "post-up", lifname, up))
|
||||
return false;
|
||||
|
||||
lif_state_upsert(state, lifname, iface);
|
||||
|
||||
iface->is_up = true;
|
||||
lif_state_ref_if(state, lifname, iface);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -345,14 +474,126 @@ lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *ifa
|
|||
if (!lif_lifecycle_run_phase(opts, iface, "post-down", lifname, up))
|
||||
return false;
|
||||
|
||||
if (!lif_lifecycle_run_phase(opts, iface, "destroy", lifname, up))
|
||||
return false;
|
||||
|
||||
/* when going up, dependents go down last. */
|
||||
if (!handle_dependents(opts, iface, collection, state, up))
|
||||
return false;
|
||||
|
||||
lif_state_delete(state, lifname);
|
||||
|
||||
iface->is_up = false;
|
||||
lif_state_unref_if(state, lifname, iface);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
count_interface_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection, struct lif_interface *parent, size_t depth)
|
||||
{
|
||||
/* if we have looped, return true immediately to break the loop. */
|
||||
if (parent->is_pending)
|
||||
return true;
|
||||
|
||||
/* query our dependents if we don't have them already */
|
||||
if (!lif_lifecycle_query_dependents(opts, parent, parent->ifname))
|
||||
return false;
|
||||
|
||||
/* set rdepends_count to depth, dependents will be depth + 1 */
|
||||
parent->is_pending = true;
|
||||
parent->rdepends_count = depth;
|
||||
|
||||
struct lif_dict_entry *requires = lif_dict_find(&parent->vars, "requires");
|
||||
|
||||
/* no dependents, nothing to worry about */
|
||||
if (requires == NULL)
|
||||
{
|
||||
parent->is_pending = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/* walk any dependents */
|
||||
char require_ifs[4096] = {};
|
||||
strlcpy(require_ifs, requires->data, sizeof require_ifs);
|
||||
char *bufp = require_ifs;
|
||||
|
||||
for (char *tokenp = lif_next_token(&bufp); *tokenp; tokenp = lif_next_token(&bufp))
|
||||
{
|
||||
struct lif_interface *iface = lif_interface_collection_find(collection, tokenp);
|
||||
|
||||
if (!count_interface_rdepends(opts, collection, iface, depth + 1))
|
||||
{
|
||||
parent->is_pending = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
parent->is_pending = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
ssize_t
|
||||
lif_lifecycle_count_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection)
|
||||
{
|
||||
struct lif_node *iter;
|
||||
|
||||
LIF_DICT_FOREACH(iter, collection)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
struct lif_interface *iface = entry->data;
|
||||
|
||||
/* start depth at interface's rdepends_count, which will be 0 for the root,
|
||||
* but will be more if additional rdepends are found...
|
||||
*/
|
||||
if (!count_interface_rdepends(opts, collection, iface, iface->rdepends_count))
|
||||
{
|
||||
fprintf(stderr, "ifupdown: dependency graph is broken for interface %s\n", iface->ifname);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* figure out the max depth */
|
||||
size_t maxdepth = 0;
|
||||
|
||||
LIF_DICT_FOREACH(iter, collection)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
struct lif_interface *iface = entry->data;
|
||||
|
||||
if (iface->rdepends_count > maxdepth)
|
||||
maxdepth = iface->rdepends_count;
|
||||
}
|
||||
|
||||
/* move the collection to a temporary list so we can reorder it */
|
||||
struct lif_list temp_list = {};
|
||||
struct lif_node *iter_next;
|
||||
|
||||
LIF_LIST_FOREACH_SAFE(iter, iter_next, collection->list.head)
|
||||
{
|
||||
void *data = iter->data;
|
||||
|
||||
lif_node_delete(iter, &collection->list);
|
||||
memset(iter, 0, sizeof *iter);
|
||||
|
||||
lif_node_insert(iter, data, &temp_list);
|
||||
}
|
||||
|
||||
/* walk backwards from maxdepth to 0, readding nodes */
|
||||
for (ssize_t curdepth = maxdepth; curdepth > -1; curdepth--)
|
||||
{
|
||||
LIF_LIST_FOREACH_SAFE(iter, iter_next, temp_list.head)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
struct lif_interface *iface = entry->data;
|
||||
|
||||
if ((ssize_t) iface->rdepends_count != curdepth)
|
||||
continue;
|
||||
|
||||
lif_node_delete(iter, &temp_list);
|
||||
memset(iter, 0, sizeof *iter);
|
||||
|
||||
lif_node_insert(iter, entry, &collection->list);
|
||||
}
|
||||
}
|
||||
|
||||
return maxdepth;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,8 +19,10 @@
|
|||
#include "libifupdown/interface.h"
|
||||
#include "libifupdown/execute.h"
|
||||
|
||||
extern bool lif_lifecycle_query_dependents(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *lifname);
|
||||
extern bool lif_lifecycle_run_phase(const struct lif_execute_opts *opts, struct lif_interface *iface, const char *phase, const char *lifname, bool up);
|
||||
extern bool lif_lifecycle_run(const struct lif_execute_opts *opts, struct lif_interface *iface, struct lif_dict *collection, struct lif_dict *state, const char *lifname, bool up);
|
||||
extern ssize_t lif_lifecycle_count_rdepends(const struct lif_execute_opts *opts, struct lif_dict *collection);
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: linked lists
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -14,8 +15,25 @@
|
|||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "libifupdown/list.h"
|
||||
|
||||
void
|
||||
lif_list_free_nodes(struct lif_list *list)
|
||||
{
|
||||
if (list == NULL)
|
||||
return;
|
||||
|
||||
struct lif_node *iter, *iter_next;
|
||||
|
||||
LIF_LIST_FOREACH_SAFE(iter, iter_next, list->head)
|
||||
{
|
||||
free (iter);
|
||||
}
|
||||
|
||||
free (list);
|
||||
}
|
||||
|
||||
void
|
||||
lif_node_insert(struct lif_node *node, void *data, struct lif_list *list)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
* Purpose: linked lists
|
||||
*
|
||||
* Copyright (c) 2020 Ariadne Conill <ariadne@dereferenced.org>
|
||||
* Copyright (c) 2020 Maximilian Wilhelm <max@sdn.clinic>
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
|
@ -29,6 +30,8 @@ struct lif_list {
|
|||
size_t length;
|
||||
};
|
||||
|
||||
extern void lif_list_free_nodes(struct lif_list *list);
|
||||
|
||||
extern void lif_node_insert(struct lif_node *node, void *data, struct lif_list *list);
|
||||
extern void lif_node_insert_tail(struct lif_node *node, void *data, struct lif_list *list);
|
||||
extern void lif_node_delete(struct lif_node *node, struct lif_list *list);
|
||||
|
|
@ -37,6 +40,9 @@ extern void lif_node_delete(struct lif_node *node, struct lif_list *list);
|
|||
for ((iter) = (head); (iter) != NULL; (iter) = (iter)->next)
|
||||
|
||||
#define LIF_LIST_FOREACH_SAFE(iter, iter_next, head) \
|
||||
for ((iter) = (head), (iter_next) = (iter)->next; (iter) != NULL; (iter) = (iter_next), (iter_next) = (iter) != NULL ? (iter)->next : NULL)
|
||||
for ((iter) = (head), (iter_next) = (iter) != NULL ? (iter)->next : NULL; (iter) != NULL; (iter) = (iter_next), (iter_next) = (iter) != NULL ? (iter)->next : NULL)
|
||||
|
||||
#define LIF_LIST_FOREACH_REVERSE(iter, tail) \
|
||||
for ((iter) = (tail); (iter) != NULL; (iter) = (iter)->prev)
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -13,27 +13,46 @@
|
|||
* from the use of this software.
|
||||
*/
|
||||
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include "libifupdown/state.h"
|
||||
#include "libifupdown/fgetline.h"
|
||||
#include "libifupdown/tokenize.h"
|
||||
|
||||
bool
|
||||
lif_state_read(struct lif_dict *state, FILE *fd)
|
||||
{
|
||||
char linebuf[4096];
|
||||
|
||||
while (lif_fgetline(linebuf, sizeof linebuf, fd))
|
||||
{
|
||||
char *ifname = linebuf;
|
||||
char *bufp = linebuf;
|
||||
char *ifname = lif_next_token(&bufp);
|
||||
char *refcount = lif_next_token(&bufp);
|
||||
char *explicit = lif_next_token(&bufp);
|
||||
size_t rc = 1;
|
||||
char *equals_p = strchr(linebuf, '=');
|
||||
bool is_explicit = false;
|
||||
|
||||
if (*explicit && !strcmp(explicit, "explicit"))
|
||||
is_explicit = true;
|
||||
|
||||
if (*refcount)
|
||||
{
|
||||
rc = strtoul(refcount, NULL, 10);
|
||||
|
||||
if (rc == 0 || rc == ULONG_MAX)
|
||||
rc = 1;
|
||||
}
|
||||
|
||||
if (equals_p == NULL)
|
||||
{
|
||||
lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = ifname });
|
||||
lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = ifname, .refcount = rc, .is_explicit = is_explicit });
|
||||
continue;
|
||||
}
|
||||
|
||||
*equals_p++ = '\0';
|
||||
lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = equals_p });
|
||||
lif_state_upsert(state, ifname, &(struct lif_interface){ .ifname = equals_p, .refcount = rc, .is_explicit = is_explicit });
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -45,8 +64,9 @@ lif_state_read_path(struct lif_dict *state, const char *path)
|
|||
FILE *fd = fopen(path, "r");
|
||||
bool ret;
|
||||
|
||||
/* if file cannot be opened, assume an empty state */
|
||||
if (fd == NULL)
|
||||
return false;
|
||||
return true;
|
||||
|
||||
ret = lif_state_read(state, fd);
|
||||
fclose(fd);
|
||||
|
|
@ -54,10 +74,39 @@ lif_state_read_path(struct lif_dict *state, const char *path)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
lif_state_ref_if(struct lif_dict *state, const char *ifname, struct lif_interface *iface)
|
||||
{
|
||||
iface->refcount++;
|
||||
lif_state_upsert(state, ifname, iface);
|
||||
}
|
||||
|
||||
void
|
||||
lif_state_unref_if(struct lif_dict *state, const char *ifname, struct lif_interface *iface)
|
||||
{
|
||||
if (iface->refcount == 0)
|
||||
return;
|
||||
|
||||
iface->refcount--;
|
||||
|
||||
if (iface->refcount)
|
||||
lif_state_upsert(state, ifname, iface);
|
||||
else
|
||||
lif_state_delete(state, ifname);
|
||||
}
|
||||
|
||||
void
|
||||
lif_state_upsert(struct lif_dict *state, const char *ifname, struct lif_interface *iface)
|
||||
{
|
||||
lif_dict_add(state, ifname, strdup(iface->ifname));
|
||||
lif_state_delete(state, ifname);
|
||||
|
||||
struct lif_state_record *rec = calloc(1, sizeof(*rec));
|
||||
|
||||
rec->mapped_if = strdup(iface->ifname);
|
||||
rec->refcount = iface->refcount;
|
||||
rec->is_explicit = iface->is_explicit;
|
||||
|
||||
lif_dict_add(state, ifname, rec);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -68,7 +117,10 @@ lif_state_delete(struct lif_dict *state, const char *ifname)
|
|||
if (entry == NULL)
|
||||
return;
|
||||
|
||||
free(entry->data);
|
||||
struct lif_state_record *rec = entry->data;
|
||||
free(rec->mapped_if);
|
||||
free(rec);
|
||||
|
||||
lif_dict_delete_entry(state, entry);
|
||||
}
|
||||
|
||||
|
|
@ -80,8 +132,10 @@ lif_state_write(const struct lif_dict *state, FILE *f)
|
|||
LIF_DICT_FOREACH(iter, state)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
struct lif_state_record *rec = entry->data;
|
||||
|
||||
fprintf(f, "%s=%s\n", entry->key, (const char *) entry->data);
|
||||
fprintf(f, "%s=%s %zu%s\n", entry->key, rec->mapped_if, rec->refcount,
|
||||
rec->is_explicit ? " explicit" : "");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +161,8 @@ lif_state_lookup(struct lif_dict *state, struct lif_dict *if_collection, const c
|
|||
if (entry == NULL)
|
||||
return NULL;
|
||||
|
||||
struct lif_dict_entry *if_entry = lif_dict_find(if_collection, (const char *) entry->data);
|
||||
struct lif_state_record *rec = entry->data;
|
||||
struct lif_dict_entry *if_entry = lif_dict_find(if_collection, rec->mapped_if);
|
||||
|
||||
if (if_entry == NULL)
|
||||
return NULL;
|
||||
|
|
@ -123,9 +178,11 @@ lif_state_sync(struct lif_dict *state, struct lif_dict *if_collection)
|
|||
LIF_DICT_FOREACH(iter, state)
|
||||
{
|
||||
struct lif_dict_entry *entry = iter->data;
|
||||
struct lif_interface *iface = lif_interface_collection_find(if_collection, entry->key);
|
||||
struct lif_state_record *rec = entry->data;
|
||||
struct lif_interface *iface = lif_interface_collection_find(if_collection, rec->mapped_if);
|
||||
|
||||
iface->is_up = true;
|
||||
iface->refcount = rec->refcount;
|
||||
iface->is_explicit = rec->is_explicit;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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
127
libifupdown/yaml-base.c
Normal 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
52
libifupdown/yaml-base.h
Normal 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
66
libifupdown/yaml-writer.c
Normal 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
24
libifupdown/yaml-writer.h
Normal 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
|
||||
|
|
@ -2,6 +2,9 @@ syntax(2)
|
|||
|
||||
test_suite('ifupdown-ng')
|
||||
|
||||
atf_test_program{name='multicall_test'}
|
||||
atf_test_program{name='ifquery_test'}
|
||||
atf_test_program{name='ifup_test'}
|
||||
atf_test_program{name='ifdown_test'}
|
||||
|
||||
include('linux/Kyuafile')
|
||||
|
|
|
|||
1
tests/executors/bond
Symbolic link
1
tests/executors/bond
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
mock-executor
|
||||
1
tests/executors/bridge
Symbolic link
1
tests/executors/bridge
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
mock-executor
|
||||
1
tests/executors/dhcp
Symbolic link
1
tests/executors/dhcp
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
mock-executor
|
||||
1
tests/executors/link
Symbolic link
1
tests/executors/link
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
mock-executor
|
||||
7
tests/executors/mock-dependency-generator
Executable file
7
tests/executors/mock-dependency-generator
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/sh
|
||||
|
||||
[ -z "$IF_MOCK_DEPENDS" ] && IF_MOCK_DEPENDS="eth0 eth1 eth2 eth3 eth4"
|
||||
|
||||
case "$PHASE" in
|
||||
depend) echo "$IF_MOCK_DEPENDS" ;;
|
||||
esac
|
||||
2
tests/executors/mock-executor
Executable file
2
tests/executors/mock-executor
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
exit 0
|
||||
1
tests/executors/static
Symbolic link
1
tests/executors/static
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
mock-executor
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue