conlink 2.0.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.
Files changed (59) hide show
  1. package/.dockerignore +5 -0
  2. package/Dockerfile +34 -0
  3. package/LICENSE +373 -0
  4. package/README.md +485 -0
  5. package/TODO +34 -0
  6. package/conlink +11 -0
  7. package/conlink-start.sh +172 -0
  8. package/examples/dot.js +36 -0
  9. package/examples/index.html +11 -0
  10. package/examples/net2dot.yaml +21 -0
  11. package/examples/test1-compose.yaml +60 -0
  12. package/examples/test2-compose.yaml +31 -0
  13. package/examples/test2-network.yaml +5 -0
  14. package/examples/test3-network.yaml +5 -0
  15. package/examples/test4-multiple/all-compose.yaml +5 -0
  16. package/examples/test4-multiple/base-compose.yaml +25 -0
  17. package/examples/test4-multiple/node1-compose.yaml +17 -0
  18. package/examples/test4-multiple/nodes2-compose.yaml +20 -0
  19. package/examples/test4-multiple/web-network.yaml +2 -0
  20. package/examples/test5-geneve-compose.yaml +31 -0
  21. package/examples/test6-cfn.yaml +184 -0
  22. package/examples/test7-compose.yaml +31 -0
  23. package/examples/test8-compose.yaml +35 -0
  24. package/host-build.yaml +1 -0
  25. package/inspect.json +210 -0
  26. package/link-add.sh +197 -0
  27. package/link-del.sh +60 -0
  28. package/net2dot +11 -0
  29. package/notes.txt +82 -0
  30. package/old/Dockerfile.bak +26 -0
  31. package/old/add-link.sh +82 -0
  32. package/old/conlink +12 -0
  33. package/old/conlink.cljs +131 -0
  34. package/old/dot_gitignore +1 -0
  35. package/old/examples/test2-compose.yaml +32 -0
  36. package/old/examples/test2-network.yaml +42 -0
  37. package/old/move-link.sh +108 -0
  38. package/old/net2dot.py +122 -0
  39. package/old/notes-old.txt +97 -0
  40. package/old/package.json +16 -0
  41. package/old/schema.yaml +138 -0
  42. package/old/schema.yaml.bak +76 -0
  43. package/old/test2b-compose.yaml +18 -0
  44. package/old/veth-link.sh +96 -0
  45. package/package.json +15 -0
  46. package/schema-ish.yaml +29 -0
  47. package/schema.yaml +71 -0
  48. package/shadow-cljs.edn +33 -0
  49. package/src/conlink/addrs.cljc +63 -0
  50. package/src/conlink/core.cljs +772 -0
  51. package/src/conlink/net2dot.cljs +158 -0
  52. package/src/conlink/util.cljs +140 -0
  53. package/tests/invalid-schema-1.yaml +6 -0
  54. package/tests/invalid-schema-2.yaml +6 -0
  55. package/tests/invalid-schema-3.yaml +17 -0
  56. package/tests/invalid-schema-4.yaml +14 -0
  57. package/tests/invalid-schema-5.yaml +12 -0
  58. package/tests/invalid-schema-6.yaml +12 -0
  59. package/tmp/conlink/.env +1 -0
@@ -0,0 +1,76 @@
1
+ $defs:
2
+ one_link:
3
+
4
+ type: object
5
+ additionalProperties: false
6
+ required: ["links"]
7
+ properties:
8
+ links:
9
+ type: array
10
+ items:
11
+ type: object
12
+ # oneOf: [{required: ["container"]},
13
+ # {required: ["service"]},
14
+ # #{required: ["bridge"], properties: {type: {const: {oneOf: ["geneve", "vxlan"]}}}}]
15
+ # #{properties: {type: {const: "geneve"}}, required: ["bridge"]},
16
+ # #{properties: {type: {const: "vxlan"}}, required: ["bridge"]}
17
+ # {allOf: [{properties: {type: {const: "geneve"}}},
18
+ # {properties: {type: {const: "vxlan"}}}]}
19
+ # ]
20
+
21
+ # if: {properties: {type: {const: "veth"}}}
22
+ # then:
23
+ # allOf:
24
+ # - {required: ["bridge"]}
25
+ # - {not: {required: ["vlanid", "vni", "remote"]}}
26
+ # else:
27
+ # not: {required: ["bridge"]}
28
+ # if: {properties: {type: {const: "vlan"}}}
29
+ # then: {required: ["vlanid"]}
30
+ # else: {not: {required: ["vlanid"]}}
31
+
32
+ allOf:
33
+ - if: {properties: {type: {const: "veth"}}}
34
+ then: {required: ["bridge"]}
35
+ else: {not: {required: ["BOGUS"]}}
36
+ - if: {properties: {type: {const: "geneve"}}}
37
+ then: {required: ["bridge", "remote", "vni"]}
38
+ #else: {not: {required: ["bridge", "remote", "vni"]}}
39
+ else: {not: {required: ["BOGUS"]}}
40
+ - if: {properties: {type: {const: "vxlan"}}}
41
+ then: {required: ["bridge", "remote", "vni"]}
42
+ #else: {not: {required: ["bridge", "remote", "vni"]}}
43
+ else: {not: {required: ["BOGUS"]}}
44
+
45
+ additionalProperties: false
46
+ properties:
47
+ type:
48
+ type: string
49
+ default: "veth"
50
+ enum: [veth, dummy, vlan, ipvlan, ipvtap, macvlan, macvtap, geneve, vxlan]
51
+ service: {type: string}
52
+ container: {type: string}
53
+ bridge: {type: string}
54
+ dev: {type: string, default: "eth0"}
55
+ ip: {type: string, pattern: "^([0-9]{1,3}[.]){3}[0-9]+/[0-9]*$"}
56
+ mac: {type: string, pattern: "^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$"}
57
+ mtu: {type: number}
58
+ route: {type: string}
59
+
60
+ vlanid: {type: number}
61
+ nat: {type: string, pattern: "^([0-9]{1,3}[.]){3}[0-9]+/[0-9]*$"}
62
+
63
+ vni: {type: number}
64
+ remote: {type: string, pattern: "^([0-9]{1,3}[.]){3}[0-9]+$"}
65
+
66
+ commands:
67
+ type: array
68
+ items:
69
+ type: object
70
+ required: ["command"]
71
+ oneOf: [{required: ["container"]},
72
+ {required: ["service"]}]
73
+ properties:
74
+ service: {type: string}
75
+ container: {type: string}
76
+ command: {type: string}
@@ -0,0 +1,18 @@
1
+ # A docker-compose file with an external network configuration file
2
+ # and docker containers that are connected via a switch. The
3
+ # "internet" container is setup that is also connected to the switch
4
+ # and is listening on 8.8.8.8 (to test routing from the nodes).
5
+
6
+ version: "2.4"
7
+
8
+ services:
9
+ node:
10
+ image: alpine
11
+ network_mode: none
12
+ scale: 2
13
+ command: sleep 864000
14
+
15
+ internet:
16
+ image: alpine
17
+ network_mode: none
18
+ command: sleep 864000
@@ -0,0 +1,96 @@
1
+ #!/bin/bash
2
+
3
+ # Copyright (c) 2023, Viasat, Inc
4
+ # Licensed under MPL 2.0
5
+
6
+ set -e
7
+
8
+ usage () {
9
+ echo >&2 "${0} [OPTIONS] INTF0 INTF1 PID0 PID1"
10
+ echo >&2 ""
11
+ echo >&2 " INTF0 is the name of first veth interface"
12
+ echo >&2 " INTF1 is the name of second veth interface name"
13
+ echo >&2 " PID0 is the process ID of the first netns"
14
+ echo >&2 " PID1 is the process ID of the second netns"
15
+ echo >&2 ""
16
+ echo >&2 "OPTIONS:"
17
+ echo >&2 " --verbose - Verbose output (set -x)"
18
+ echo >&2 " --ip0 IP0 - IP (CIDR) address for INTF0"
19
+ echo >&2 " --ip1 IP1 - IP (CIDR) address for INTF1"
20
+ echo >&2 " --mac0 MAC0 - MAC address for INTF0"
21
+ echo >&2 " --mac1 MAC1 - MAC address for INTF1"
22
+ echo >&2 " --route0 'ROUTE' - route to add to INTF0"
23
+ echo >&2 " --route1 'ROUTE' - route to add to INTF1"
24
+ echo >&2 " --mtu MTU - MTU for both interfaces"
25
+ exit 2
26
+ }
27
+
28
+ VERBOSE=${VERBOSE:-}
29
+ IP0= IP1= MAC0= MAC1= ROUTE0= ROUTE1= MTU=
30
+
31
+ info() { echo "veth-link [${PID0}/${IF0} <-> ${PID1}/${IF1}] ${*}"; }
32
+ warn() { >&2 echo "veth-link [${PID0}/${IF0} <-> ${PID1}/${IF1}] ${*}"; }
33
+ die() { warn "ERROR: ${*}"; exit 1; }
34
+
35
+ # Set name, MAC, IP, ROUTE, MTU, and up state for interface within netns
36
+ setup_if() {
37
+ local SIF=$1 IF=$2 NS=$3 MAC=$4 IP=$5 ROUTE=$6 MTU=$7
38
+
39
+ ip -netns ${NS} --force -b - <<EOF
40
+ link set dev ${SIF} name ${IF}
41
+ ${IP:+addr add ${IP} dev ${IF}}
42
+ ${MAC:+link set dev ${IF} address ${MAC}}
43
+ ${MTU:+link set dev ${IF} mtu ${MTU}}
44
+ link set dev ${IF} up
45
+ ${ROUTE:+route add ${ROUTE} dev ${IF}}
46
+ EOF
47
+ }
48
+
49
+ # Parse arguments
50
+ positional=
51
+ while [ "${*}" ]; do
52
+ param=$1; OPTARG=$2
53
+ case ${param} in
54
+ --verbose) VERBOSE=1 ;;
55
+ --ip0) IP0="${OPTARG}"; shift ;;
56
+ --ip1) IP1="${OPTARG}"; shift ;;
57
+ --mac0) MAC0="${OPTARG}"; shift ;;
58
+ --mac1) MAC1="${OPTARG}"; shift ;;
59
+ --route0) ROUTE0="${OPTARG}"; shift ;;
60
+ --route1) ROUTE1="${OPTARG}"; shift ;;
61
+ --mtu) MTU="${OPTARG}"; shift ;;
62
+ -h|--help) usage ;;
63
+ *) positional="${positional} $1" ;;
64
+ esac
65
+ shift
66
+ done
67
+ set -- ${positional}
68
+ IF0=$1 IF1=$2 PID0=$3 PID1=$4
69
+
70
+ [ "${VERBOSE}" ] && set -x || true
71
+
72
+ # Check arguments
73
+ [ "${IF0}" -a "${IF1}" -a "${PID0}" -a "${PID1}" ] || usage
74
+
75
+ # Sanity checks
76
+ [ ! -d /proc/$PID0 ] && die "PID0 $PID0 is no longer running!"
77
+ [ ! -d /proc/$PID1 ] && die "PID1 $PID1 is no longer running!"
78
+
79
+ ### Do the work
80
+
81
+ info "Creating veth pair link (${IP0}|${MAC0} <-> ${IP1}|${MAC1})"
82
+
83
+ info "Creating ip netns to pid mappings"
84
+ mkdir -p /var/run/netns
85
+ ln -sf /proc/${PID0}/ns/net /var/run/netns/ns${PID0}
86
+ ln -sf /proc/${PID1}/ns/net /var/run/netns/ns${PID1}
87
+
88
+ info "Creating veth pair with ends in each namespace"
89
+ SIF0=if0-${RANDOM} SIF1=if1-${RANDOM}
90
+ ip link add ${SIF0} netns ns${PID0} type veth peer ${SIF1} netns ns${PID1}
91
+
92
+ info "Setting netns, name, MAC, IP, ROUTE, MTU, and up state"
93
+ setup_if ${SIF0} ${IF0} ns${PID0} "${MAC0}" "${IP0}" "${ROUTE0}" "${MTU}"
94
+ setup_if ${SIF1} ${IF1} ns${PID1} "${MAC1}" "${IP1}" "${ROUTE1}" "${MTU}"
95
+
96
+ info "Created veth pair link (${IP0}|${MAC0} <-> ${IP1}|${MAC1})"
package/package.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "conlink",
3
+ "version": "2.0.0",
4
+ "dependencies": {
5
+ "ajv": "^8.12.0",
6
+ "dockerode": "^3.3.4",
7
+ "nbb": "^1.2.179",
8
+ "neodoc": "^2.0.2",
9
+ "yaml": "^2.2.1"
10
+ },
11
+ "devDependencies": {
12
+ "shadow-cljs": "^2.25.7",
13
+ "source-map-support": "^0.5.21"
14
+ }
15
+ }
@@ -0,0 +1,29 @@
1
+ links:
2
+ - mode: local (default), host, tunnel
3
+ service: node1,
4
+ # OR
5
+ container: examples_node1_1
6
+ remote: SWITCH (local), HOST_INTF (host), REMOTE_IP (tunnel)
7
+ intf: eth0
8
+ ip: INTF_IP
9
+ mac: INTF_MAC
10
+ nat: ...
11
+ tc: ...
12
+
13
+ # mode local
14
+ ovs: ...
15
+
16
+ # mode host
17
+ type: vlan, macvlan, macvtap, ipvlan, ipvtap
18
+ host-mode: private, vepa, bridge, etc
19
+ vlanid: 5
20
+
21
+ # mode tunnel
22
+ type: geneve
23
+ vni: 1001
24
+
25
+ switches:
26
+ - name: s1
27
+ opts: ...
28
+
29
+ containers:
package/schema.yaml ADDED
@@ -0,0 +1,71 @@
1
+ $defs:
2
+ cidr: {type: string, pattern: "^([0-9]{1,3}[.]){3}[0-9]+/[0-9]*$"}
3
+ ip: {type: string, pattern: "^([0-9]{1,3}[.]){3}[0-9]+$"}
4
+ mac: {type: string, pattern: "^([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}$"}
5
+ intf: {type: string, pattern: "^.{1,15}$"}
6
+
7
+ type: object
8
+ additionalProperties: false
9
+ required: ["links"]
10
+ properties:
11
+ links:
12
+ type: array
13
+ items:
14
+ type: object
15
+ oneOf: [{required: ["container"]},
16
+ {required: ["service"]}]
17
+
18
+ if: {properties: {type: {const: "veth"}}}
19
+ then:
20
+ allOf:
21
+ - {required: ["bridge"]}
22
+ - {not: {required: ["mode", "vlanid"]}}
23
+ else:
24
+ not: {required: ["bridge"]}
25
+ if: {properties: {type: {const: "vlan"}}}
26
+ then: {required: ["vlanid"]}
27
+ else: {not: {required: ["mode", "vlanid"]}}
28
+
29
+ additionalProperties: false
30
+ properties:
31
+ type:
32
+ type: string
33
+ default: "veth"
34
+ enum: [veth, dummy, vlan, ipvlan, ipvtap, macvlan, macvtap]
35
+ service: {type: string}
36
+ container: {type: string}
37
+ bridge: {type: string}
38
+ outer-dev: {type: string, pattern: "^.{1,15}$"}
39
+ dev: {type: string, pattern: "^.{1,15}$", default: "eth0"}
40
+ ip: { "$ref": "#/$defs/cidr" }
41
+ mac: { "$ref": "#/$defs/mac" }
42
+ mtu: {type: number}
43
+ route: {type: string}
44
+ nat: { "$ref": "#/$defs/ip" }
45
+ netem: {type: string}
46
+ mode: {type: string}
47
+ vlanid: {type: number}
48
+
49
+ tunnels:
50
+ type: array
51
+ items:
52
+ type: object
53
+ required: ["type", "bridge", "remote", "vni"]
54
+ properties:
55
+ type: {type: string, enum: [geneve, vxlan]}
56
+ bridge: {type: string}
57
+ remote: { "$ref": "#/$defs/ip" }
58
+ vni: {type: number}
59
+ netem: {type: string}
60
+
61
+ commands:
62
+ type: array
63
+ items:
64
+ type: object
65
+ required: ["command"]
66
+ oneOf: [{required: ["container"]},
67
+ {required: ["service"]}]
68
+ properties:
69
+ service: {type: string}
70
+ container: {type: string}
71
+ command: {type: string}
@@ -0,0 +1,33 @@
1
+ ;; shadow-cljs configuration
2
+ {:source-paths
3
+ ["src/"]
4
+
5
+ :dependencies
6
+ [[cljs-bean "1.9.0"]
7
+ [funcool/promesa "11.0.678"]]
8
+
9
+ :builds
10
+ {:conlink
11
+ {:target :node-script
12
+ :main conlink.core/main
13
+ :output-dir "build/"
14
+ :output-to "build/conlink.js"
15
+ ;; Don't try and connect back to shadow-cljs process
16
+ :devtools {:enabled false :console-support false}
17
+ :compiler-options
18
+ {:optimizations :simple
19
+ :source-map-use-fs-paths true}}
20
+
21
+ :net2dot
22
+ {:target :node-script
23
+ :main conlink.net2dot/main
24
+ :output-dir "build/"
25
+ :output-to "build/net2dot.js"
26
+ ;; Don't try and connect back to shadow-cljs process
27
+ :devtools {:enabled false :console-support false}
28
+ :compiler-options
29
+ {:optimizations :simple
30
+ :source-map-use-fs-paths true}}
31
+
32
+ }}
33
+
@@ -0,0 +1,63 @@
1
+ (ns conlink.addrs
2
+ (:require [clojure.string :as string]))
3
+
4
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
5
+ ;; Network Address functions
6
+ ;; - based on github.com/LonoCloud/clj-protocol
7
+
8
+ (defn num->string [n base]
9
+ #?(:cljs (.toString n base)
10
+ :clj (Long/toString n base)))
11
+
12
+ (defn string->num
13
+ ([s] (string->num s 10))
14
+ ([s base] #?(:cljs (js/parseInt s base)
15
+ :clj (Long/parseLong s base))))
16
+
17
+ (defn octet->int
18
+ "Convert sequence of octets/bytes `octets` (in MSB first order) into
19
+ an integer"
20
+ [octets]
21
+ (reduce (fn [a o] (+ o (* a 256))) octets))
22
+
23
+ (defn int->octet
24
+ "Convert integer `n` into `cnt` octets/bytes (in MSB first order)"
25
+ [n cnt]
26
+ (vec (first
27
+ (reduce (fn [[res x] _] [(conj res (bit-and x 255)) (quot x 256)])
28
+ [(list) n]
29
+ (range cnt)))))
30
+
31
+ (defn int->hex
32
+ "Convert integer `i` into hex string representation"
33
+ [i]
34
+ (let [h (num->string i 16)]
35
+ (if (= 1 (count h)) (str "0" h) h)))
36
+
37
+ (defn ip->octet "Convert IPv4 string to bytes/octets" [ip]
38
+ (map string->num (string/split ip #"[.]")))
39
+
40
+ (defn octet->ip "convert bytes/octets to IPv4 string" [os]
41
+ (string/join "." os))
42
+
43
+ (defn mac->octet "Convert MAC addr string to bytes/octets" [mac]
44
+ (map #(string->num %1 16) (string/split mac #":")))
45
+
46
+ (defn octet->mac "Convert bytes/octets to MAC addr string" [os]
47
+ (string/join ":" (map int->hex os)))
48
+
49
+
50
+ (defn ip->int "Convert IPv4 string to uint32 value" [ip]
51
+ (octet->int (ip->octet ip)))
52
+
53
+ (defn int->ip "Convert IPv4 uint32 value to IPv4 string" [num]
54
+ (octet->ip (int->octet num 4)))
55
+
56
+ (defn mac->int "Convert MAC string to int value" [ip]
57
+ (octet->int (mac->octet ip)))
58
+
59
+ (defn int->mac "Convert MAC int value to IPv4 string" [num]
60
+ (octet->mac (int->octet num 6)))
61
+
62
+
63
+