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.
- package/.dockerignore +5 -0
- package/Dockerfile +34 -0
- package/LICENSE +373 -0
- package/README.md +485 -0
- package/TODO +34 -0
- package/conlink +11 -0
- package/conlink-start.sh +172 -0
- package/examples/dot.js +36 -0
- package/examples/index.html +11 -0
- package/examples/net2dot.yaml +21 -0
- package/examples/test1-compose.yaml +60 -0
- package/examples/test2-compose.yaml +31 -0
- package/examples/test2-network.yaml +5 -0
- package/examples/test3-network.yaml +5 -0
- package/examples/test4-multiple/all-compose.yaml +5 -0
- package/examples/test4-multiple/base-compose.yaml +25 -0
- package/examples/test4-multiple/node1-compose.yaml +17 -0
- package/examples/test4-multiple/nodes2-compose.yaml +20 -0
- package/examples/test4-multiple/web-network.yaml +2 -0
- package/examples/test5-geneve-compose.yaml +31 -0
- package/examples/test6-cfn.yaml +184 -0
- package/examples/test7-compose.yaml +31 -0
- package/examples/test8-compose.yaml +35 -0
- package/host-build.yaml +1 -0
- package/inspect.json +210 -0
- package/link-add.sh +197 -0
- package/link-del.sh +60 -0
- package/net2dot +11 -0
- package/notes.txt +82 -0
- package/old/Dockerfile.bak +26 -0
- package/old/add-link.sh +82 -0
- package/old/conlink +12 -0
- package/old/conlink.cljs +131 -0
- package/old/dot_gitignore +1 -0
- package/old/examples/test2-compose.yaml +32 -0
- package/old/examples/test2-network.yaml +42 -0
- package/old/move-link.sh +108 -0
- package/old/net2dot.py +122 -0
- package/old/notes-old.txt +97 -0
- package/old/package.json +16 -0
- package/old/schema.yaml +138 -0
- package/old/schema.yaml.bak +76 -0
- package/old/test2b-compose.yaml +18 -0
- package/old/veth-link.sh +96 -0
- package/package.json +15 -0
- package/schema-ish.yaml +29 -0
- package/schema.yaml +71 -0
- package/shadow-cljs.edn +33 -0
- package/src/conlink/addrs.cljc +63 -0
- package/src/conlink/core.cljs +772 -0
- package/src/conlink/net2dot.cljs +158 -0
- package/src/conlink/util.cljs +140 -0
- package/tests/invalid-schema-1.yaml +6 -0
- package/tests/invalid-schema-2.yaml +6 -0
- package/tests/invalid-schema-3.yaml +17 -0
- package/tests/invalid-schema-4.yaml +14 -0
- package/tests/invalid-schema-5.yaml +12 -0
- package/tests/invalid-schema-6.yaml +12 -0
- 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
|
package/old/veth-link.sh
ADDED
|
@@ -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
|
+
}
|
package/schema-ish.yaml
ADDED
|
@@ -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}
|
package/shadow-cljs.edn
ADDED
|
@@ -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
|
+
|