conlink 2.0.3 → 2.2.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.
@@ -27,9 +27,11 @@ General Options:
27
27
  -v, --verbose Show verbose output (stderr)
28
28
  [env: VERBOSE]
29
29
  --show-config Print loaded network config JSON and exit
30
- --bridge-mode BRIDGE-MODE Bridge mode (ovs, linux, or auto)
30
+ --default-bridge-mode BRIDGE-MODE Default bridge mode (ovs, linux, patch, or auto)
31
31
  to use for bridge/switch connections
32
32
  [default: auto] [env: CONLINK_BRIDGE_MODE]
33
+ --default-mtu MTU Default link MTU (for non *vlan types)
34
+ [default: 65535]
33
35
  --network-file NETWORK-FILE... Network config file
34
36
  --compose-file COMPOSE-FILE... Docker compose file with network config
35
37
  --compose-project NAME Docker compose project name for resolving
@@ -57,11 +59,14 @@ General Options:
57
59
  (def VLAN-TYPES #{:vlan :macvlan :macvtap :ipvlan :ipvtap})
58
60
  (def LINK-ADD-OPTS [:ip :mac :route :mtu :nat :netem :mode :vlanid :remote :vni])
59
61
  (def INTF-MAX-LEN 15)
62
+ (def DOCKER-INTF "DOCKER-ETH0")
60
63
 
61
- (def ctx (atom {:error #(apply Eprintln "ERROR:" %&)
62
- :warn #(apply Eprintln "WARNING:" %&)
63
- :log Eprintln
64
- :info list}))
64
+ (def ctx (atom {:error #(apply Eprintln "ERROR:" %&)
65
+ :warn #(apply Eprintln "WARNING:" %&)
66
+ :log Eprintln
67
+ :info #(identity nil)
68
+ :kmod-ovs? false
69
+ :kmod-mirred? false}))
65
70
 
66
71
  ;; Simple utility functions
67
72
  (defn json-str [obj]
@@ -94,34 +99,95 @@ General Options:
94
99
  net-cfg))
95
100
 
96
101
  (defn enrich-link
97
- "Add default values to a link:
102
+ "Check and enrich link config
103
+ - Resolve bridge name to full bridge map.
104
+ - Add default values to a link:
98
105
  - type: veth
99
106
  - dev: eth0
100
- - mtu: 9000 (for non *vlan type)
107
+ - mtu: --default-mtu (for non *vlan type)
101
108
  - base: :conlink for veth type, :host for *vlan types, :local otherwise"
102
- [{:as link :keys [type base bridge ip vlanid]}]
103
- (let [type (keyword (or type "veth"))
109
+ [{:as link :keys [type base bridge ip forward]} bridges]
110
+ (let [{:keys [default-mtu docker-eth0?]} @ctx
111
+ type (keyword (or type "veth"))
112
+ dev (get link :dev "eth0")
104
113
  base-default (cond (= :veth type) :conlink
105
114
  (VLAN-TYPES type) :host
106
115
  :else :local)
107
116
  base (get link :base base-default)
117
+ bridge (get bridges bridge)
108
118
  link (merge
109
119
  link
110
120
  {:type type
111
- :dev (get link :dev "eth0")
121
+ :dev dev
112
122
  :base base}
123
+ (when bridge
124
+ {:bridge bridge})
113
125
  (when (not (VLAN-TYPES type))
114
- {:mtu (get link :mtu 9000)}))]
126
+ {:mtu (get link :mtu default-mtu)})
127
+ (when forward
128
+ {:forward
129
+ (map #(let [[port_a port_b proto] (S/split % #"[:/]")]
130
+ [(js/parseInt port_a) (js/parseInt port_b) proto])
131
+ forward)}))]
132
+ (when forward
133
+ (let [link-id (str (or (:service link) (:container link)) ":" dev)
134
+ pre (str "link '" link-id "' has forward setting")]
135
+ (when (not= :veth type)
136
+ (fatal 1 (str pre " but is not of type :veth")))
137
+ (when (not (#{:ovs :linux} (:mode bridge)))
138
+ (fatal 1 (str pre " but bridge mode is not 'ovs' or 'linux'")))
139
+ (when (not ip)
140
+ (fatal 1 (str pre " but does not have IP")))
141
+ (when (not docker-eth0?)
142
+ (fatal 1 (str pre " but no docker eth0 is present")))))
115
143
  link))
116
144
 
145
+ (defn enrich-bridge
146
+ "Check and enrich bridge config.
147
+ If bridge mode is :auto then return :ovs if the 'openvswitch' kernel module
148
+ is loaded otherwise fall back to :linux. Exit with an error if mode is :ovs
149
+ or :patch and the 'openvswitch' or 'act_mirred' kernel modules are not
150
+ loaded respectively."
151
+ [{:as bridge-opts :keys [bridge mode]}]
152
+ (let [{:keys [warn default-bridge-mode kmod-ovs? kmod-mirred?]} @ctx
153
+ mode (keyword (or mode default-bridge-mode))
154
+ _ (when (and (= :ovs mode) (not kmod-ovs?))
155
+ (fatal 1 (str "bridge " bridge " mode is 'ovs', "
156
+ "but no 'openvswitch' kernel module loaded")))
157
+ _ (when (and (= :patch mode) (not kmod-mirred?))
158
+ (warn (str "bridge " bridge " mode is 'patch', "
159
+ "but no 'act_mirred' kernel module loaded, "
160
+ " assuming it will load when needed.")))
161
+ _ (when (and (= :auto mode) (not kmod-ovs?))
162
+ (warn (str "bridge " bridge " mode is 'auto', "
163
+ " but no 'openvswitch' kernel module loaded, "
164
+ " so falling back to 'linux'")))
165
+ mode (if (= :auto mode)
166
+ (if kmod-ovs? :ovs :linux)
167
+ mode)]
168
+ (assoc bridge-opts :mode mode)))
169
+
117
170
  (defn enrich-network-config
118
- "Validate and update each link (enrich-link) and add
119
- :containers and :services maps with restructured link and command
120
- configuration to provide a more efficient structure for looking up
121
- configuration later."
122
- [{:as cfg :keys [links commands]}]
123
- (let [links (vec (map enrich-link links))
124
- cfg (merge cfg {:links links :containers {} :services {}})
171
+ "Validate and update each bridge (enrich-bridge) and link (enrich-link) and
172
+ add :bridges, :containers, and :services maps with restructured bridge, link,
173
+ and command configuration to provide a more efficient structure for looking
174
+ up configuration later."
175
+ [{:as cfg :keys [links commands bridges]}]
176
+ (let [bridge-map (reduce (fn [acc b] (assoc acc (:bridge b) b))
177
+ {} bridges)
178
+ ;; Add bridges specified in links only
179
+ all-bridges (reduce (fn [bs b]
180
+ (assoc bs b (get bs b {:bridge b})))
181
+ bridge-map
182
+ (keep :bridge links))
183
+ ;; Enrich each bridge
184
+ bridges (reduce (fn [bs [k v]] (assoc bs k (enrich-bridge v)))
185
+ {} all-bridges)
186
+ links (mapv #(enrich-link % bridges) links)
187
+ cfg (merge cfg {:links links
188
+ :bridges bridges
189
+ :containers {}
190
+ :services {}})
125
191
  rfn (fn [kind cfg {:as x :keys [container service]}]
126
192
  (cond-> cfg
127
193
  container (update-in [:containers container kind] conjv x)
@@ -153,15 +219,17 @@ General Options:
153
219
  "\nUser config:\n" (indent-pprint-str data " "))
154
220
  "\nValidation errors:\n" msg))))))
155
221
 
222
+
223
+ ;;; Runtime state related
224
+
156
225
  (defn gen-network-state
157
226
  "Generate network state/context from network configuration. Adds
158
227
  empty :devices map and :bridges map containing nil status for
159
228
  each bridge mentioned in the network config :links and :tunnels."
160
- [{:keys [links tunnels]}]
161
- (reduce (fn [state bridge]
162
- (assoc-in state [:bridges bridge :status] nil))
163
- {:devices {} :bridges {}}
164
- (keep :bridge (concat links tunnels))))
229
+ [{:keys [links tunnels bridges]}]
230
+ {:devices {}
231
+ :bridges (into {} (for [[k v] bridges]
232
+ [k (merge v {:status nil :links #{}})]))})
165
233
 
166
234
  (defn link-outer-dev
167
235
  "outer-dev format:
@@ -179,7 +247,7 @@ General Options:
179
247
  "Add offset value to ip and mac keys in a link definition to account
180
248
  for multiple instances of that link i.e. a compose service with
181
249
  multiple replicas (scale >= 2)."
182
- [{:as link :keys [ip mac]} offset]
250
+ [{:as link :keys [ip mac forward]} offset]
183
251
  ;; TODO: add vlanid
184
252
  (let [mac (when mac
185
253
  (addrs/int->mac
@@ -188,8 +256,15 @@ General Options:
188
256
  (let [[ip prefix] (S/split ip #"/")]
189
257
  (str (addrs/int->ip
190
258
  (+ offset (addrs/ip->int ip)))
191
- "/" prefix)))]
192
- (merge link (when mac {:mac mac}) (when ip {:ip ip}))))
259
+ "/" prefix)))
260
+ forward (when forward
261
+ (map #(let [[port_a port_b proto] %]
262
+ [(+ offset port_a) port_b proto])
263
+ forward))]
264
+ (merge link
265
+ (when mac {:mac mac})
266
+ (when ip {:ip ip})
267
+ (when forward {:forward forward}))))
193
268
 
194
269
 
195
270
  (defn link-instance-enrich
@@ -261,26 +336,36 @@ General Options:
261
336
  (P/recur cmds)
262
337
  res))))
263
338
 
339
+ (defn kmod-loaded?
340
+ "Return whether kernel module 'kmod' is loaded."
341
+ [kmod]
342
+ (P/let [cmd (str "grep -o '^" kmod "\\>' /proc/modules")
343
+ res (run cmd {:quiet true})]
344
+ (and (= 0 (:code res)) (= kmod (trim (:stdout res))))))
345
+
346
+ (defn intf-exists?
347
+ "Return whether network interface exists"
348
+ [intf]
349
+ (P/let [cmd (str "[ -d /sys/class/net/" intf " ]")
350
+ res (run cmd {:quiet true})]
351
+ (= 0 (:code res))))
352
+
264
353
  (defn rename-docker-eth0
265
- "If eth0 exists, then rename it to DOCKER-ETH0 to prevent 'RTNETLINK
354
+ "Rename docker's provided eth0 to DOCKER-INTF to prevent 'RTNETLINK
266
355
  answers: File exists' errors during creation of links that use
267
356
  'eth0' device name. This is necessary because even if the netns is
268
357
  specified with the same link create command, the creation and move
269
358
  does not appear to be idempotent and results in the conflict."
270
359
  []
271
360
  (P/let [{:keys [log]} @ctx
272
- res (run "[ -d /sys/class/net/eth0 ]" {:quiet true})]
273
- (if (not= 0 (:code res))
274
- (log "No eth0 docker network interface detected")
275
- (P/let [_ (log "Renaming eth0 to DOCKER-ETH0")
276
- res (run* [(str "ip route save dev eth0 > /tmp/routesave")
277
- (str "ip link set eth0 down")
278
- (str "ip link set eth0 name DOCKER-ETH0")
279
- (str "ip link set DOCKER-ETH0 up")
280
- (str "ip route restore < /tmp/routesave")]
281
- {:id "rename"})]
282
- (when (not= 0 (:code res))
283
- (fatal 1 "Could not rename docker eth0 interface"))))))
361
+ res (run* [(str "ip route save dev eth0 > /tmp/routesave")
362
+ (str "ip link set eth0 down")
363
+ (str "ip link set eth0 name " DOCKER-INTF)
364
+ (str "ip link set " DOCKER-INTF " up")
365
+ (str "ip route restore < /tmp/routesave")]
366
+ {:id "rename"})]
367
+ (when (not= 0 (:code res))
368
+ (fatal 1 "Could not rename docker eth0 interface"))))
284
369
 
285
370
  (defn start-ovs
286
371
  "Start and initialize the openvswitch daemons. Exit with error if it
@@ -291,88 +376,127 @@ General Options:
291
376
  (fatal 1 (str "Failed starting OVS: " (:stderr res)))
292
377
  res)))
293
378
 
294
- (defn kmod-loaded?
295
- "Return whether kernel module 'kmod' is loaded."
296
- [kmod]
297
- (P/let [cmd (str "grep -o '^" kmod "\\>' /proc/modules")
298
- res (run cmd {:quiet true})]
299
- (and (= 0 (:code res)) (= kmod (trim (:stdout res))))))
300
-
301
- ;;; Link and bridge commands
379
+ ;;; Bridge commands
302
380
 
303
381
  (defn check-no-bridge
304
382
  "Check that no bridge named 'bridge' is currently configured.
305
- Bridge type is dependent on bridge-mode (:ovs or :linux). Exit with
383
+ Bridge type is dependent on mode (:ovs or :linux). Exit with
306
384
  error if the bridge already exists."
307
- [bridge]
308
- (P/let [{:keys [info bridge-mode]} @ctx
385
+ [{:keys [bridge mode]}]
386
+ (P/let [{:keys [info]} @ctx
309
387
  cmd (get {:ovs (str "ovs-vsctl list-ifaces " bridge)
310
- :linux (str "ip link show type bridge " bridge)}
311
- bridge-mode)
312
- res (run cmd {:quiet true})]
313
- (if (= 0 (:code res))
314
- ;; TODO: maybe mark as :exists and use without cleanup
315
- (fatal 1 (str "Bridge " bridge " already exists"))
316
- (if (re-seq #"(does not exist|no bridge named)" (:stderr res))
317
- true
318
- (fatal 1 (str "Unable to run '" cmd "': " (:stderr res)))))))
388
+ :linux (str "ip link show type bridge " bridge)
389
+ :patch nil}
390
+ mode)]
391
+ (if (not cmd)
392
+ true
393
+ (P/let [res (run cmd {:quiet true})]
394
+ (if (= 0 (:code res))
395
+ ;; TODO: maybe mark as :exists and use without cleanup
396
+ (fatal 1 (str "Bridge " bridge " already exists"))
397
+ (if (re-seq #"(does not exist|no bridge named)" (:stderr res))
398
+ true
399
+ (fatal 1 (str "Unable to run '" cmd "': " (:stderr res)))))))))
319
400
 
320
401
 
321
402
  (defn bridge-create
322
403
  "Create a bridge named 'bridge'.
323
- Bridge type is dependent on bridge-mode (:ovs or :linux)."
324
- [bridge]
325
- (P/let [{:keys [info error bridge-mode]} @ctx
326
- _ (info "Creating bridge/switch" bridge)
404
+ Bridge type is dependent on mode (:ovs or :linux)."
405
+ [{:keys [bridge mode]}]
406
+ (P/let [{:keys [info error]} @ctx
327
407
  cmd (get {:ovs (str "ovs-vsctl add-br " bridge)
328
- :linux (str "ip link add " bridge " up type bridge")}
329
- bridge-mode)
330
- res (run cmd)]
331
- (if (not= 0 (:code res))
332
- (error (str "Unable to create bridge/switch " bridge))
333
- (swap! ctx assoc-in [:network-state :bridges bridge :status] :created))
334
- res))
408
+ :linux (str "ip link add " bridge " up type bridge")
409
+ :patch nil}
410
+ mode)]
411
+ (if (not cmd)
412
+ (info (str "Ignoring bridge/switch " bridge " for mode " mode))
413
+ (P/let [_ (info "Creating bridge/switch" bridge)
414
+ res (run* [cmd (str "ip link set " bridge " up")])]
415
+ (if (not= 0 (:code res))
416
+ (error (str "Unable to create bridge/switch " bridge))
417
+ (swap! ctx assoc-in [:network-state :bridges bridge :status] :created))
418
+ true))))
335
419
 
336
420
  (defn bridge-del
337
421
  "Delete the bridge named 'bridge'.
338
- Bridge type is dependent on bridge-mode (:ovs or :linux)."
339
- [bridge]
340
- (P/let [{:keys [info error bridge-mode]} @ctx
341
- _ (info "Deleting bridge/switch" bridge)
422
+ Bridge type is dependent on mode (:ovs or :linux)."
423
+ [{:keys [bridge mode]}]
424
+ (P/let [{:keys [info error]} @ctx
342
425
  cmd (get {:ovs (str "ovs-vsctl del-br " bridge)
343
- :linux (str "ip link del " bridge)} bridge-mode)
344
- res (run cmd)]
345
- (if (not= 0 (:code res))
346
- (error (str "Unable to delete bridge " bridge))
347
- (swap! ctx assoc-in [:network-state :bridges bridge :status] nil))
348
- res))
426
+ :linux (str "ip link del " bridge)
427
+ :patch nil} mode)]
428
+ (if (not cmd)
429
+ (info (str "Ignoring bridge/switch " bridge " for mode " mode))
430
+ (P/let [_ (info "Deleting bridge/switch" bridge)
431
+ res (run cmd)]
432
+ (if (not= 0 (:code res))
433
+ (error (str "Unable to delete bridge " bridge))
434
+ (swap! ctx assoc-in [:network-state :bridges bridge :status] nil))
435
+ true))))
349
436
 
350
437
  (defn bridge-add-link
351
438
  "Add the link/interface 'dev' to the bridge 'bridge'.
352
- Bridge type is dependent on bridge-mode (:ovs or :linux)."
353
- [bridge dev]
354
- (P/let [{:keys [error bridge-mode]} @ctx
439
+ Bridge type is dependent on mode (:ovs or :linux)."
440
+ [{:keys [bridge mode]} dev]
441
+ (P/let [{:keys [error]} @ctx
355
442
  cmd (get {:ovs (str "ovs-vsctl add-port " bridge " " dev)
356
443
  :linux (str "ip link set dev " dev " master " bridge)}
357
- bridge-mode)
444
+ mode)
358
445
  res (run cmd)]
359
- (when (not= 0 (:code res))
360
- (error (str "Unable to add link " dev " into " bridge)))
361
- res))
446
+ (if (= 0 (:code res))
447
+ (swap! ctx update-in [:network-state :bridges bridge :links] conj dev)
448
+ (error (str "Unable to add link " dev " into " bridge)))))
362
449
 
363
450
  (defn bridge-drop-link
364
451
  "Remove the link/interface 'dev' from the bridge 'bridge'.
365
- Bridge type is dependent on bridge-mode (:ovs or :linux)."
366
- [bridge dev]
367
- (P/let [{:keys [error bridge-mode]} @ctx
452
+ Bridge type is dependent on mode (:ovs or :linux)."
453
+ [{:keys [bridge mode]} dev]
454
+ (P/let [{:keys [error]} @ctx
368
455
  cmd (get {:ovs (str "ovs-vsctl del-port " bridge " " dev)
369
456
  :linux (str "ip link set dev " dev " nomaster")}
370
- bridge-mode)
457
+ mode)
371
458
  res (run cmd)]
372
- (when (not= 0 (:code res))
373
- (error (str "Unable to drop link " dev " from " bridge)))
374
- res))
459
+ (if (= 0 (:code res))
460
+ (swap! ctx update-in [:network-state :bridges bridge :links] disj dev)
461
+ (error (str "Unable to drop link " dev " from " bridge)))))
462
+
463
+ (defn patch-add-link
464
+ "Setup patch between 'dev' and its peer link using tc qdisc mirred
465
+ filter action. Peer links are tracked in pseudo-bridge 'bridge'."
466
+ [{:keys [bridge mode]} dev]
467
+ (let [{:keys [info error]} @ctx
468
+ links-path [:network-state :bridges bridge :links]
469
+ links (get-in @ctx links-path)
470
+ peers (disj links dev)]
471
+ (condp = (count peers)
472
+ 0
473
+ (P/do
474
+ (info (str "Registering first peer link "
475
+ dev " in :patch 'bridge' " bridge))
476
+ (swap! ctx update-in links-path conj dev))
477
+
478
+ 1
479
+ (P/let [cmd (str "link-mirred.sh " dev " " (first peers))
480
+ res (run cmd)]
481
+ (if (= 0 (:code res))
482
+ (swap! ctx update-in links-path conj dev)
483
+ (error (str "Failed to setup tc filter action for "
484
+ dev " in :patch 'bridge' " bridge))))
485
+
486
+ (error "Cannot add third peer link "
487
+ dev " to :patch 'bridge' " bridge))))
375
488
 
489
+ (defn patch-drop-link
490
+ "Remove tracking of 'dev' from pseudo-bridge 'bridge'."
491
+ [{:keys [bridge mode]} dev]
492
+ (let [{:keys [info error]} @ctx
493
+ links-path [:network-state :bridges bridge :links]]
494
+ (info (str "Removing peer link "
495
+ dev " from :patch 'bridge' " bridge))
496
+ ;; State is in the links, no extra cleanup
497
+ (swap! ctx update-in links-path conj dev)))
498
+
499
+ ;;; Link commands
376
500
 
377
501
  (defn link-add
378
502
  "Create a link/interface defined by 'link' in a container by calling
@@ -380,7 +504,7 @@ General Options:
380
504
  line arguments from the 'link' definition and reports the results."
381
505
  [link]
382
506
  (P/let [{:keys [error]} @ctx
383
- {:keys [type dev outer-dev pid outer-pid container dev-id]} link
507
+ {:keys [type dev outer-dev pid outer-pid dev-id]} link
384
508
  cmd (str "link-add.sh"
385
509
  " '" (name type) "' '" pid "' '" dev "'"
386
510
  (when outer-pid (str " --pid1 " outer-pid))
@@ -409,6 +533,26 @@ General Options:
409
533
  (error (str "Unable to delete " dev-id ": " (:stderr res)))))
410
534
  res))
411
535
 
536
+ ;;; Port forward command
537
+
538
+ (defn forward-modify
539
+ "Depending on 'action' create ('add') or delete ('del') port
540
+ forwards defined by :forward property of 'link'."
541
+ [link action]
542
+ (P/let [{:keys [error]} @ctx
543
+ {:keys [outer-dev dev-id bridge ip forward]} link]
544
+ (P/all (for [fwd forward]
545
+ (P/let [[port_a port_b proto] fwd
546
+ ip (S/replace ip #"/.*" "")
547
+ cmd (str "link-forward.sh " action
548
+ " " DOCKER-INTF " " (:bridge bridge)
549
+ " " port_a ":" ip ":" port_b "/" proto)
550
+ res (run cmd {:id dev-id})]
551
+ (when (not= 0 (:code res))
552
+ (error (str "Unable to " action " forward "
553
+ "'" fwd "' for " dev-id)))
554
+ res)))))
555
+
412
556
 
413
557
  ;;; docker/docker-compose utilities
414
558
 
@@ -507,7 +651,7 @@ General Options:
507
651
  [link action]
508
652
  (P/let
509
653
  [{:keys [error log]} @ctx
510
- {:keys [type outer-dev bridge dev-id]} link
654
+ {:keys [type outer-dev bridge dev-id forward]} link
511
655
  status-path [:network-state :devices dev-id :status]
512
656
  link-status (get-in @ctx status-path)]
513
657
  (log (str (get {"start" "Creating" "die" "Deleting"} action)
@@ -519,7 +663,11 @@ General Options:
519
663
  (P/do
520
664
  (swap! ctx assoc-in status-path :creating)
521
665
  (link-add link)
522
- (when bridge (bridge-add-link bridge outer-dev))
666
+ (when bridge
667
+ (if (= :patch (:mode bridge))
668
+ (patch-add-link bridge outer-dev)
669
+ (bridge-add-link bridge outer-dev)))
670
+ (when forward (forward-modify link "add"))
523
671
  (swap! ctx assoc-in status-path :created)))
524
672
 
525
673
  "die"
@@ -527,7 +675,11 @@ General Options:
527
675
  (error (str "Link " dev-id " does not exist"))
528
676
  (P/do
529
677
  (swap! ctx assoc-in status-path :deleting)
530
- (when bridge (bridge-drop-link bridge outer-dev))
678
+ (when forward (forward-modify link "del"))
679
+ (when bridge
680
+ (if (= :patch (:mode bridge))
681
+ (patch-drop-link bridge outer-dev)
682
+ (bridge-drop-link bridge outer-dev)))
531
683
  (link-del link)
532
684
  (swap! ctx assoc-in status-path nil))))))
533
685
 
@@ -635,7 +787,7 @@ General Options:
635
787
  (when (seq bridges)
636
788
  (P/do
637
789
  (log (str "Removing bridges: " (S/join ", " (keys bridges))))
638
- (P/all (map bridge-del (keys bridges)))))
790
+ (P/all (map bridge-del (vals bridges)))))
639
791
  (js/process.exit 127))))
640
792
 
641
793
 
@@ -650,40 +802,6 @@ General Options:
650
802
  (when (empty? config-schema)
651
803
  (fatal 2 "Could not find config-schema" orig-config-schema)))
652
804
 
653
- (defn startup-checks
654
- "Check startup state and return map of :bridge-mode, :docker, and
655
- :podman. If bridge-mode is :auto then return :ovs if the
656
- 'openvswitch' kernel module is loaded otherwise fall back to :linux.
657
- Exit with an error if bridge-mode is :ovs and the 'openvswitch'
658
- kernel module is not loaded or if neither a docker or podman
659
- connection could be established."
660
- [{:keys [bridge-mode docker-socket podman-socket]}]
661
- (P/let
662
- [{:keys [info warn]} @ctx
663
- ovs? (kmod-loaded? "openvswitch")
664
- bridge-mode (condp = [bridge-mode ovs?]
665
- [:auto true]
666
- :ovs
667
-
668
- [:auto false]
669
- (do
670
- (warn (str "bridge-mode is 'auto' but no 'openvswitch' "
671
- "kernel module loaded, so using 'linux'"))
672
- :linux)
673
-
674
- [:ovs false]
675
- (fatal 1 (str "bridge-mode is 'ovs', but no 'openvswitch' "
676
- "kernel module loaded"))
677
-
678
- bridge-mode)
679
- docker (docker-client docker-socket)
680
- podman (docker-client podman-socket)]
681
- (when (and (not docker) (not podman))
682
- (fatal 1 "Failed to start either docker or podman client/listener"))
683
- {:bridge-mode bridge-mode
684
- :docker docker
685
- :podman podman}))
686
-
687
805
  (defn server
688
806
  "Process:
689
807
  - parse/validate command line options
@@ -692,7 +810,7 @@ General Options:
692
810
  - determine our own container ID and compose properties (if any)
693
811
  - generate runtime network state and other process context/state
694
812
  - install exit/cleanup handlers
695
- - start/init openvswitch daemons/config (if :ovs bridge-mode)
813
+ - start/init openvswitch daemons/config (if any bridges use :ovs mode)
696
814
  - check that any defined bridges do not already exist
697
815
  - create any bridges defined in network config links
698
816
  - start listening/handling docker/podman container events
@@ -704,7 +822,7 @@ General Options:
704
822
  {:keys [log info]} (swap! ctx merge (when verbose {:info Eprintln}))
705
823
  opts (merge
706
824
  opts
707
- {:bridge-mode (keyword (:bridge-mode opts))
825
+ {:default-bridge-mode (keyword (:default-bridge-mode opts))
708
826
  :orig-config-schema (:config-schema opts)
709
827
  :config-schema (resolve-path (:config-schema opts) SCHEMA-PATHS)
710
828
  :network-file (mapcat #(S/split % #":") (:network-file opts))
@@ -715,7 +833,16 @@ General Options:
715
833
  {:keys [network-file compose-file compose-project]} opts
716
834
  env (js->clj (js/Object.assign #js {} js/process.env))
717
835
  self-pid js/process.pid
836
+ self-cid (get-container-id)
718
837
  schema (load-config (:config-schema opts))
838
+ kmod-ovs? (kmod-loaded? "openvswitch")
839
+ kmod-mirred? (kmod-loaded? "act_mirred")
840
+ docker-eth0? (and self-cid (intf-exists? "eth0"))
841
+ _ (swap! ctx merge {:default-bridge-mode (:default-bridge-mode opts)
842
+ :default-mtu (:default-mtu opts)
843
+ :kmod-ovs? kmod-ovs?
844
+ :kmod-mirred? kmod-mirred?
845
+ :docker-eth0? docker-eth0?})
719
846
  network-config (P/-> (load-configs compose-file network-file)
720
847
  (interpolate-walk env)
721
848
  (check-schema schema verbose)
@@ -724,8 +851,11 @@ General Options:
724
851
  (println (js/JSON.stringify (->js network-config)))
725
852
  (js/process.exit 0))
726
853
 
727
- {:keys [bridge-mode docker podman]} (startup-checks opts)
728
- self-cid (get-container-id)
854
+ docker (docker-client (:docker-socket opts))
855
+ podman (docker-client (:podman-socket opts))
856
+ _ (when (and (not docker) (not podman))
857
+ (fatal 1 "Failed to start either docker or podman client/listener"))
858
+
729
859
  self-container-obj (when self-cid
730
860
  (get-container (or docker podman) self-cid))
731
861
  self-container (inspect-container self-container-obj)
@@ -733,8 +863,7 @@ General Options:
733
863
  {:project compose-project}
734
864
  (get-compose-labels self-container))
735
865
  network-state (gen-network-state network-config)
736
- ctx-data {:bridge-mode bridge-mode
737
- :network-config network-config
866
+ ctx-data {:network-config network-config
738
867
  :network-state network-state
739
868
  :compose-opts compose-opts
740
869
  :docker docker
@@ -749,7 +878,6 @@ General Options:
749
878
  (js/process.on "SIGTERM" #(exit-handler % "signal"))
750
879
  (js/process.on "uncaughtException" #(exit-handler %1 %2))
751
880
 
752
- (log "Bridge mode:" (name bridge-mode))
753
881
  (log (str "Using schema at '" (:config-schema opts) "'"))
754
882
  (info (str "Starting network config\n"
755
883
  (indent-pprint-str network-config " ")))
@@ -761,18 +889,20 @@ General Options:
761
889
  (info "Detected compose context:" compose-project))
762
890
 
763
891
  (P/do
764
- (when self-cid
892
+ (when docker-eth0?
893
+ (log "Renaming eth0 to" DOCKER-INTF)
765
894
  (rename-docker-eth0))
766
895
 
767
- (when (= :ovs bridge-mode)
896
+ (when (some #(= :ovs (:mode %)) (-> network-config :bridges vals))
768
897
  (start-ovs))
769
898
 
770
899
  ;; Check that bridges/switches do not already exist
771
- (P/all (for [bridge (-> network-state :bridges keys)]
900
+ (P/all (for [bridge (vals (:bridges network-state))]
772
901
  (check-no-bridge bridge)))
902
+
773
903
  ;; Create bridges/switch configs
774
904
  ;; TODO: should be done on-demand
775
- (P/all (for [bridge (-> network-state :bridges keys)]
905
+ (P/all (for [bridge (vals (:bridges network-state))]
776
906
  (bridge-create bridge)))
777
907
 
778
908
  ;; Create tunnels configs