diff --git a/back/requirements.txt b/back/requirements.txt
index 0c083992..1cb334da 100644
--- a/back/requirements.txt
+++ b/back/requirements.txt
@@ -4,4 +4,4 @@ python-dotenv==1.1.1
marshmallow_dataclass==8.7.1
psutil==7.0.0
netaddr==1.3.0
-ipmininet @ git+https://github.com/mimi-net/ipmininet.git@1.2.4
\ No newline at end of file
+ipmininet @ git+https://github.com/mimi-net/ipmininet.git@1.2.5
\ No newline at end of file
diff --git a/back/src/jobs.py b/back/src/jobs.py
index 7046a302..60455acc 100755
--- a/back/src/jobs.py
+++ b/back/src/jobs.py
@@ -3,8 +3,8 @@
import shlex
import time
from typing import Any, Callable, Dict, List
-
from ipmininet.host.config.dnsmasq import Dnsmasq
+from ipmininet.router.config.dhcprelay import DHCPRelay
from mininet.log import info
from netaddr import EUI, AddrFormatError
from network_schema import Job
@@ -535,6 +535,19 @@ def dhcp_server(job: Job, job_host):
info(f"[dhcp_server] dnsmasq started on host={job_host.name}")
+def dhcp_relay(job: Job, job_host):
+ dhcp_server_ip = job.arg_1
+ listening_ip = job.arg_2
+ daemon = DHCPRelay(
+ node=job_host,
+ dhcp_server_ip=dhcp_server_ip,
+ listening_ip=listening_ip,
+ )
+ job_host.build_daemon(daemon)
+ job_host.start_daemon(daemon)
+ info(f"dnsmasq --dhcp-relay={listening_ip},{dhcp_server_ip}")
+
+
class Jobs:
"""Class for representing various commands for working with miminet network"""
@@ -569,6 +582,7 @@ def __init__(self, job: Job, job_host: Any, **kwargs) -> None:
201: open_tcp_server_handler,
202: block_tcp_udp_port,
203: dhcp_server,
+ 204: dhcp_relay,
}
self._job: Job = job
self._job_host = job_host
diff --git a/back/tests/test_json/dhcp_relay_no_client_answer.json b/back/tests/test_json/dhcp_relay_no_client_answer.json
new file mode 100644
index 00000000..54811d22
--- /dev/null
+++ b/back/tests/test_json/dhcp_relay_no_client_answer.json
@@ -0,0 +1 @@
+[[{"data": {"id": "pkt_UI1QGK53", "label": "ARP-request\nWho has 192.168.10.2? Tell 192.168.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 192.168.10.3", "path": "edge_mnwcvu628bbw4u6v8di", "source": "router_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274672995385"}], [{"data": {"id": "pkt_048BOD85", "label": "ARP-request\nWho has 192.168.10.2? Tell 192.168.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 192.168.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274673012501"}], [{"data": {"id": "pkt_FIPV4QO4", "label": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "type": "packet"}, "config": {"type": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274673027832"}], [{"data": {"id": "pkt_ZY0N0WW7", "label": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "type": "packet"}, "config": {"type": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "path": "edge_mnwcvu628bbw4u6v8di", "source": "l2sw2", "target": "router_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274673043742"}], [{"data": {"id": "pkt_JW244TDV", "label": "ICMP echo-request\n192.168.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "ICMP echo-request\n192.168.10.3 > 192.168.10.2", "path": "edge_mnwcvu628bbw4u6v8di", "source": "router_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274673060126"}], [{"data": {"id": "pkt_WP9RYEH0", "label": "ICMP echo-request\n192.168.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "ICMP echo-request\n192.168.10.3 > 192.168.10.2", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274673076671"}], [{"data": {"id": "pkt_ISD5OLD5", "label": "ICMP echo-reply\n192.168.10.2 > 192.168.10.3", "type": "packet"}, "config": {"type": "ICMP echo-reply\n192.168.10.2 > 192.168.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274673091814"}], [{"data": {"id": "pkt_BCYWGSZS", "label": "ICMP echo-reply\n192.168.10.2 > 192.168.10.3", "type": "packet"}, "config": {"type": "ICMP echo-reply\n192.168.10.2 > 192.168.10.3", "path": "edge_mnwcvu628bbw4u6v8di", "source": "l2sw2", "target": "router_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274673107063"}]]
\ No newline at end of file
diff --git a/back/tests/test_json/dhcp_relay_no_client_network.json b/back/tests/test_json/dhcp_relay_no_client_network.json
new file mode 100644
index 00000000..3321e6dc
--- /dev/null
+++ b/back/tests/test_json/dhcp_relay_no_client_network.json
@@ -0,0 +1,233 @@
+{
+ "nodes": [
+ {
+ "classes":[
+ "l2_switch"
+ ],
+ "config":{
+ "label":"l2sw1",
+ "stp":0,
+ "type":"l2_switch"
+ },
+ "data":{
+ "id":"l2sw1",
+ "label":"l2sw1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvh8lkuozj9b2hw",
+ "id":"l2sw1_3",
+ "name":"l2sw1_3",
+ "type_connection":null,
+ "vlan":null
+ },
+ {
+ "id":"l2sw1_1",
+ "name":"l2sw1_1",
+ "connect":"edge_mnxf20jpi9lk28vbr2",
+ "vlan":null,
+ "type_connection":null
+ }
+ ],
+ "position":{
+ "x":250,
+ "y":200
+ }
+ },
+ {
+ "classes":[
+ "l3_router"
+ ],
+ "config":{
+ "default_gw":"",
+ "label":"router_1",
+ "type":"router"
+ },
+ "data":{
+ "id":"router_1",
+ "label":"router_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvh8lkuozj9b2hw",
+ "id":"iface_20417632",
+ "ip":"172.16.10.3",
+ "name":"iface_20417632",
+ "netmask":24
+ },
+ {
+ "connect":"edge_mnwcvu628bbw4u6v8di",
+ "id":"iface_13153444",
+ "ip":"192.168.10.3",
+ "name":"iface_13153444",
+ "netmask":24
+ }
+ ],
+ "position":{
+ "x":350,
+ "y":100
+ }
+ },
+ {
+ "classes":[
+ "l2_switch"
+ ],
+ "config":{
+ "label":"l2sw2",
+ "stp":0,
+ "type":"l2_switch"
+ },
+ "data":{
+ "id":"l2sw2",
+ "label":"l2sw2"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvu628bbw4u6v8di",
+ "id":"l2sw2_1",
+ "name":"l2sw2_1",
+ "type_connection":null,
+ "vlan":null
+ },
+ {
+ "connect":"edge_mnwcvw5j8b38v1xr5sj",
+ "id":"l2sw2_2",
+ "name":"l2sw2_2",
+ "type_connection":null,
+ "vlan":null
+ }
+ ],
+ "position":{
+ "x":450,
+ "y":200
+ }
+ },
+ {
+ "classes":[
+ "server"
+ ],
+ "config":{
+ "default_gw":"192.168.10.3",
+ "label":"server_1",
+ "type":"server"
+ },
+ "data":{
+ "id":"server_1",
+ "label":"server_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvw5j8b38v1xr5sj",
+ "id":"iface_16417632",
+ "ip":"192.168.10.2",
+ "name":"iface_16417632",
+ "netmask":24
+ }
+ ],
+ "position":{
+ "x":450,
+ "y":300
+ }
+ },
+ {
+ "data":{
+ "id":"host_1",
+ "label":"host_1"
+ },
+ "position":{
+ "x":250,
+ "y":300
+ },
+ "classes":[
+ "host"
+ ],
+ "config":{
+ "type":"host",
+ "label":"host_1",
+ "default_gw":""
+ },
+ "interface":[
+ {
+ "id":"iface_46653148",
+ "name":"iface_46653148",
+ "connect":"edge_mnxf20jpi9lk28vbr2"
+ }
+ ]
+ }
+ ],
+ "edges": [
+ {
+ "data":{
+ "id":"edge_mnwcvh8lkuozj9b2hw",
+ "source":"l2sw1",
+ "target":"router_1",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnwcvu628bbw4u6v8di",
+ "source":"router_1",
+ "target":"l2sw2",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnwcvw5j8b38v1xr5sj",
+ "source":"l2sw2",
+ "target":"server_1",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnxf20jpi9lk28vbr2",
+ "source":"host_1",
+ "target":"l2sw1",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ }
+ ],
+ "jobs": [
+ {
+ "arg_1":"172.16.10.10",
+ "arg_2":"172.16.10.100",
+ "arg_3":"24",
+ "arg_4":"172.16.10.3",
+ "arg_5":"iface_16417632",
+ "host_id":"server_1",
+ "id":"c124a6208e0441478f7548a14003b8b5",
+ "job_id":203,
+ "level":3,
+ "print_cmd":"dhcp ip range: 172.16.10.10,172.16.10.100/24 gw:172.16.10.3"
+ },
+ {
+ "id":"406fa1c5e3794d36b40c920dc80f129e",
+ "job_id":1,
+ "print_cmd":"ping -c 1 192.168.10.2",
+ "arg_1":"192.168.10.2",
+ "level":2,
+ "host_id":"router_1"
+ },
+ {
+ "id":"2e320d269cf841aa977817f5f21fb14e",
+ "job_id":204,
+ "print_cmd":"dnsmasq --dhcp-relay=172.16.10.3,192.168.10.2", "arg_1":"192.168.10.2",
+ "arg_2":"172.16.10.3",
+ "level":2,
+ "host_id":"router_1"
+ }
+ ],
+ "config": {
+ "zoom": 2,
+ "pan_x": 193,
+ "pan_y": 8
+ },
+ "pcap": [],
+ "packets": "[]"
+}
\ No newline at end of file
diff --git a/back/tests/test_json/dhcp_relay_no_gateway_answer.json b/back/tests/test_json/dhcp_relay_no_gateway_answer.json
new file mode 100644
index 00000000..fbc4b478
--- /dev/null
+++ b/back/tests/test_json/dhcp_relay_no_gateway_answer.json
@@ -0,0 +1 @@
+[[{"data": {"id": "pkt_57448WIK", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnxf20jpi9lk28vbr2", "source": "host_1", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275940616600"}], [{"data": {"id": "pkt_HANS8A4M", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnym51eah6wx0v6s9h", "source": "l2sw1", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275940632726"}], [{"data": {"id": "pkt_JIATC2IM", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275940648266"}], [{"data": {"id": "pkt_VK4RTY1D", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275940672473"}], [{"data": {"id": "pkt_R9BDLMKS", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275941678060"}], [{"data": {"id": "pkt_AUHPWQY3", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275941693430"}], [{"data": {"id": "pkt_EQY7NYFC", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275942702422"}], [{"data": {"id": "pkt_TJ0JAYUO", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275942718230"}], [{"data": {"id": "pkt_WJ7SM6D5", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnxf20jpi9lk28vbr2", "source": "host_1", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275943572633"}], [{"data": {"id": "pkt_E8Z0HLZ3", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnym51eah6wx0v6s9h", "source": "l2sw1", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776275943597792"}]]
\ No newline at end of file
diff --git a/back/tests/test_json/dhcp_relay_no_gateway_network.json b/back/tests/test_json/dhcp_relay_no_gateway_network.json
new file mode 100644
index 00000000..674e493c
--- /dev/null
+++ b/back/tests/test_json/dhcp_relay_no_gateway_network.json
@@ -0,0 +1,233 @@
+{
+ "nodes": [
+ {
+ "classes":[
+ "l2_switch"
+ ],
+ "config":{
+ "label":"l2sw1",
+ "stp":0,
+ "type":"l2_switch"
+ },
+ "data":{
+ "id":"l2sw1",
+ "label":"l2sw1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnxf20jpi9lk28vbr2",
+ "id":"l2sw1_1",
+ "name":"l2sw1_1",
+ "type_connection":null,
+ "vlan":null
+ },
+ {
+ "connect":"edge_mnym51eah6wx0v6s9h",
+ "id":"l2sw1_2",
+ "name":"l2sw1_2",
+ "type_connection":null,
+ "vlan":null
+ }
+ ],
+ "position":{
+ "x":250,
+ "y":200
+ }
+ },
+ {
+ "classes":[
+ "l2_switch"
+ ],
+ "config":{
+ "label":"l2sw2",
+ "stp":0,
+ "type":"l2_switch"
+ },
+ "data":{
+ "id":"l2sw2",
+ "label":"l2sw2"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvw5j8b38v1xr5sj",
+ "id":"l2sw2_2",
+ "name":"l2sw2_2",
+ "type_connection":null,
+ "vlan":null
+ },
+ {
+ "connect":"edge_mnym5331cjpgqzraqy4",
+ "id":"l2sw2_3",
+ "name":"l2sw2_3",
+ "type_connection":null,
+ "vlan":null
+ }
+ ],
+ "position":{
+ "x":450,
+ "y":200
+ }
+ },
+ {
+ "classes":[
+ "server"
+ ],
+ "config":{
+ "default_gw":"",
+ "label":"server_1",
+ "type":"server"
+ },
+ "data":{
+ "id":"server_1",
+ "label":"server_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvw5j8b38v1xr5sj",
+ "id":"iface_16417632",
+ "ip":"192.168.10.2",
+ "name":"iface_16417632",
+ "netmask":24
+ }
+ ],
+ "position":{
+ "x":450,
+ "y":300
+ }
+ },
+ {
+ "classes":[
+ "host"
+ ],
+ "config":{
+ "default_gw":"",
+ "label":"host_1",
+ "type":"host"
+ },
+ "data":{
+ "id":"host_1",
+ "label":"host_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnxf20jpi9lk28vbr2",
+ "id":"iface_46653148",
+ "name":"iface_46653148"
+ }
+ ],
+ "position":{
+ "x":250,
+ "y":300
+ }
+ },
+ {
+ "classes":[
+ "l3_router"
+ ],
+ "config":{
+ "default_gw":"",
+ "label":"router_1",
+ "type":"router"
+ },
+ "data":{
+ "id":"router_2",
+ "label":"router_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnym51eah6wx0v6s9h",
+ "id":"iface_02514152",
+ "ip":"172.16.10.3",
+ "name":"iface_02514152",
+ "netmask":24
+ },
+ {
+ "connect":"edge_mnym5331cjpgqzraqy4",
+ "id":"iface_64411470",
+ "ip":"192.168.10.3",
+ "name":"iface_64411470",
+ "netmask":24
+ }
+ ],
+ "position":{
+ "x":350,
+ "y":100
+ }
+ }
+ ],
+ "edges": [
+ {
+ "data":{
+ "id":"edge_mnwcvw5j8b38v1xr5sj",
+ "source":"l2sw2",
+ "target":"server_1",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnxf20jpi9lk28vbr2",
+ "source":"host_1",
+ "target":"l2sw1",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnym51eah6wx0v6s9h",
+ "source":"l2sw1",
+ "target":"router_2",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnym5331cjpgqzraqy4",
+ "source":"l2sw2",
+ "target":"router_2",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ }
+ ],
+ "jobs": [
+ {
+ "id":"2fb202bfe7bd48d390734b68f0e2a61d",
+ "job_id":203,
+ "print_cmd":"dhcp ip range: 172.16.10.10,172.16.10.100/24 gw:172.16.10.3",
+ "arg_1":"172.16.10.10",
+ "arg_2":"172.16.10.100",
+ "arg_3":"24",
+ "arg_4":"172.16.10.3",
+ "arg_5":"iface_16417632",
+ "level":1,
+ "host_id":"server_1"
+ },
+ {
+ "id":"773ccad1dc9244ca84603adfc4996b13",
+ "job_id":108,
+ "print_cmd":"dhcp client",
+ "arg_1":"iface_46653148",
+ "level":2,
+ "host_id":"host_1"
+ },
+ {
+ "id":"b9a5d85ff6824c77ac6b79916868dde9",
+ "job_id":204,
+ "print_cmd":"dnsmasq --dhcp-relay=172.16.10.3,192.168.10.2", "arg_1":"192.168.10.2",
+ "arg_2":"172.16.10.3",
+ "level":2,
+ "host_id":"router_2"
+ }
+ ],
+ "config": {
+ "zoom": 2,
+ "pan_x": 226,
+ "pan_y": 32
+ },
+ "pcap": [],
+ "packets": "[]"
+}
\ No newline at end of file
diff --git a/back/tests/test_json/dhcp_relay_one_host_answer.json b/back/tests/test_json/dhcp_relay_one_host_answer.json
new file mode 100644
index 00000000..5baffaa1
--- /dev/null
+++ b/back/tests/test_json/dhcp_relay_one_host_answer.json
@@ -0,0 +1 @@
+[[{"data": {"id": "pkt_AXCL63GP", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnxf20jpi9lk28vbr2", "source": "host_1", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458598371"}], [{"data": {"id": "pkt_3HIXS5PZ", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnym51eah6wx0v6s9h", "source": "l2sw1", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458617785"}], [{"data": {"id": "pkt_9JD4CGL7", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458635547"}], [{"data": {"id": "pkt_RGLR8ZZU", "label": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.2? Tell 172.16.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458650834"}], [{"data": {"id": "pkt_J18K75BO", "label": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "type": "packet"}, "config": {"type": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458668841"}], [{"data": {"id": "pkt_GUW7LSIK", "label": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "type": "packet"}, "config": {"type": "ARP-response\n192.168.10.2 at 00:00:00:00:00:02", "path": "edge_mnym5331cjpgqzraqy4", "source": "l2sw2", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458684908"}], [{"data": {"id": "pkt_S4MPKFCQ", "label": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458700631"}], [{"data": {"id": "pkt_9X4HLA3K", "label": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458716180"}], [{"data": {"id": "pkt_Y97GBZD3", "label": "ARP-request\nWho has 192.168.10.3? Tell 192.168.10.2", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.3? Tell 192.168.10.2", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458731971"}], [{"data": {"id": "pkt_PS3HPWBN", "label": "ARP-request\nWho has 192.168.10.3? Tell 192.168.10.2", "type": "packet"}, "config": {"type": "ARP-request\nWho has 192.168.10.3? Tell 192.168.10.2", "path": "edge_mnym5331cjpgqzraqy4", "source": "l2sw2", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458749686"}], [{"data": {"id": "pkt_J8A9M2WL", "label": "ARP-response\n192.168.10.3 at b6:26:e1:dc:04:3d", "type": "packet"}, "config": {"type": "ARP-response\n192.168.10.3 at b6:26:e1:dc:04:3d", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458767553"}], [{"data": {"id": "pkt_0JP2K6LE", "label": "ARP-response\n192.168.10.3 at b6:26:e1:dc:04:3d", "type": "packet"}, "config": {"type": "ARP-response\n192.168.10.3 at b6:26:e1:dc:04:3d", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458784013"}], [{"data": {"id": "pkt_K4GOSPAU", "label": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "type": "packet"}, "config": {"type": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458803871"}], [{"data": {"id": "pkt_3HBQ2GCX", "label": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "type": "packet"}, "config": {"type": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "path": "edge_mnym5331cjpgqzraqy4", "source": "l2sw2", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458824362"}], [{"data": {"id": "pkt_P0ZD1ER9", "label": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "path": "edge_mnym51eah6wx0v6s9h", "source": "router_2", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458839747"}], [{"data": {"id": "pkt_ZFUP1M50", "label": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "path": "edge_mnxf20jpi9lk28vbr2", "source": "l2sw1", "target": "host_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274458855049"}], [{"data": {"id": "pkt_HXXDBDTS", "label": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "path": "edge_mnym51eah6wx0v6s9h", "source": "router_2", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274459886331"}], [{"data": {"id": "pkt_O3ZHEUY7", "label": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "path": "edge_mnxf20jpi9lk28vbr2", "source": "l2sw1", "target": "host_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274459902402"}], [{"data": {"id": "pkt_LX4LL32B", "label": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "path": "edge_mnym51eah6wx0v6s9h", "source": "router_2", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274460909267"}], [{"data": {"id": "pkt_UZD6JTH5", "label": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "type": "packet"}, "config": {"type": "ARP-request\nWho has 172.16.10.11? Tell 172.16.10.3", "path": "edge_mnxf20jpi9lk28vbr2", "source": "l2sw1", "target": "host_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274460924472"}], [{"data": {"id": "pkt_M8XYB04Z", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnxf20jpi9lk28vbr2", "source": "host_1", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461124444"}], [{"data": {"id": "pkt_JWTVLIVK", "label": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Discover\n0.0.0.0 > 255.255.255.255", "path": "edge_mnym51eah6wx0v6s9h", "source": "l2sw1", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461141033"}], [{"data": {"id": "pkt_V6PXSKCC", "label": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461157135"}], [{"data": {"id": "pkt_KZHHJSUF", "label": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "DHCP Discover\n172.16.10.3 > 192.168.10.2", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461181723"}], [{"data": {"id": "pkt_2ZOK158O", "label": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461749118"}, {"data": {"id": "pkt_1F4ID9XE", "label": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461749495"}], [{"data": {"id": "pkt_U5WUUZIP", "label": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "path": "edge_mnym5331cjpgqzraqy4", "source": "l2sw2", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461764435"}, {"data": {"id": "pkt_4Q5P76EP", "label": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n192.168.10.2 > 172.16.10.3", "path": "edge_mnym5331cjpgqzraqy4", "source": "l2sw2", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461764518"}], [{"data": {"id": "pkt_A7M6HSSN", "label": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "type": "packet"}, "config": {"type": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "path": "edge_mnym51eah6wx0v6s9h", "source": "router_2", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461780240"}, {"data": {"id": "pkt_I0DLSYW5", "label": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "path": "edge_mnym51eah6wx0v6s9h", "source": "router_2", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461780297"}, {"data": {"id": "pkt_VUXA96EB", "label": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "path": "edge_mnym51eah6wx0v6s9h", "source": "router_2", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461780327"}], [{"data": {"id": "pkt_4UQMDCHD", "label": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "type": "packet"}, "config": {"type": "ICMP echo-request\n192.168.10.2 > 172.16.10.11", "path": "edge_mnxf20jpi9lk28vbr2", "source": "l2sw1", "target": "host_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461795643"}, {"data": {"id": "pkt_ASR42W0A", "label": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "path": "edge_mnxf20jpi9lk28vbr2", "source": "l2sw1", "target": "host_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461795669"}, {"data": {"id": "pkt_94QMROKZ", "label": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "type": "packet"}, "config": {"type": "DHCP Offer 172.16.10.11/16\n172.16.10.3 > 172.16.10.11", "path": "edge_mnxf20jpi9lk28vbr2", "source": "l2sw1", "target": "host_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461795676"}], [{"data": {"id": "pkt_JWZD1IPO", "label": "DHCP Request 172.16.10.11\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Request 172.16.10.11\n0.0.0.0 > 255.255.255.255", "path": "edge_mnxf20jpi9lk28vbr2", "source": "host_1", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461811492"}], [{"data": {"id": "pkt_C7V9PV0K", "label": "DHCP Request 172.16.10.11\n0.0.0.0 > 255.255.255.255", "type": "packet"}, "config": {"type": "DHCP Request 172.16.10.11\n0.0.0.0 > 255.255.255.255", "path": "edge_mnym51eah6wx0v6s9h", "source": "l2sw1", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461830787"}], [{"data": {"id": "pkt_UOFM0RW3", "label": "DHCP Request 172.16.10.11\n172.16.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "DHCP Request 172.16.10.11\n172.16.10.3 > 192.168.10.2", "path": "edge_mnym5331cjpgqzraqy4", "source": "router_2", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461847123"}], [{"data": {"id": "pkt_SNL7BILN", "label": "DHCP Request 172.16.10.11\n172.16.10.3 > 192.168.10.2", "type": "packet"}, "config": {"type": "DHCP Request 172.16.10.11\n172.16.10.3 > 192.168.10.2", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "l2sw2", "target": "server_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461867744"}], [{"data": {"id": "pkt_GX9TXBCM", "label": "DHCP ACK\n192.168.10.2 > 172.16.10.3", "type": "packet"}, "config": {"type": "DHCP ACK\n192.168.10.2 > 172.16.10.3", "path": "edge_mnwcvw5j8b38v1xr5sj", "source": "server_1", "target": "l2sw2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461887205"}], [{"data": {"id": "pkt_8B7FO3UC", "label": "DHCP ACK\n192.168.10.2 > 172.16.10.3", "type": "packet"}, "config": {"type": "DHCP ACK\n192.168.10.2 > 172.16.10.3", "path": "edge_mnym5331cjpgqzraqy4", "source": "l2sw2", "target": "router_2", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461902698"}], [{"data": {"id": "pkt_WGR692VK", "label": "DHCP ACK\n172.16.10.3 > 172.16.10.11", "type": "packet"}, "config": {"type": "DHCP ACK\n172.16.10.3 > 172.16.10.11", "path": "edge_mnym51eah6wx0v6s9h", "source": "router_2", "target": "l2sw1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461917959"}], [{"data": {"id": "pkt_X3GNFLA1", "label": "DHCP ACK\n172.16.10.3 > 172.16.10.11", "type": "packet"}, "config": {"type": "DHCP ACK\n172.16.10.3 > 172.16.10.11", "path": "edge_mnxf20jpi9lk28vbr2", "source": "l2sw1", "target": "host_1", "loss_percentage": 0.0, "duplicate_percentage": 0.0}, "timestamp": "1776274461933400"}]]
\ No newline at end of file
diff --git a/back/tests/test_json/dhcp_relay_one_host_network.json b/back/tests/test_json/dhcp_relay_one_host_network.json
new file mode 100644
index 00000000..f5e3d19d
--- /dev/null
+++ b/back/tests/test_json/dhcp_relay_one_host_network.json
@@ -0,0 +1,233 @@
+{
+ "nodes": [
+ {
+ "classes":[
+ "l2_switch"
+ ],
+ "config":{
+ "label":"l2sw1",
+ "stp":0,
+ "type":"l2_switch"
+ },
+ "data":{
+ "id":"l2sw1",
+ "label":"l2sw1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnxf20jpi9lk28vbr2",
+ "id":"l2sw1_1",
+ "name":"l2sw1_1",
+ "type_connection":null,
+ "vlan":null
+ },
+ {
+ "connect":"edge_mnym51eah6wx0v6s9h",
+ "id":"l2sw1_2",
+ "name":"l2sw1_2",
+ "type_connection":null,
+ "vlan":null
+ }
+ ],
+ "position":{
+ "x":250,
+ "y":200
+ }
+ },
+ {
+ "classes":[
+ "l2_switch"
+ ],
+ "config":{
+ "label":"l2sw2",
+ "stp":0,
+ "type":"l2_switch"
+ },
+ "data":{
+ "id":"l2sw2",
+ "label":"l2sw2"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvw5j8b38v1xr5sj",
+ "id":"l2sw2_2",
+ "name":"l2sw2_2",
+ "type_connection":null,
+ "vlan":null
+ },
+ {
+ "connect":"edge_mnym5331cjpgqzraqy4",
+ "id":"l2sw2_3",
+ "name":"l2sw2_3",
+ "type_connection":null,
+ "vlan":null
+ }
+ ],
+ "position":{
+ "x":450,
+ "y":200
+ }
+ },
+ {
+ "classes":[
+ "server"
+ ],
+ "config":{
+ "default_gw":"192.168.10.3",
+ "label":"server_1",
+ "type":"server"
+ },
+ "data":{
+ "id":"server_1",
+ "label":"server_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnwcvw5j8b38v1xr5sj",
+ "id":"iface_16417632",
+ "ip":"192.168.10.2",
+ "name":"iface_16417632",
+ "netmask":24
+ }
+ ],
+ "position":{
+ "x":450,
+ "y":300
+ }
+ },
+ {
+ "classes":[
+ "host"
+ ],
+ "config":{
+ "default_gw":"",
+ "label":"host_1",
+ "type":"host"
+ },
+ "data":{
+ "id":"host_1",
+ "label":"host_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnxf20jpi9lk28vbr2",
+ "id":"iface_46653148",
+ "name":"iface_46653148"
+ }
+ ],
+ "position":{
+ "x":250,
+ "y":300
+ }
+ },
+ {
+ "classes":[
+ "l3_router"
+ ],
+ "config":{
+ "default_gw":"",
+ "label":"router_1",
+ "type":"router"
+ },
+ "data":{
+ "id":"router_2",
+ "label":"router_1"
+ },
+ "interface":[
+ {
+ "connect":"edge_mnym51eah6wx0v6s9h",
+ "id":"iface_02514152",
+ "ip":"172.16.10.3",
+ "name":"iface_02514152",
+ "netmask":24
+ },
+ {
+ "connect":"edge_mnym5331cjpgqzraqy4",
+ "id":"iface_64411470",
+ "ip":"192.168.10.3",
+ "name":"iface_64411470",
+ "netmask":24
+ }
+ ],
+ "position":{
+ "x":350,
+ "y":100
+ }
+ }
+ ],
+ "edges": [
+ {
+ "data":{
+ "id":"edge_mnwcvw5j8b38v1xr5sj",
+ "source":"l2sw2",
+ "target":"server_1",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnxf20jpi9lk28vbr2",
+ "source":"host_1",
+ "target":"l2sw1",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnym51eah6wx0v6s9h",
+ "source":"l2sw1",
+ "target":"router_2",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ },
+ {
+ "data":{
+ "id":"edge_mnym5331cjpgqzraqy4",
+ "source":"l2sw2",
+ "target":"router_2",
+ "loss_percentage":0,
+ "duplicate_percentage":0
+ }
+ }
+ ],
+ "jobs": [
+ {
+ "id":"2fb202bfe7bd48d390734b68f0e2a61d",
+ "job_id":203,
+ "print_cmd":"dhcp ip range: 172.16.10.10,172.16.10.100/24 gw:172.16.10.3",
+ "arg_1":"172.16.10.10",
+ "arg_2":"172.16.10.100",
+ "arg_3":"24",
+ "arg_4":"172.16.10.3",
+ "arg_5":"iface_16417632",
+ "level":1,
+ "host_id":"server_1"
+ },
+ {
+ "id":"773ccad1dc9244ca84603adfc4996b13",
+ "job_id":108,
+ "print_cmd":"dhcp client",
+ "arg_1":"iface_46653148",
+ "level":2,
+ "host_id":"host_1"
+ },
+ {
+ "id":"b9a5d85ff6824c77ac6b79916868dde9",
+ "job_id":204,
+ "print_cmd":"dnsmasq --dhcp-relay=172.16.10.3,192.168.10.2", "arg_1":"192.168.10.2",
+ "arg_2":"172.16.10.3",
+ "level":2,
+ "host_id":"router_2"
+ }
+ ],
+ "config": {
+ "zoom": 2,
+ "pan_x": 226,
+ "pan_y": 32
+ },
+ "pcap": [],
+ "packets": "[]"
+}
\ No newline at end of file
diff --git a/front/.env b/front/.env
index 6df934b1..b8d53e48 100755
--- a/front/.env
+++ b/front/.env
@@ -32,4 +32,4 @@ REFRESH_TOKEN_EXPIRES=12
# YANDEX_POSTGRES_SSLMODE=verify-full
# Режим работы: dev (локальный PostgreSQL) или prod (Yandex Cloud PostgreSQL)
-MODE=dev
\ No newline at end of file
+MODE=prod
\ No newline at end of file
diff --git a/front/src/miminet_host.py b/front/src/miminet_host.py
index fb2ae734..8bf6776a 100755
--- a/front/src/miminet_host.py
+++ b/front/src/miminet_host.py
@@ -425,6 +425,15 @@ def build_error(error_type: str, cmd: str) -> str:
emptiness_check
).set_error_msg('Не указан интерфейс для команды "Добавить ARP Proxy-интерфейс"')
+# Add DHCP Relay
+dhcp_relay_job = router.create_job(204, "dnsmasq --dhcp-relay=[1],[0]")
+dhcp_relay_job.add_param("config_router_dhcp_relay_server_ip_input_field").add_check(
+ IPv4_check
+).set_error_msg('Неверно указан IP адрес для команды "Включить DHCP Relay"')
+dhcp_relay_job.add_param("config_router_dhcp_relay_listening_ip_input_field").add_check(
+ IPv4_check
+).set_error_msg('Неверно указан IP адрес для команды "Включить DHCP Relay"')
+
# ~ ~ ~ SWITCH JOBS ~ ~ ~
link_down_job = switch.create_job(6, "link down [1]")
diff --git a/front/src/static/config_devices.js b/front/src/static/config_devices.js
index 52954b62..a9ae7c06 100644
--- a/front/src/static/config_devices.js
+++ b/front/src/static/config_devices.js
@@ -935,6 +935,9 @@ const ConfigRouterJobOnChange = function(evnt) {
UpdateRouterForm('config_router_add_port_forwarding_udp_script');
FillDeviceSelectIntf("#config_router_add_port_forwarding_udp_iface_select_field", "#router_id", "Выберите линк", false)
break;
+ case '204':
+ UpdateRouterForm('config_router_dhcp_relay_script');
+ break;
default:
console.log("Unknown target.value");
}
@@ -1412,6 +1415,11 @@ const EditJobInRouter = function(router_id, job_id, network_guid) {
$('#config_router_add_port_forwarding_udp_dest_ip_input_field').val(job.arg_3 || '')
$('#config_router_add_port_forwarding_udp_dest_port_input_field').val(job.arg_4 || '')
break;
+ case '204': // Добавить DHCP Relay
+ UpdateRouterForm('config_router_dhcp_relay_script');
+ $('#config_router_dhcp_relay_server_ip_input_field').val(job.arg_1 || '');
+ $('#config_router_dhcp_relay_listening_ip_input_field').val(job.arg_2 || '');
+ break;
default:
console.error('Unknown job type for editing:', job.job_id);
}
diff --git a/front/src/static/config_router.html b/front/src/static/config_router.html
index 51ff6fdf..2c3f6385 100755
--- a/front/src/static/config_router.html
+++ b/front/src/static/config_router.html
@@ -56,6 +56,7 @@
+
@@ -208,3 +209,12 @@
+
+
diff --git a/front/tests/test_dhcp_relay.py b/front/tests/test_dhcp_relay.py
new file mode 100644
index 00000000..5f4edabd
--- /dev/null
+++ b/front/tests/test_dhcp_relay.py
@@ -0,0 +1,243 @@
+import pytest
+from conftest import MiminetTester
+from utils.networks import NodeType, MiminetTestNetwork
+from utils.locators import Location
+from utils.checkers import TestNetworkComparator
+
+
+class TestDHCPRelay:
+
+ @pytest.fixture(scope="class")
+ def network(self, selenium: MiminetTester):
+ network = MiminetTestNetwork(selenium)
+
+ # nodes
+ network.add_node(NodeType.Host, 25, 100) # host
+ network.add_node(NodeType.Switch, 25, 50) # switch 1
+ network.add_node(NodeType.Router, 75, 0) # router
+ network.add_node(NodeType.Switch, 100, 50) # switch 2
+ network.add_node(NodeType.Server, 100, 100) # server
+
+ # edges
+ network.add_edge(0, 1) # host -> switch 1
+ network.add_edge(1, 2) # switch 1 -> router
+ network.add_edge(2, 3) # router -> switch 2
+ network.add_edge(3, 4) # switch 2 -> server
+
+ # configure host
+ host_iface = network.nodes[0]["interface"][0]["id"]
+ host_config = network.open_node_config(0)
+ host_config.add_jobs(
+ 108,
+ {Location.Network.ConfigPanel.Host.Job.DHCLIENT_INTF.selector: host_iface},
+ )
+ host_config.submit()
+
+ # configure router
+ router_config = network.open_node_config(2)
+ router_config.fill_link("172.16.10.3", 24)
+ router_config.fill_link("192.168.10.3", 24, 1)
+ router_config.add_jobs(
+ 204,
+ {
+ Location.Network.ConfigPanel.Router.Job.DHCP_RELAY_SERVER_IP_INPUT_FIELD.selector: "192.168.10.2",
+ Location.Network.ConfigPanel.Router.Job.DHCP_RELAY_LISTENING_IP_INPUT_FIELD.selector: "172.16.10.3",
+ },
+ )
+ router_config.submit()
+
+ # config server
+ server_iface = network.nodes[4]["interface"][0]["id"]
+ server_config = network.open_node_config(4)
+ server_config.fill_link("192.168.10.2", 24)
+ server_config.fill_default_gw("192.168.10.3")
+ server_config.add_jobs(
+ 203,
+ {
+ Location.Network.ConfigPanel.Server.Job.DHCP_IP_RANGE_START_FIELD.selector: "172.16.10.10",
+ Location.Network.ConfigPanel.Server.Job.DHCP_IP_RANGE_END_FIELD.selector: "172.16.10.100",
+ Location.Network.ConfigPanel.Server.Job.DHCP_MASK_FIELD.selector: "24",
+ Location.Network.ConfigPanel.Server.Job.DHCP_IP_GW_FIELD.selector: "172.16.10.3",
+ Location.Network.ConfigPanel.Server.Job.DHCP_INTF.selector: server_iface,
+ },
+ )
+ server_config.submit()
+
+ yield network
+
+ network.delete()
+
+ def test_dhcp(self, selenium: MiminetTester, network: MiminetTestNetwork):
+ assert TestNetworkComparator.compare_nodes(network.nodes, self.JSON_NODES)
+ assert TestNetworkComparator.compare_edges(network.edges, self.JSON_EDGES)
+ assert TestNetworkComparator.compare_jobs(network.jobs, self.JSON_JOBS)
+
+ JSON_NODES = [
+ {
+ "classes": ["host"],
+ "config": {"default_gw": "", "label": "host_1", "type": "host"},
+ "data": {"id": "host_1", "label": "host_1"},
+ "interface": [
+ {
+ "connect": "edge_mo0ldvxj74nu3vhlaio",
+ "id": "iface_16773533",
+ "name": "iface_16773533",
+ }
+ ],
+ "position": {"x": 25, "y": 100},
+ },
+ {
+ "classes": ["l2_switch"],
+ "config": {"label": "l2sw1", "stp": 0, "type": "l2_switch"},
+ "data": {"id": "l2sw1", "label": "l2sw1"},
+ "interface": [
+ {
+ "connect": "edge_mo0ldvxj74nu3vhlaio",
+ "id": "l2sw1_1",
+ "name": "l2sw1_1",
+ "type_connection": None,
+ "vlan": None,
+ },
+ {
+ "connect": "edge_mo0ldxdrueu0023e09",
+ "id": "l2sw1_2",
+ "name": "l2sw1_2",
+ "type_connection": None,
+ "vlan": None,
+ },
+ ],
+ "position": {"x": 25, "y": 50},
+ },
+ {
+ "classes": ["l3_router"],
+ "config": {"default_gw": "", "label": "router_1", "type": "router"},
+ "data": {"id": "router_1", "label": "router_1"},
+ "interface": [
+ {
+ "connect": "edge_mo0ldxdrueu0023e09",
+ "id": "iface_40424245",
+ "ip": "172.16.10.3",
+ "name": "iface_40424245",
+ "netmask": 24,
+ },
+ {
+ "connect": "edge_mo0ldyffpg9scxw32yq",
+ "id": "iface_77344228",
+ "ip": "192.168.10.3",
+ "name": "iface_77344228",
+ "netmask": 24,
+ },
+ ],
+ "position": {"x": 75, "y": 0},
+ },
+ {
+ "classes": ["l2_switch"],
+ "config": {"label": "l2sw2", "stp": 0, "type": "l2_switch"},
+ "data": {"id": "l2sw2", "label": "l2sw2"},
+ "interface": [
+ {
+ "connect": "edge_mo0ldyffpg9scxw32yq",
+ "id": "l2sw2_1",
+ "name": "l2sw2_1",
+ "type_connection": None,
+ "vlan": None,
+ },
+ {
+ "connect": "edge_mo0ldzsfp1nakly2jf",
+ "id": "l2sw2_2",
+ "name": "l2sw2_2",
+ "type_connection": None,
+ "vlan": None,
+ },
+ ],
+ "position": {"x": 100, "y": 50},
+ },
+ {
+ "classes": ["server"],
+ "config": {
+ "default_gw": "192.168.10.3",
+ "label": "server_1",
+ "type": "server",
+ },
+ "data": {"id": "server_1", "label": "server_1"},
+ "interface": [
+ {
+ "connect": "edge_mo0ldzsfp1nakly2jf",
+ "id": "iface_28835140",
+ "ip": "192.168.10.2",
+ "name": "iface_28835140",
+ "netmask": 24,
+ }
+ ],
+ "position": {"x": 100, "y": 100},
+ },
+ ]
+ JSON_EDGES = [
+ {
+ "data": {
+ "id": "edge_mo0ldvxj74nu3vhlaio",
+ "source": "host_1",
+ "target": "l2sw1",
+ "loss_percentage": 0,
+ "duplicate_percentage": 0,
+ }
+ },
+ {
+ "data": {
+ "id": "edge_mo0ldxdrueu0023e09",
+ "source": "l2sw1",
+ "target": "router_1",
+ "loss_percentage": 0,
+ "duplicate_percentage": 0,
+ }
+ },
+ {
+ "data": {
+ "id": "edge_mo0ldyffpg9scxw32yq",
+ "source": "router_1",
+ "target": "l2sw2",
+ "loss_percentage": 0,
+ "duplicate_percentage": 0,
+ }
+ },
+ {
+ "data": {
+ "id": "edge_mo0ldzsfp1nakly2jf",
+ "source": "l2sw2",
+ "target": "server_1",
+ "loss_percentage": 0,
+ "duplicate_percentage": 0,
+ }
+ },
+ ]
+ JSON_JOBS = [
+ {
+ "id": "e8611d72c979483b940ddc5d2acebe6d",
+ "job_id": 108,
+ "print_cmd": "dhcp client",
+ "arg_1": "iface_16773533",
+ "level": 0,
+ "host_id": "host_1",
+ },
+ {
+ "id": "317a8189832c4477a8c34f916c9d8dbe",
+ "job_id": 204,
+ "print_cmd": "dnsmasq --dhcp-relay=172.16.10.3,192.168.10.2",
+ "arg_1": "192.168.10.2",
+ "arg_2": "172.16.10.3",
+ "level": 1,
+ "host_id": "router_1",
+ },
+ {
+ "id": "437af3d8726a4363940552c89c201693",
+ "job_id": 203,
+ "print_cmd": "dhcp ip range: 172.16.10.10,172.16.10.100/24 gw: 172.16.10.3",
+ "arg_1": "172.16.10.10",
+ "arg_2": "172.16.10.100",
+ "arg_3": "24",
+ "arg_4": "172.16.10.3",
+ "arg_5": "iface_28835140",
+ "level": 2,
+ "host_id": "server_1",
+ },
+ ]
diff --git a/front/tests/utils/locators.py b/front/tests/utils/locators.py
index 7be2c735..867aa134 100644
--- a/front/tests/utils/locators.py
+++ b/front/tests/utils/locators.py
@@ -286,6 +286,12 @@ class Job:
PORT_FORWARDING_UDP_DEST_PORT_FIELD = Locator(
"#config_router_add_port_forwarding_udp_dest_port_input_field"
)
+ DHCP_RELAY_SERVER_IP_INPUT_FIELD = Locator(
+ "#config_router_dhcp_relay_server_ip_input_field"
+ )
+ DHCP_RELAY_LISTENING_IP_INPUT_FIELD = Locator(
+ "#config_router_dhcp_relay_listening_ip_input_field"
+ )
class Server(CommonDevice):
MAIN_FORM = Locator("#config_main_form")