conlink 2.2.0 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - name: Checkout
14
- uses: actions/checkout@v3
14
+ uses: actions/checkout@v4
15
15
 
16
16
  - name: npm install
17
17
  run: npm install
@@ -19,6 +19,6 @@ jobs:
19
19
  - name: compose build of conlink
20
20
  run: docker compose -f examples/test1-compose.yaml build
21
21
 
22
- - name: "./run-tests.sh"
22
+ - name: "dctest test/test*.yaml"
23
23
  timeout-minutes: 5
24
- run: time ./run-tests.sh
24
+ run: time node_modules/.bin/dctest --verbose-commands conlink-test $(ls -v test/test*.yaml)
package/Dockerfile CHANGED
@@ -24,7 +24,7 @@ FROM node:16-slim as run
24
24
  RUN apt-get -y update
25
25
  # Runtime deps and utilities
26
26
  RUN apt-get -y install libpcap-dev tcpdump iproute2 iputils-ping curl \
27
- iptables bridge-utils ethtool \
27
+ iptables bridge-utils ethtool jq \
28
28
  openvswitch-switch openvswitch-testcontroller
29
29
 
30
30
  COPY --from=build /app/ /app/
package/README.md CHANGED
@@ -1,9 +1,46 @@
1
1
  # conlink: Declarative Low-Level Networking for Containers
2
2
 
3
-
4
3
  Create (layer 2 and layer 3) networking between containers using
5
4
  a declarative configuration.
6
5
 
6
+ Conlink also includes scripts that make docker compose a much more
7
+ powerful development and testing environment (refer to
8
+ [Compose scripts](#compose-scripts-mdc-waitsh-and-copysh) for
9
+ details):
10
+
11
+ * [mdc](#mdc): modular management of multiple compose configurations
12
+ * [wait.sh](#waitsh): wait for network and file conditions before continuing
13
+ * [copy.sh](#copysh): recursively copy files with variable templating
14
+
15
+ ## Why conlink?
16
+
17
+ There are a number of limitations of docker-compose networking that
18
+ conlink addresses:
19
+
20
+ * Operates at layer 3 of the network stack (i.e. IP).
21
+ * Has a fixed model of container interface naming: first interface is
22
+ `eth0`, second is `eth1`, etc.
23
+ * For containers with multiple interfaces, the mapping between docker
24
+ compose networks and container interface is not controllable
25
+ (https://github.com/docker/compose/issues/4645#issuecomment-701351876,
26
+ https://github.com/docker/compose/issues/8561#issuecomment-1872510968)
27
+ * If a container uses the scale property, then IPs cannot be
28
+ assigned and user assigned MAC addresses will be the same for every
29
+ instance of that service.
30
+ * Docker bridge networking interferes with switch and bridge protocol
31
+ traffic (BPDU, STP, LLDP, etc). Conlink supports the "patch" link
32
+ mode that allows this type of traffic to pass correctly.
33
+
34
+ Conlink has the following features:
35
+
36
+ - Declarative network configuration (links, bridges, patches, etc)
37
+ - Event driven (container restarts and scale changes)
38
+ - Low-level control of network interfaces/links: MTU, routes, port
39
+ forwarding, netem properties, etc
40
+ - Automatic IP and MAC address incrementing for scaled containers
41
+ - Central network container for easy monitoring and debug
42
+ - Composable configuration from multiple sources/locations
43
+
7
44
  ## Prerequisites
8
45
 
9
46
  General:
@@ -74,12 +111,12 @@ same bridge (broadcast domain).
74
111
  Network configuration can either be loaded directly from configuration
75
112
  files using the `--network-config` option or it can be loaded from
76
113
  `x-network` properties contained in docker-compose files using the
77
- `--compose-file`. Multiple of each option may be specified and all the
78
- network configuration will be merged into a final network
79
- configuration.
114
+ `--compose-file` option. Multiple of each option may be specified and
115
+ all the network configuration will be merged into a final network
116
+ configuration. Both options also support colon separated lists.
80
117
 
81
- The network configuration can have three top level keys: `links`,
82
- `tunnels`, and `commands`.
118
+ The network configuration can have four top level keys: `links`,
119
+ `bridges`, `tunnels`, and `commands`.
83
120
 
84
121
  ### Links
85
122
 
@@ -103,12 +140,13 @@ The following table describes the link properties:
103
140
  | ip | * | CIDR | | IP CIDR 7 |
104
141
  | mac | 3 | MAC | | MAC addr 7 |
105
142
  | mtu | * | number 4 | 65535 | intf MTU |
106
- | route | * | string | | ip route add args |
143
+ | route | * | strings 8 | | ip route add args |
107
144
  | nat | * | IP | | DNAT/SNAT to IP |
108
- | netem | * | string | | tc qdisc NetEm options |
145
+ | netem | * | strings 8 | | tc qdisc NetEm options |
109
146
  | mode | 5 | string | | virt intf mode |
110
147
  | vlanid | vlan | number | | VLAN ID |
111
- | forward | veth | string array 6 | | forward conlink ports 7 |
148
+ | forward | veth | strings 6 8 | | forward conlink ports 7 |
149
+ | ethtool | veth | strings 8 | | ethtool settings |
112
150
 
113
151
  - 1 - veth, dummy, vlan, ipvlan, macvlan, ipvtap, macvtap
114
152
  - 2 - defaults to outer compose service
@@ -117,6 +155,7 @@ The following table describes the link properties:
117
155
  - 5 - macvlan, macvtap, ipvlan, ipvtap
118
156
  - 6 - string syntax: `conlink_port:container_port/proto`
119
157
  - 7 - offset by scale/replica index
158
+ - 8 - either a single string or an array of strings
120
159
 
121
160
  Each link has a 'type' key that defaults to "veth" and each link
122
161
  definition must also have either a `service` key or a `container` key.
@@ -150,6 +189,12 @@ For publicly publishing a port, the conlink container needs to be on
150
189
  a docker network and the `conlink_port` should match the target port
151
190
  of a docker published port (for the conlink container).
152
191
 
192
+ For the `ethtool` property, refer to the `ethtool` man page. The
193
+ syntax for each ethtool setting is basically the ethtool command line
194
+ arguments without the "devname. So the equivalent of the ethtool
195
+ command `ethtool --offload eth0 rx off` would be link configuration
196
+ `{dev: eth0, ethtool: ["--offload rx off"], ...}`.
197
+
153
198
  ### Bridges
154
199
 
155
200
  The bridge settings currently only support the "mode" setting. If
@@ -531,13 +576,15 @@ validate connectivity using ping:
531
576
  ```
532
577
  export BRIDGE_MODE="linux" # "ovs", "patch", "auto"
533
578
  docker-compose -f examples/test9-compose.yaml up --build --force-recreate
534
- docker-compose -f examples/test9-compose.yaml exec node ping 10.0.1.2
579
+ docker-compose -f examples/test9-compose.yaml exec node1 ping 10.0.1.2
535
580
  ```
536
581
 
537
- ### test10: port forwarding
582
+ ### test10: port forwarding and routing
538
583
 
539
584
  This example demonstrates port forwarding from the conlink container
540
- to two containers running simple web servers.
585
+ to two containers running simple web servers. It also demonstrates the
586
+ use of a router container and multiple route rules in the other
587
+ containers.
541
588
 
542
589
  Start the test10 compose configuration:
543
590
 
@@ -581,6 +628,154 @@ curl 0.0.0.0:80
581
628
  curl 0.0.0.0:81
582
629
  ```
583
630
 
631
+ Start a two tcpdump processes in the conlink container to watch
632
+ routed ICMP traffic and then ping between containers across the router
633
+ container:
634
+
635
+ ```
636
+ docker compose -f examples/test10-compose.yaml exec network tcpdump -nli router_1-es1 icmp
637
+ docker compose -f examples/test10-compose.yaml exec network tcpdump -nli router_1-es2 icmp
638
+ ```
639
+
640
+ ```
641
+ docker-compose -f examples/test10-compose.yaml exec node1 ping 10.2.0.1
642
+ docker-compose -f examples/test10-compose.yaml exec node1 ping 10.2.0.2
643
+ docker-compose -f examples/test10-compose.yaml exec node2 ping 10.1.0.1
644
+
645
+ ```
646
+
647
+ ## Compose scripts: mdc, wait.sh, and copy.sh
648
+
649
+ ### mdc
650
+
651
+ The `mdc` command adds flexibility and power to the builtin overlay
652
+ capability of docker compose. Docker compose can specify multiple
653
+ compose files that will be combined into a single configuration.
654
+ Compose files that are specified later will overlay or override
655
+ earlier compose files. For example, if compose files A and B are
656
+ loaded by docker compose, then the `image` property of a service in
657
+ file B will take precedence (or override) the `image` property for the
658
+ same service in file A. Some properties such as `volumes` and
659
+ `environment` will have the sub-properties merged or appended to.
660
+
661
+ There are several ways that `mdc` adds to the composition capabilities
662
+ of docker compose:
663
+ 1. **Mode/module dependency resolution**. The modes or modules that
664
+ are combined by `mdc` are defined as directories that contain
665
+ mode/module specific content. A `deps` file in a mode/module
666
+ directory is used to specify dependencies on other modes/modules.
667
+ The syntax and resolution algorithm is defined by the
668
+ [resolve-deps](https://github.com/Viasat/resolve-deps) project.
669
+ 2. **Environment variable file combining/overlaying**. Each `.env`
670
+ file that appears in a mode/module directory will be appended into
671
+ a single `.env` file at the top-level where the `mdc` command is
672
+ invoked. Later environment variables will override earlier ones
673
+ with the same name. Variable interpolation and some shell-style
674
+ variable expansion can be used to combine/append environment
675
+ variables. For example if FOO and BAR are defined in an earlier
676
+ mode/module, then BAZ could be defined like this:
677
+ `BAZ="${FOO:-${BAR}-SUFF"` which will set BAZ to FOO if FOO is set,
678
+ otherwise, it will set BAZ to BAR with a "-SUFF" suffix.
679
+ 3. **Directory hierarchy combining/overlaying**. If the mode/module
680
+ directory has subdirectories that themselves contain a "files/"
681
+ sub-directory, then the mode subdirectories will be recursively
682
+ copied into the top-level ".files/" directory. For example,
683
+ consider if the following files exists under the modes "foo" and
684
+ "bar" (with a dependency of "bar" on "foo"):
685
+ `foo/svc1/files/etc/conf1`, `foo/svc2/files/etc/conf2`, and
686
+ `bar/svc1/files/etc/conf1`. When `mdc` is run this will result in
687
+ the following two files: `.files/svc1/etc/conf1` and
688
+ `.files/svc2/etc/conf2`. The content of `conf1` will come from the
689
+ "bar" mode because it is resolved second. The use of the `copy.sh`
690
+ script (described below) simplifies recursive file copying and also
691
+ provides variable templating of copied files.
692
+ 4. **Set environment variables based on the selected modes/modules**.
693
+ When `mdc` is run it will set the following special environment
694
+ variables in the top-level `.env` file:
695
+ * `COMPOSE_FILE`: A colon separated and dependency ordered list of
696
+ compose file paths from each resolved mode/module directory.
697
+ * `COMPOSE_DIR`: The directory where the top-level `.env` is
698
+ created.
699
+ * `COMPOSE_PRPOFILES`: A comma separated list of each resolved
700
+ mode/module with a `MODE_` prefix on the name. These are docker
701
+ compose profiles that can be used to enable services in one
702
+ mode/module compose file when a different mode/module is
703
+ selected/resolved by `mdc`. For example, if a compose file in
704
+ "bar" has a service that should only be enabled when the "foo"
705
+ mode/module is also requested/resolved, then the service can be
706
+ tagged with the `MODE_foo` profile.
707
+ * `MDC_MODE_DIRS`: A comma separated list of mode/module
708
+ directories. This can be used by other external tools that have
709
+ specific mode/module behavior.
710
+
711
+ Conlink network configuration can be specified in `x-network`
712
+ properties within compose files. This can be a problem with the
713
+ builtin overlay functionality of docker compose because `x-` prefixed
714
+ properties are simply overriden as a whole without any special merging
715
+ behavior. To work around this limitation, conlink has the ability to
716
+ directly merge `x-network` configuration from multiple compose files
717
+ by passing the `COMPOSE_FILE` variable to the conlink `--compose-file`
718
+ parameter (which supports a colon sperated list of compose files).
719
+
720
+ ### wait.sh
721
+
722
+ The dynamic event driven nature of conlink mean that interfaces may
723
+ appear after the container service code starts running (unlike plain
724
+ docker container networking). For this reason, the `wait.sh` script is
725
+ provided to simplify waiting for interfaces to appear (and other
726
+ network conditions). Here is a compose file snippit that will wait for
727
+ `eth0` to appear and for `eni1` to both appear and have an IP address
728
+ assigned before running the startup command (after the `--`):
729
+
730
+ ```
731
+ services:
732
+ svc1:
733
+ volumes:
734
+ - ./conlink/scripts:/scripts:ro
735
+ command: /scripts/wait.sh -i eth0 -I eni1 -- /start-cmd.sh arg1 arg2
736
+ ```
737
+
738
+ In addition to waiting for interfaces and address assignment,
739
+ `wait.sh` can also wait for a file to appear (`-f FILE`), a remote TCP
740
+ port to become accessible (`-t HOST:PORT`), or run a command until it
741
+ completes successfully (`-c COMMAND`).
742
+
743
+
744
+ ### copy.sh
745
+
746
+ One of the features of the `mdc` command is to collect directory
747
+ hierarchies from mode/module directories into a single `.files/`
748
+ directory at the top-level. The intended use of the merged directory
749
+ hierarchy is to be merged into file-systems of running containers.
750
+ However, simple volume mounts will replace entire directory
751
+ hierarchies (and hide all prior files under the mount point). The
752
+ `copy.sh` script is provided for easily merging/overlaying one
753
+ directory hierarchy onto another one. In addition, the `-T` option
754
+ will also replace special `{{VAR}}` tokens in the files being copied
755
+ with the value of the matching environment variable.
756
+
757
+ Here is a compose file snippit that shows the use of `copy.sh` to
758
+ recursively copy/overlay the directory tree in `./.files/svc2` onto
759
+ the container root file-system. In addition, due to the use of the
760
+ `-T` option, the script will replace any occurence of the string
761
+ `{{FOO}}` with the value of the `FOO` environment variable within any
762
+ of the files that are copied:
763
+
764
+ ```
765
+ services:
766
+ svc2:
767
+ environment:
768
+ - FOO=123
769
+ volumes:
770
+ - ./.files/svc2:/files:ro
771
+ command: /scripts/copy.sh -T /files / -- /start-cmd.sh arg1 arg2
772
+ ```
773
+
774
+ Note that instances of `copy.sh` and `wait.sh` can be easily chained
775
+ together like this:
776
+ ```
777
+ /scripts/copy.sh -T /files / -- /scripts/wait.sh -i eth0 -- cmd args
778
+ ```
584
779
 
585
780
  ## GraphViz network configuration rendering
586
781
 
@@ -7,7 +7,8 @@ services:
7
7
  command: "python3 -m http.server -d /var 80"
8
8
  x-network:
9
9
  links:
10
- - {bridge: s2, ip: "10.0.1.1/24", route: "default",
10
+ - {bridge: s1, ip: "10.1.0.1/24",
11
+ route: ["10.0.0.0/8 via 10.1.0.100"],
11
12
  forward: ["1080:80/tcp", "1180:80/tcp"]}
12
13
 
13
14
  node2:
@@ -17,8 +18,18 @@ services:
17
18
  command: "python3 -m http.server -d /usr 80"
18
19
  x-network:
19
20
  links:
20
- - {bridge: s1, ip: "10.0.2.1/24", route: "default",
21
- forward: ["80:80/tcp"]}
21
+ - {bridge: s2, ip: "10.2.0.1/24",
22
+ route: "10.0.0.0/8 via 10.2.0.100",
23
+ forward: "80:80/tcp"}
24
+
25
+ router:
26
+ image: python:3-alpine
27
+ network_mode: none
28
+ command: sleep Infinity
29
+ x-network:
30
+ links:
31
+ - {bridge: s1, ip: "10.1.0.100/24", dev: es1}
32
+ - {bridge: s2, ip: "10.2.0.100/24", dev: es2}
22
33
 
23
34
  network:
24
35
  build: {context: ../}
@@ -28,7 +28,13 @@ services:
28
28
  ip: 10.0.1.1/16
29
29
  mac: 00:0a:0b:0c:0d:01
30
30
  mtu: 4111
31
- netem: "delay 40ms rate 10mbit"
31
+ netem: "rate 10mbit delay 40ms"
32
+ ethtool: "--offload rx off"
32
33
  - bridge: s2
33
34
  ip: 100.0.1.1/16
34
35
  dev: eth1
36
+
37
+ x-network:
38
+ links:
39
+ # The delay setting is overridden by the one in the service
40
+ - {service: node, bridge: s1, netem: "delay 200ms"}
@@ -19,14 +19,19 @@ services:
19
19
  - BRIDGE_MODE
20
20
  command: /app/build/conlink.js --default-bridge-mode linux --compose-file /test/test9-compose.yaml
21
21
 
22
- node:
22
+ node1:
23
+ image: alpine
24
+ network_mode: none
25
+ command: sleep Infinity
26
+
27
+ node2:
23
28
  image: alpine
24
29
  network_mode: none
25
- scale: 2
26
30
  command: sleep Infinity
27
31
 
28
32
  x-network:
29
33
  links:
30
- - {bridge: s1, service: node, ip: 10.0.1.1/24}
34
+ - {bridge: s1, service: node1, ip: 10.0.1.1/24}
35
+ - {bridge: s1, service: node2, ip: 10.0.1.2/24}
31
36
  bridges:
32
37
  - {bridge: s1, mode: "${BRIDGE_MODE:-auto}"}
package/link-add.sh CHANGED
@@ -32,9 +32,8 @@ usage () {
32
32
  echo >&2 " --mac MAC0 - MAC address for INTF0"
33
33
  echo >&2 " --mac0 MAC0 - MAC address for INTF0"
34
34
  echo >&2 " --mac1 MAC1 - MAC address for INTF1"
35
- echo >&2 " --route 'ROUTE' - route to add to INTF0"
36
- echo >&2 " --route|--route0 'ROUTE' - route to add to INTF0"
37
- echo >&2 " --route1 'ROUTE' - route to add to INTF1"
35
+ echo >&2 " --route|--route0 'ROUTE' - route to add to INTF0 (can repeat)"
36
+ echo >&2 " --route1 'ROUTE' - route to add to INTF1 (can repeat)"
38
37
  echo >&2 " --mtu MTU - MTU for both interfaces"
39
38
  echo >&2 ""
40
39
  echo >&2 " --mode MODE - Mode settings for *vlan TYPEs"
@@ -43,7 +42,10 @@ usage () {
43
42
  echo >&2 " --remote REMOTE - Remote address for geneve/vxlan types"
44
43
  echo >&2 " --vni VNI - Virtual Network Identifier for geneve/vxlan types"
45
44
  echo >&2 ""
46
- echo >&2 " --netem NETEM - tc qdisc netem OPTIONS (man 8 netem)"
45
+ echo >&2 " --netem NETEM - tc qdisc netem OPTIONS (can repeat)"
46
+ echo >&2 " (man 8 netem)"
47
+ echo >&2 " --ethtool 'ARG OPTS' - ethtool ARG INTF0 OPTS (can repeat)"
48
+ echo >&2 " (man 8 ethtool)"
47
49
  echo >&2 " --nat TARGET - Stateless NAT traffic to/from TARGET"
48
50
  echo >&2 " (in primary/PID0 netns)"
49
51
  echo >&2 ""
@@ -54,17 +56,21 @@ info() { echo "link-add [${LOG_ID}] ${*}"; }
54
56
  warn() { >&2 echo "link-add [${LOG_ID}] ${*}"; }
55
57
  die() { warn "ERROR: ${*}"; exit 1; }
56
58
 
57
- # Set MAC, IP, ROUTE, MTU, and up state for interface in netns
59
+ # Set MAC, IP, ROUTES, MTU, and up state for interface in netns
58
60
  setup_if() {
59
- local IF=$1 NS=$2 MAC=$3 IP=$4 ROUTE=$5 MTU=$6
61
+ local IF=$1 NS=$2 MAC=$3 IP=$4 MTU=$5 ROUTES=$6 routes=
62
+ echo >&2 "ROUTES: ${ROUTES}"
63
+ while read rt; do
64
+ routes="${routes}route add ${rt} dev ${IF}\n"
65
+ done < <(echo -en "${ROUTES}")
60
66
 
61
- info "Setting ${IP:+IP ${IP}, }${MAC:+MAC ${MAC}, }${MTU:+MTU ${MTU}, }${ROUTE:+ROUTE '${ROUTE}', }up state"
67
+ info "Setting ${IP:+IP ${IP}, }${MAC:+MAC ${MAC}, }${MTU:+MTU ${MTU}, }${ROUTES:+ROUTES '${ROUTES//$'\n'/,}', }up state"
62
68
  ip -netns ${NS} --force -b - <<EOF
63
69
  ${IP:+addr add ${IP} dev ${IF}}
64
70
  ${MAC:+link set dev ${IF} address ${MAC}}
65
71
  ${MTU:+link set dev ${IF} mtu ${MTU}}
66
72
  link set dev ${IF} up
67
- ${ROUTE:+route add ${ROUTE} dev ${IF}}
73
+ $(echo -en "${routes}")
68
74
  EOF
69
75
  }
70
76
 
@@ -78,8 +84,8 @@ IPTABLES() {
78
84
  # Parse arguments
79
85
  VERBOSE=${VERBOSE:-}
80
86
  PID1=${PID1:-<SELF>} IF1=${IF1:-eth0}
81
- IP0= IP1= MAC0= MAC1= ROUTE0= ROUTE1= MTU=
82
- MODE= VLANID= REMOTE= VNI= NETEM= NAT=
87
+ IP0= IP1= MAC0= MAC1= ROUTES0= ROUTES1= MTU=
88
+ MODE= VLANID= REMOTE= VNI= NETEM= NAT= ETHTOOL=
83
89
  positional=
84
90
  while [ "${*}" ]; do
85
91
  param=$1; OPTARG=$2
@@ -91,17 +97,17 @@ while [ "${*}" ]; do
91
97
  --ip1) IP1="${OPTARG}"; shift ;;
92
98
  --mac|--mac0) MAC0="${OPTARG}"; shift ;;
93
99
  --mac1) MAC1="${OPTARG}"; shift ;;
94
- --route|--route0) ROUTE0="${OPTARG}"; shift ;;
95
- --route1) ROUTE1="${OPTARG}"; shift ;;
100
+ --route|--route0) ROUTES0="${ROUTES0}${OPTARG}\n"; shift ;;
101
+ --route1) ROUTES1="${ROUTES1}${OPTARG}\n"; shift ;;
96
102
  --mtu) MTU="${OPTARG}"; shift ;;
97
-
103
+ --ethtool) ETHTOOL="${ETHTOOL}${OPTARG}\n"; shift ;;
98
104
  --mode) MODE="${OPTARG}"; shift ;;
99
105
  --vlanid) VLANID="${OPTARG}"; shift ;;
100
106
 
101
107
  --remote) REMOTE="${OPTARG}"; shift ;;
102
108
  --vni) VNI="${OPTARG}"; shift ;;
103
109
 
104
- --netem) NETEM="${OPTARG}"; shift ;;
110
+ --netem) NETEM="${NETEM} ${OPTARG}"; shift ;;
105
111
  --nat) NAT="${OPTARG}"; shift ;;
106
112
  -h|--help) usage ;;
107
113
  *) positional="${positional} $1" ;;
@@ -181,15 +187,20 @@ geneve|vxlan)
181
187
  ;;
182
188
  esac
183
189
 
184
- setup_if ${IF0} ${NS0} "${MAC0}" "${IP0}" "${ROUTE0}" "${MTU}"
190
+ setup_if ${IF0} ${NS0} "${MAC0}" "${IP0}" "${MTU}" "${ROUTES0}"
185
191
  [ "${TYPE}" = "veth" ] && \
186
- setup_if ${IF1} ${NS1} "${MAC1}" "${IP1}" "${ROUTE1}" "${MTU}"
192
+ setup_if ${IF1} ${NS1} "${MAC1}" "${IP1}" "${MTU}" "${ROUTES1}"
187
193
 
188
194
  if [ "${NETEM}" ]; then
189
195
  info "Setting tc qdisc netem: ${NETEM}"
190
196
  tc -netns ${NS0} qdisc add dev ${IF0} root netem ${NETEM}
191
197
  fi
192
198
 
199
+ while read arg opts; do
200
+ info "Applying ethtool ${arg} ${IF0} ${opts} (in ${NS0})"
201
+ ip netns exec ${NS0} ethtool ${arg} ${IF0} ${opts}
202
+ done < <(echo -en "${ETHTOOL}")
203
+
193
204
  if [ "${NAT}" ]; then
194
205
  info "Adding NAT rule to ${NAT}"
195
206
  IPTABLES ${NS0} PREROUTING -t nat -i ${IF0} -j DNAT --to-destination ${NAT}
package/link-forward.sh CHANGED
@@ -37,7 +37,8 @@ LOG_ID="${spec}"
37
37
  info "${action^} forwarding ${intf_a} -> ${intf_b}"
38
38
 
39
39
  IPTABLES PREROUTING -t nat -i ${intf_a} -p ${proto} --dport ${port_a} -j DNAT --to-destination ${ip}:${port_b}
40
- IPTABLES POSTROUTING -t nat -o ${intf_b} -j MASQUERADE
40
+ IPTABLES PREROUTING -t nat -i ${intf_a} -p ${proto} --dport ${port_a} -j MARK --set-mark 1
41
+ IPTABLES POSTROUTING -t nat -o ${intf_b} -m mark --mark 1 -j MASQUERADE
41
42
 
42
43
  case "${action}" in
43
44
  add) ip route replace ${ip} dev ${intf_b} ;;
package/link-mirred.sh CHANGED
@@ -34,41 +34,20 @@ die() { warn "ERROR: ${*}"; exit 1; }
34
34
  add_ingress() {
35
35
  local IF=$1 res=
36
36
 
37
- res=$(tc qdisc show dev ${IF} 2>&1)
38
- case "${res}" in
39
- *"qdisc ingress ffff:"*)
40
- info "${IF0} already has ingress qdisc"
41
- ;;
42
- ""|*"qdisc noqueue"*)
43
- info "Adding ingress qdisc to ${IF}"
44
- tc qdisc add dev "${IF}" ingress \
45
- || die "Could not add ingress qdisc to ${IF}"
46
- ;;
47
- *)
48
- die "${IF} has invalid ingress qdisc or could not be queried"
49
- ;;
50
- esac
37
+ info "Adding ingress qdisc to ${IF}"
38
+ tc qdisc replace dev "${IF}" ingress \
39
+ || die "Could not add ingress qdisc to ${IF}"
51
40
  }
52
41
 
53
42
  # Idempotently add mirred filter redirect rule to an interface
54
43
  add_mirred() {
55
44
  local IF0=$1 IF1=$2 res=
56
45
 
57
- res=$(tc filter show dev ${IF0} parent ffff: 2>&1)
58
- case "${res}" in
59
- *"action order 1: mirred (Egress Redirect to device ${IF1}"*)
60
- info "${IF0} already has filter redirect action"
61
- ;;
62
- "")
63
- info "Adding filter redirect action from ${IF0} to ${IF1}"
64
- tc filter add dev ${IF0} parent ffff: protocol all u32 match u8 0 0 action \
65
- mirred egress redirect dev ${IF1} \
66
- || die "Could not add filter redirect action from ${IF0} to ${IF1}"
67
- ;;
68
- *)
69
- die "${IF0} has invalid filter redirect action or could not be queried"
70
- ;;
71
- esac
46
+ info "Adding filter redirect action from ${IF0} to ${IF1}"
47
+ tc filter del dev ${IF0} root
48
+ tc filter replace dev ${IF0} parent ffff: protocol all u32 match u8 0 0 action \
49
+ mirred egress redirect dev ${IF1} \
50
+ || die "Could not add filter redirect action from ${IF0} to ${IF1}"
72
51
  }
73
52
 
74
53
  # Parse arguments
package/mdc CHANGED
@@ -17,21 +17,18 @@ LS=$(which ls)
17
17
  RESOLVE_DEPS="${RESOLVE_DEPS-./node_modules/@lonocloud/resolve-deps/resolve-deps.py}"
18
18
  DOCKER_COMPOSE="${DOCKER_COMPOSE:-docker-compose}"
19
19
 
20
- [ -f "${RESOLVE_DEPS}" ] || die "Missing ${RESOLVE_DEPS}. Perhaps 'npm install'?"
20
+ which ${RESOLVE_DEPS} >/dev/null 2>/dev/null \
21
+ || die "Missing ${RESOLVE_DEPS}. Perhaps 'npm install'?"
22
+
23
+ # Resolve mode directory paths
24
+
21
25
  MODE_SPEC="${1}"; shift
22
- if [ "${RESOLVE_DEPS}" ]; then
23
- MODES="$(${RESOLVE_DEPS} "${MODES_DIR}" ${MODE_SPEC})"
24
- else
25
- MODES="${MODE_SPEC//,/ }"
26
- fi
26
+ RESOLVED_MODES="$(${RESOLVE_DEPS} --path "${MODES_DIR}" --format=paths ${MODE_SPEC})"
27
27
 
28
- echo >&2 "MODES: ${MODES}"
29
- vecho "ENV_FILE: ${ENV_FILE}"
28
+ # Create base (empty) compose file to anchor mode relative paths
29
+ # to the same root directory. Create files dir.
30
30
 
31
- declare -A FINISHED
32
31
  COMPOSE_FILE=./.compose-empty.yaml
33
- COMPOSE_PROFILES=
34
- MDC_MODE_DIRS=
35
32
  cat /dev/null > ${ENV_FILE}-mdc-tmp
36
33
  echo -e "version: '2.4'\nservices: {}" > ./.compose-empty.yaml
37
34
 
@@ -42,19 +39,25 @@ esac
42
39
  [ -d "${MDC_FILES_DIR}" ] && rm -r${VERBOSE:+v} ${MDC_FILES_DIR}/
43
40
  mkdir -p "${MDC_FILES_DIR}"
44
41
 
45
- for mode in ${MODES}; do
42
+ # Incorporate modes' compose config, env, and files
43
+
44
+ declare -A FINISHED
45
+ COMPOSE_PROFILES=
46
+ MDC_MODE_NAMES=
47
+ MDC_MODE_DIRS=
48
+ for resolved in ${RESOLVED_MODES}; do
49
+ mode=${resolved%=*}
50
+ path=${resolved#*=}
51
+
46
52
  # Only process each mode once
47
53
  [ "${FINISHED[${mode}]}" ] && continue
48
54
  FINISHED["${mode}"]=1
49
55
 
50
- # mode dir must exist
51
- [ -d "${MODES_DIR}/${mode}" ] || \
52
- die "No mode dir found for ${mode}"
53
-
54
- MDC_MODE_DIRS="${MDC_MODE_DIRS},${MODES_DIR}/${mode}"
56
+ MDC_MODE_NAMES="${MDC_MODE_NAMES} ${mode}"
57
+ MDC_MODE_DIRS="${MDC_MODE_DIRS},${path}"
55
58
 
56
59
  # mode can refer to a compose file in multiple ways
57
- cfiles="${MODES_DIR}/${mode}/compose.yaml ${MODES_DIR}/${mode}/compose.yml ${MODES_DIR}/${mode}/docker-compose.yaml ${MODES_DIR}/${mode}/docker-compose.yml"
60
+ cfiles="${path}/compose.yaml ${path}/compose.yml ${path}/docker-compose.yaml ${path}/docker-compose.yml"
58
61
  for cfile in ${cfiles}; do
59
62
  if [ -e "${cfile}" ]; then
60
63
  COMPOSE_FILE="${COMPOSE_FILE}:${cfile}"
@@ -66,27 +69,35 @@ for mode in ${MODES}; do
66
69
  COMPOSE_PROFILES="${COMPOSE_PROFILES},MODE_${mode}"
67
70
 
68
71
  # if there is a mode specific env file then include it
69
- efiles="${MODES_DIR}/${mode}/env ${MODES_DIR}/${mode}/.env"
72
+ efiles="${path}/env ${path}/.env"
70
73
  for efile in ${efiles}; do
71
74
  if [ -e "${efile}" ]; then
72
- echo "### mdc begin mode ${mode}" >> ${ENV_FILE}-mdc-tmp
75
+ echo "### mdc begin mode ${mode} (${efile})" >> ${ENV_FILE}-mdc-tmp
73
76
  vecho "cat ${efile} >> ${ENV_FILE}-mdc-tmp"
74
77
  cat ${efile} >> ${ENV_FILE}-mdc-tmp
75
- echo "### mdc end mode ${mode}" >> ${ENV_FILE}-mdc-tmp
78
+ echo >> ${ENV_FILE}-mdc-tmp
79
+ echo "### mdc end mode ${mode} (${efile})" >> ${ENV_FILE}-mdc-tmp
76
80
  fi
77
81
  done
78
82
 
79
83
  # if there are mode specific files then copy them to MDC_FILES_DIR
80
- if [ -d "${MODES_DIR}/${mode}" ]; then
81
- for vfd in $(cd ${MODES_DIR}/${mode} && $LS -d */files 2>/dev/null || true); do
84
+ if [ -d "${path}" ]; then
85
+ for vfd in $(cd ${path} && $LS -d */files 2>/dev/null || true); do
82
86
  dest=${MDC_FILES_DIR}/${vfd%/files}
83
87
  mkdir -p ${dest}
84
- vecho cp -a ${MODES_DIR}/${mode}/${vfd}/* ${dest}
85
- cp -a ${MODES_DIR}/${mode}/${vfd}/* ${dest}
88
+ vecho cp -a ${path}/${vfd}/* ${dest}
89
+ cp -a ${path}/${vfd}/* ${dest}
86
90
  done
87
91
  fi
88
92
  done
89
93
 
94
+ vecho
95
+
96
+ # Summarize and set env
97
+
98
+ echo >&2 "MODES: ${MDC_MODE_NAMES}"
99
+ vecho "ENV_FILE: ${ENV_FILE}"
100
+
90
101
  COMPOSE_FILE="${COMPOSE_FILE#:}"
91
102
  vecho "COMPOSE_FILE: ${COMPOSE_FILE}"
92
103
  echo "COMPOSE_FILE=${COMPOSE_FILE}" >> ${ENV_FILE}-mdc-tmp
@@ -100,6 +111,8 @@ MDC_MODE_DIRS="${MDC_MODE_DIRS#,}"
100
111
  vecho "MDC_MODE_DIRS: ${MDC_MODE_DIRS}"
101
112
  echo "MDC_MODE_DIRS=\"${MDC_MODE_DIRS}\"" >> ${ENV_FILE}-mdc-tmp
102
113
 
114
+ vecho
115
+
103
116
  vecho mv ${ENV_FILE}-mdc-tmp ${ENV_FILE}
104
117
  mv ${ENV_FILE}-mdc-tmp ${ENV_FILE}
105
118