-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add parallel processing mac2ip DHCP server.
Uses the node:cluster module to launch parallel workers processes (based on `--processes CNT` parameter). The primary process does the arg parsing, spawns the workers, waits for them to start, and then sends them to full user configuration (file and CLI parameters). Also add the capability (in dhcp.node-server/create-server) to listen on all interfaces. This precludes answering with a broadcast message so this is more useful for unicast DHCP server mode (e.g. such as a DHCP relay situation). Add a separate `test/docker-compose-mac3ip.yaml` compose test to specifically test the mac2ip server.
- Loading branch information
Showing
10 changed files
with
274 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
;; Copyright (c) 2023, Viasat, Inc | ||
;; Licensed under EPL 2.0 | ||
|
||
(ns dhcp.mac2ip-server | ||
"Multiprocess DHCP server that does direct mapping of MAC to IP addresses." | ||
(:require [cljs-bean.core :refer [->clj ->js]] | ||
[protocol.fields :as fields] | ||
[protocol.addrs :as addrs] | ||
[dhcp.core :as dhcp] | ||
[dhcp.util :as util] | ||
[dhcp.logging :as logging] | ||
[dhcp.node-server :as server] | ||
[clojure.walk :refer [postwalk]])) | ||
|
||
(def cluster (js/require "node:cluster")) | ||
(def minimist (js/require "minimist")) | ||
|
||
(def USAGE "Usage: mac2ip-server [options] | ||
Options: | ||
--processes CNT - Number of worker processes | ||
[default: 1] | ||
--config-file CFG - Load json/edn config from CFG | ||
[default: mac2ip.json] | ||
--if-name IF-NAME - Bind to interface IF-NAME | ||
[default: all] | ||
--log-level LEVEL - Set logging level to LEVEL: | ||
[default: 1] | ||
0 - none | ||
1 - one character per event | ||
2 - full log line for each event | ||
") | ||
|
||
(def arg-defaults {:processes 1 | ||
:if-name "all" | ||
:config-file "mac2ip.json" | ||
:log-level 1}) | ||
|
||
(defn load-config | ||
[config-file] | ||
(let [cfg (util/load-config config-file) | ||
ranges (for [r (:ranges cfg)] | ||
{:ip-start (-> r :ip-start addrs/ip->int) | ||
:ip-end (-> r :ip-end addrs/ip->int) | ||
:mac-start (-> r :mac-start addrs/mac->int) | ||
:mac-end (-> r :mac-end addrs/mac->int)})] | ||
(assoc cfg :ranges ranges))) | ||
|
||
(defn mac->ip [msg-map ranges] | ||
(let [mac-int (addrs/mac->int (:chaddr msg-map)) | ||
r (first (filter #(and (>= mac-int (:mac-start %)) | ||
(<= mac-int (:mac-end %))) | ||
ranges))] | ||
(when r | ||
(addrs/int->ip (+ (:ip-start r) (- mac-int (:mac-start r))))))) | ||
|
||
(defn mac2ip-handler [{:keys [ranges server-info log-msg | ||
log-level] :as cfg} msg-map] | ||
(let [ip (mac->ip msg-map ranges)] | ||
(if (not ip) | ||
(do | ||
(log-msg :error (str "MAC " (:chaddr msg-map) "is out of range")) | ||
nil) | ||
(do | ||
(condp = log-level | ||
2 (log-msg :info (str "Assigning " ip " to " (:chaddr msg-map))) | ||
nil) | ||
(merge | ||
(dhcp/default-response msg-map server-info) | ||
(select-keys msg-map [:giaddr :opt/relay-agent-info]) | ||
(:fields cfg) ;; config file field/option overrides | ||
{:yiaddr ip}))))) | ||
|
||
(defn worker [user-cfg] | ||
(let [log-msg logging/log-message | ||
cfg (merge | ||
user-cfg | ||
{:message-handler mac2ip-handler | ||
:error-handler #(util/fatal 1 "Could not create server:" %) | ||
:log-msg log-msg})] | ||
|
||
(logging/start-logging cfg) | ||
(log-msg :info "Starting DHCP Server...") | ||
(server/create-server cfg))) | ||
|
||
(defn parse-args | ||
[& args] | ||
(let [minimist-opts {:default arg-defaults } | ||
opts (->clj (minimist (apply array args) (->js minimist-opts))) | ||
{:keys [h help if-name config-file log-level]} opts | ||
_ (when (or h help) (util/fatal 2 USAGE)) | ||
_ (when-not (util/existsSync config-file) | ||
(util/fatal 2 "Config file" config-file "does not exist")) | ||
file-cfg (load-config config-file) | ||
_ (when (and (= "all" if-name) | ||
(not (:server-info file-cfg))) | ||
(util/fatal 2 "--if-name or config server-info required")) | ||
if-info (util/get-if-ipv4 if-name) | ||
_ (when (and (not= "all" if-name) | ||
(not if-info)) | ||
(util/fatal 2 "Interface" if-name "not found")) | ||
;; precedence: CLI opts, file config, discovered interface info | ||
user-cfg (util/deep-merge {:server-info if-info | ||
:buffsz (* 16 1024 1024)} | ||
file-cfg | ||
(dissoc opts :_))] | ||
|
||
(when (and (= "all" if-name) | ||
(not (:disable-broadcast user-cfg))) | ||
(util/fatal 2 "--if-name or --disable-broadcast must be specified")) | ||
|
||
user-cfg)) | ||
|
||
(defn main | ||
"Start mac2ip DHCP server worker processes listening on `if-name` | ||
using `config-file`" | ||
[& args] | ||
(if cluster.isPrimary | ||
(let [{:keys [processes log-level] :as cfg} (apply parse-args args)] | ||
(when (= 2 log-level) (println "User config:" cfg)) | ||
(println "Forking" processes "workers") | ||
(doseq [i (range processes) | ||
:let [cfg (assoc cfg :log-prefix (str "worker-" i)) | ||
worker (cluster.fork)]] | ||
^object | ||
(.on worker "message" | ||
#(let [msg (->clj %)] | ||
(condp = (:type msg) | ||
"ready" (.send worker (->js {:type "start" | ||
:cfg cfg}))))))) | ||
(do | ||
(.on js/process "message" | ||
#(let [msg (->clj %)] | ||
(if (= "start" (:type msg)) | ||
(worker (:cfg msg))))) | ||
(.send js/process (->js {:type "ready" | ||
:pid js/process.pid}))))) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
version: "2.4" | ||
|
||
x-service-base: &service-base | ||
cap_add: [NET_ADMIN, NET_RAW, NET_BROADCAST, NET_BIND_SERVICE, SYS_ADMIN] | ||
security_opt: [ 'apparmor:unconfined' ] # needed on Ubuntu 18.04 | ||
network_mode: none | ||
image: clj-protocol-test | ||
build: | ||
dockerfile: test/Dockerfile.node | ||
context: ../ | ||
volumes: | ||
- ./:/test:ro | ||
- ../build:/root/app/build:ro | ||
- ../.shadow-cljs:/root/app/.shadow-cljs:ro | ||
|
||
services: | ||
net: | ||
image: lonocloud/conlink:20210825_165410-g9c367bc6fc24 | ||
pid: host | ||
network_mode: none | ||
cap_add: [SYS_ADMIN, SYS_NICE, NET_ADMIN, NET_BROADCAST] | ||
security_opt: [ 'apparmor:unconfined' ] # needed on Ubuntu 18.04 | ||
volumes: | ||
- /sys/fs/cgroup:/sys/fs/cgroup:ro | ||
- /var/run/docker.sock:/var/run/docker.sock | ||
- /var/lib/docker:/var/lib/docker | ||
- ./:/test | ||
command: /sbin/conlink.py --compose-file /test/docker-compose-mac2ip.yaml | ||
x-network: | ||
links: | ||
- left: {container: svr_1, intf: eth0, ip: 10.1.0.1/16} | ||
right: {container: net_1, intf: svr} | ||
|
||
- {left: {container: ucli_1, intf: eth0, ip: 10.1.1.1/16, mac: "00:00:10:01:07:01"}, right: {container: net_1, intf: uc1}} | ||
- {left: {container: ucli_2, intf: eth0, ip: 10.1.1.2/16, mac: "00:00:10:01:07:02"}, right: {container: net_1, intf: uc2}} | ||
- {left: {container: ucli_3, intf: eth0, ip: 10.1.1.3/16, mac: "00:00:10:01:07:03"}, right: {container: net_1, intf: uc3}} | ||
- {left: {container: ucli_4, intf: eth0, ip: 10.1.1.4/16, mac: "00:00:10:01:07:04"}, right: {container: net_1, intf: uc4}} | ||
- {left: {container: ucli_5, intf: eth0, ip: 10.1.1.5/16, mac: "00:00:10:01:07:05"}, right: {container: net_1, intf: uc5}} | ||
- {left: {container: ucli_6, intf: eth0, ip: 10.1.1.6/16, mac: "00:00:10:01:07:06"}, right: {container: net_1, intf: uc6}} | ||
- {left: {container: ucli_7, intf: eth0, ip: 10.1.1.7/16, mac: "00:00:10:01:07:07"}, right: {container: net_1, intf: uc7}} | ||
- {left: {container: ucli_8, intf: eth0, ip: 10.1.1.8/16, mac: "00:00:10:01:07:08"}, right: {container: net_1, intf: uc8}} | ||
- {left: {container: ucli_9, intf: eth0, ip: 10.1.1.9/16, mac: "00:00:10:01:07:09"}, right: {container: net_1, intf: uc9}} | ||
- {left: {container: ucli_10, intf: eth0, ip: 10.1.1.10/16, mac: "00:00:10:01:07:0a"}, right: {container: net_1, intf: uc10}} | ||
|
||
mininet-cfg: | ||
switches: | ||
- name: s1 | ||
interfaces: | ||
- {name: svr, node: s1} | ||
|
||
- {name: uc1, node: s1} | ||
- {name: uc2, node: s1} | ||
- {name: uc3, node: s1} | ||
- {name: uc4, node: s1} | ||
- {name: uc5, node: s1} | ||
- {name: uc6, node: s1} | ||
- {name: uc7, node: s1} | ||
- {name: uc8, node: s1} | ||
- {name: uc9, node: s1} | ||
- {name: uc10, node: s1} | ||
|
||
svr: | ||
<<: *service-base | ||
command: /root/app/init.sh /root/app/build/mac2ip-server.js --processes 2 --config-file /test/mac2ip.json --if-name eth0 --log-level 2 | ||
|
||
ucli: | ||
<<: *service-base | ||
scale: 10 | ||
#scale: 1 | ||
command: /root/app/init.sh /root/app/build/dhcp-client.js --unicast --server-ip 10.1.0.1 --if-name eth0 --log-level 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"disable-broadcast": true, | ||
"ranges": [ | ||
{ | ||
"ip-start": "10.0.0.0", | ||
"ip-end": "11.0.0.0", | ||
"mac-start": "00:00:10:00:00:00", | ||
"mac-end": "00:00:11:00:00:00" | ||
} | ||
] | ||
} |