Pushing existing code
This commit is contained in:
parent
29a131b755
commit
439cf7e389
32
README.md
32
README.md
@ -1,2 +1,32 @@
|
|||||||
# Midas
|
# midas
|
||||||
|
A topology aware zero touch provisioning (ZTP) tool for network devices.
|
||||||
|
|
||||||
|
## Supported operating systems:
|
||||||
|
- Juniper Junos
|
||||||
|
- Cisco IOS
|
||||||
|
|
||||||
|
## Modules
|
||||||
|
### midasd
|
||||||
|
The socket DHCP portion of midas.
|
||||||
|
|
||||||
|
### midast
|
||||||
|
The networkx topology portion of midas.
|
||||||
|
|
||||||
|
### midasp
|
||||||
|
The YAML/Jinja provisioning portion of midas.
|
||||||
|
|
||||||
|
## Installation / usage
|
||||||
|
```
|
||||||
|
$ cd midas
|
||||||
|
$ chmod +x midas
|
||||||
|
$ sudo ./midas
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tips
|
||||||
|
- When building your topology.yaml the visualisation builds top to bottom, arrange your nodes and edges in such a way that they correspond to the correct side of the graph, for neatness.
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- networkx
|
||||||
|
- pygraphviz
|
||||||
|
- yaml
|
||||||
|
- jinja2
|
||||||
|
119
client
Executable file
119
client
Executable file
@ -0,0 +1,119 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import socket, sys
|
||||||
|
|
||||||
|
MAX_BYTES = 1024
|
||||||
|
ServerPort = 67
|
||||||
|
ClientPort = 68
|
||||||
|
|
||||||
|
class DHCPClient():
|
||||||
|
def client(self):
|
||||||
|
print("DHCP client is starting...\n")
|
||||||
|
dest = ('<broadcast>', ServerPort)
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||||
|
s.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
|
||||||
|
s.bind(('0.0.0.0', ServerPort))
|
||||||
|
|
||||||
|
print("Sending Juniper DHCP discovery.")
|
||||||
|
packet = DHCPClient.mock_juniper_packet()
|
||||||
|
s.sendto(packet, dest)
|
||||||
|
|
||||||
|
def mock_juniper_packet():
|
||||||
|
return b'\x01\x01\x06\x01O\x00\xe0\xa1\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x94\xbf\x94\xb3n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x013\x04\x00\x01Q\x80\x0c\nLAB-SRX300\xff\x00'
|
||||||
|
|
||||||
|
def craft_discover_packet():
|
||||||
|
OP = bytes([0x01])
|
||||||
|
HTYPE = bytes([0x01])
|
||||||
|
HLEN = bytes([0x06])
|
||||||
|
HOPS = bytes([0x00])
|
||||||
|
XID = bytes([0x39, 0x03, 0xF3, 0x26])
|
||||||
|
SECS = bytes([0x00, 0x00])
|
||||||
|
FLAGS = bytes([0x00, 0x00])
|
||||||
|
CIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
YIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
SIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
GIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
CHADDR1 = bytes([0x00, 0x05, 0x3C, 0x04])
|
||||||
|
CHADDR2 = bytes([0x8D, 0x59, 0x00, 0x00])
|
||||||
|
CHADDR3 = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
CHADDR4 = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
CHADDR5 = bytes(192)
|
||||||
|
Magiccookie = bytes([0x63, 0x82, 0x53, 0x63])
|
||||||
|
DHCPOptions1 = bytes([53, 1, 1]) #DHCP Discover (value = 1)
|
||||||
|
DHCPOptions2 = bytes([50, 4, 0xC0, 0xA8, 0x01, 0x64])
|
||||||
|
|
||||||
|
packet = (
|
||||||
|
OP
|
||||||
|
+ HTYPE
|
||||||
|
+ HLEN
|
||||||
|
+ HOPS
|
||||||
|
+ XID
|
||||||
|
+ SECS
|
||||||
|
+ FLAGS
|
||||||
|
+ CIADDR
|
||||||
|
+ YIADDR
|
||||||
|
+ SIADDR
|
||||||
|
+ GIADDR
|
||||||
|
+ CHADDR1
|
||||||
|
+ CHADDR2
|
||||||
|
+ CHADDR3
|
||||||
|
+ CHADDR4
|
||||||
|
+ CHADDR5
|
||||||
|
+ Magiccookie
|
||||||
|
+ DHCPOptions1
|
||||||
|
+ DHCPOptions2
|
||||||
|
)
|
||||||
|
|
||||||
|
return packet
|
||||||
|
|
||||||
|
def craft_request_packet():
|
||||||
|
OP = bytes([0x01])
|
||||||
|
HTYPE = bytes([0x01])
|
||||||
|
HLEN = bytes([0x06])
|
||||||
|
HOPS = bytes([0x00])
|
||||||
|
XID = bytes([0x39, 0x03, 0xF3, 0x26])
|
||||||
|
SECS = bytes([0x00, 0x00])
|
||||||
|
FLAGS = bytes([0x00, 0x00])
|
||||||
|
CIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
YIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
SIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
GIADDR = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
CHADDR1 = bytes([0x00, 0x0C, 0x29, 0xDD])
|
||||||
|
CHADDR2 = bytes([0x5C, 0xA7, 0x00, 0x00])
|
||||||
|
CHADDR3 = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
CHADDR4 = bytes([0x00, 0x00, 0x00, 0x00])
|
||||||
|
CHADDR5 = bytes(192)
|
||||||
|
Magiccookie = bytes([0x63, 0x82, 0x53, 0x63])
|
||||||
|
DHCPOptions1 = bytes([53, 1, 3]) #DHCP Discover (value = 3)
|
||||||
|
DHCPOptions2 = bytes([50, 4, 0xC0, 0xA8, 0x01, 0x64])
|
||||||
|
DHCPOptions3 = bytes([54, 4, 0xC0, 0xA8, 0x01, 0x01])
|
||||||
|
|
||||||
|
packet = (
|
||||||
|
OP
|
||||||
|
+ HTYPE
|
||||||
|
+ HLEN
|
||||||
|
+ HOPS
|
||||||
|
+ XID
|
||||||
|
+ SECS
|
||||||
|
+ FLAGS
|
||||||
|
+ CIADDR
|
||||||
|
+ YIADDR
|
||||||
|
+ SIADDR
|
||||||
|
+ GIADDR
|
||||||
|
+ CHADDR1
|
||||||
|
+ CHADDR2
|
||||||
|
+ CHADDR3
|
||||||
|
+ CHADDR4
|
||||||
|
+ CHADDR5
|
||||||
|
+ Magiccookie
|
||||||
|
+ DHCPOptions1
|
||||||
|
+ DHCPOptions2
|
||||||
|
+ DHCPOptions3
|
||||||
|
)
|
||||||
|
|
||||||
|
return packet
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
dhcp_client = DHCPClient()
|
||||||
|
dhcp_client.client()
|
BIN
dhcp/.DS_Store
vendored
Normal file
BIN
dhcp/.DS_Store
vendored
Normal file
Binary file not shown.
375
dhcp/midasd.py
Normal file
375
dhcp/midasd.py
Normal file
@ -0,0 +1,375 @@
|
|||||||
|
import socket
|
||||||
|
import struct
|
||||||
|
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from datetime import datetime
|
||||||
|
from utils.log import log
|
||||||
|
|
||||||
|
# Reference docs
|
||||||
|
# https://docs.microsoft.com/en-us/windows-server/troubleshoot/dynamic-host-configuration-protocol-basics
|
||||||
|
|
||||||
|
FORMAT_STRING = (
|
||||||
|
"!" # Specifies network byte order
|
||||||
|
"s" # OP: 1 byte
|
||||||
|
"s" # HTYPE: 1 byte
|
||||||
|
"s" # HLEN: 1 byte
|
||||||
|
"s" # HOPS: 1 byte
|
||||||
|
"4s" # XID: 4 bytes
|
||||||
|
"2s" # SECS: 2 bytes
|
||||||
|
"2s" # FLAGS: 2 bytes
|
||||||
|
"4s" # CIADDR: 4 bytes
|
||||||
|
"4s" # YIADDR: 4 bytes
|
||||||
|
"4s" # SIADDR: 4 bytes
|
||||||
|
"4s" # GIADDR: 4 bytes
|
||||||
|
"6s" # CHADDR: 6 bytes
|
||||||
|
"10s" # CHADDR: 10 bytes
|
||||||
|
"192s" # OVERFLOW: 192 bytes
|
||||||
|
"4s" # MAGICCOOKIE: 4 bytes
|
||||||
|
) # 240 bytes total
|
||||||
|
|
||||||
|
OPTION_DESCRIPTIONS = {
|
||||||
|
1: 'Subnet Mask',
|
||||||
|
3: 'Default Gateway',
|
||||||
|
12: 'Hostname',
|
||||||
|
43: 'Vendor Specific Information',
|
||||||
|
50: 'Requested IP Address',
|
||||||
|
51: 'IP Address Lease Time',
|
||||||
|
53: 'DHCP Message Type',
|
||||||
|
54: 'DHCP Server IP Address',
|
||||||
|
55: 'Parameter Request List',
|
||||||
|
57: 'DHCP Maximum Message Size',
|
||||||
|
60: 'Class Identifier',
|
||||||
|
61: 'Client Identifier',
|
||||||
|
67: 'Boot File Name',
|
||||||
|
150: 'TFTP Server IP Address',
|
||||||
|
}
|
||||||
|
|
||||||
|
@dataclass()
|
||||||
|
class DHCPPacket:
|
||||||
|
# Based on DHCP Discovery packet header from: https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol
|
||||||
|
# Packet header field byte sizing: https://support.huawei.com/enterprise/en/doc/EDOC1100058931/25cd2dfc/dhcp-messages
|
||||||
|
# DHCP packet types: https://www.omnisecu.com/tcpip/dhcp-dynamic-host-configuration-protocol-message-options.php
|
||||||
|
|
||||||
|
OP: bytes
|
||||||
|
HTYPE: bytes
|
||||||
|
HLEN: bytes
|
||||||
|
HOPS: bytes
|
||||||
|
XID: bytes
|
||||||
|
SECS: bytes
|
||||||
|
FLAGS: bytes
|
||||||
|
CIADDR: bytes # Client IP address
|
||||||
|
YIADDR: bytes # DHCP Server IP address
|
||||||
|
SIADDR: bytes # Server IP address
|
||||||
|
GIADDR: bytes # Gateway IP address
|
||||||
|
CHADDR: bytes # Client hardware address
|
||||||
|
CHADDR_PADDING: bytes
|
||||||
|
OVERFLOW: bytes # Overflow space for BOOTP legacy
|
||||||
|
MCOOKIE: bytes
|
||||||
|
OPTIONS: bytes
|
||||||
|
END: bytes = bytes([0xFF])
|
||||||
|
|
||||||
|
class Packet():
|
||||||
|
@staticmethod
|
||||||
|
def construct_tlv(_type, value):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Constructs a DHCP TLV - Type | Length | Value.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
_type: typically the DHCP option number
|
||||||
|
value: value of the TLV, can be text or integer
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A bytes object TLV
|
||||||
|
'''
|
||||||
|
if isinstance(value, str):
|
||||||
|
value = str.encode(value)
|
||||||
|
return bytes([_type, len(value)]) + value
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def construct_junos_suboptions(config_filename):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Constructs Junos specific suboptions required for Zero Touch Provisioning to work.
|
||||||
|
|
||||||
|
0: firmware file
|
||||||
|
1: configuration file
|
||||||
|
3: transfer mode
|
||||||
|
|
||||||
|
Additional options are available, see:
|
||||||
|
https://www.juniper.net/documentation/us/en/software/junos/junos-install-upgrade/topics/topic-map/zero-touch-provision.html#id-zero-touch-provisioning-using-dhcp-options
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
config_filename: filename and path of the configuration file to be retrieved and applied to the client
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A bytes object consisting of multiple TLVs
|
||||||
|
'''
|
||||||
|
return Packet.construct_tlv(
|
||||||
|
_type=43,
|
||||||
|
value=b''.join(
|
||||||
|
[
|
||||||
|
#Packet.construct_tlv(0, '/path/to/junosimage.tgz'), # Image filename and path, for upgrading firmware
|
||||||
|
Packet.construct_tlv(1, config_filename), # Configuration filename and path
|
||||||
|
Packet.construct_tlv(3, 'tftp'), # Transfer mode
|
||||||
|
]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def construct_reply_packet(reference_packet, _type, source_ip, topology):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Constructs a DHCP packet of type Offer or Ack, modelled from a received packet with some options changed/added.
|
||||||
|
Ack packets are the exact same as Offer packets, just with the packet type TLV adjusted.
|
||||||
|
Different options are changed/added based on client operating system extracted from the networkx Graph object.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
reference_packet: received DHCP packet to model the reply packet from
|
||||||
|
_type: type of packet to construct, Offer or Ack
|
||||||
|
source_ip: IP address that the DHCP packet was received from
|
||||||
|
topology: networkx Graph object detailing the network topology
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
DHCP packet of type Offer or Ack
|
||||||
|
'''
|
||||||
|
packet_data = struct.unpack(FORMAT_STRING, reference_packet[:240])
|
||||||
|
reply_packet_object = DHCPPacket(*packet_data, OPTIONS=reference_packet[240:len(reference_packet)])
|
||||||
|
|
||||||
|
giaddr, yiaddr = Packet.process_giaddr(source_ip)
|
||||||
|
client_device_name, client_device_os = topology.get_client_calling_for_ip(source_ip, _type)
|
||||||
|
|
||||||
|
reply_packet_object.OP = bytes([0x02])
|
||||||
|
reply_packet_object.YIADDR = bytes([yiaddr[0], yiaddr[1], yiaddr[2], yiaddr[3]]) # Client IP
|
||||||
|
reply_packet_object.SIADDR = bytes([172, 16, 0, 200]) # Server IP: 172.16.0.200
|
||||||
|
|
||||||
|
if _type == 'offer':
|
||||||
|
packet_type = bytes([53, 1, 2]) # DHCP offer packet
|
||||||
|
elif _type == 'ack':
|
||||||
|
packet_type = bytes([53, 1, 5]) # DHCP ack packet
|
||||||
|
|
||||||
|
if client_device_os == 'junos':
|
||||||
|
reply_packet_object.OPTIONS = b"".join(
|
||||||
|
[
|
||||||
|
packet_type,
|
||||||
|
bytes([54, 4, 172, 16, 0, 200]), # Server identifier: 172.16.0.200
|
||||||
|
bytes([51, 4, 0x00, 0x01, 0x51, 0x80]), # Lease time: 86400
|
||||||
|
bytes([1, 4, 255, 255, 255, 254]), # Subnet mask: 255.255.255.254
|
||||||
|
bytes([3, 4, giaddr[0], giaddr[1], giaddr[2], giaddr[3]]), # Default gateway
|
||||||
|
bytes([150, 4, 172, 16, 0, 200]), # TFTP server: 172.16.0.200
|
||||||
|
Packet.construct_junos_suboptions(f'/configs/{client_device_name}.conf'), # Juniper specific suboptions
|
||||||
|
]
|
||||||
|
)
|
||||||
|
elif client_device_os == 'cisco_ios':
|
||||||
|
reply_packet_object.OPTIONS = b"".join(
|
||||||
|
[
|
||||||
|
packet_type,
|
||||||
|
bytes([54, 4, 172, 16, 0, 200]), # Server identifier: 172.16.0.200
|
||||||
|
bytes([51, 4, 0x00, 0x01, 0x51, 0x80]), # Lease time: 86400
|
||||||
|
bytes([1, 4, 255, 255, 255, 254]), # Subnet mask: 255.255.255.254
|
||||||
|
bytes([3, 4, giaddr[0], giaddr[1], giaddr[2], giaddr[3]]), # Default gateway
|
||||||
|
bytes([150, 4, 172, 16, 0, 200]), # TFTP server: 172.16.0.200
|
||||||
|
Packet.construct_tlv(67, f'/configs/{client_device_name}.conf'), # Cisco specific bootfile
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
reply_packet_list = []
|
||||||
|
|
||||||
|
for field, byte_value in reply_packet_object.__dict__.items():
|
||||||
|
reply_packet_list.append(byte_value)
|
||||||
|
|
||||||
|
reply_packet = b''.join(value for value in reply_packet_list)
|
||||||
|
|
||||||
|
return reply_packet
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def examine_packet(raw_packet):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Examines a DHCP packet and splits it into packet data and DHCP options.
|
||||||
|
It's expected that DHCP packet length after 240 will be DHCP options.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
raw_packet: raw DHCP packet to examine
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
packet: DHCP packet data minus options
|
||||||
|
options: DHCP packet options
|
||||||
|
options_descriptions: DHCP packet options descriptions
|
||||||
|
'''
|
||||||
|
packet_data = struct.unpack(FORMAT_STRING, raw_packet[:240])
|
||||||
|
packet = DHCPPacket(*packet_data, OPTIONS=raw_packet[240:len(raw_packet)])
|
||||||
|
|
||||||
|
options, options_descriptions = Packet.extract_options(packet.OPTIONS)
|
||||||
|
|
||||||
|
return packet, options, options_descriptions
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def extract_options(packet_data):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Extracts DHCP options from a given raw DHCP packet.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
packet_data: DHCP packet as raw bytes
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
option_dict: dictionairy of DHCP options extracted from a packet
|
||||||
|
option_description_dict: dictionairy of DHCP option descriptions that were found in a packet
|
||||||
|
'''
|
||||||
|
option_dict = {}
|
||||||
|
option_description_dict = {}
|
||||||
|
|
||||||
|
index = 0
|
||||||
|
while index < len(packet_data):
|
||||||
|
option_number = packet_data[index]
|
||||||
|
if option_number == 255:
|
||||||
|
break
|
||||||
|
option_length = packet_data[index + 1]
|
||||||
|
option_value, option_description = Packet.make_human_readable(option_number, packet_data[index + 2 : index + 2 + option_length])
|
||||||
|
|
||||||
|
option_dict[option_number] = option_value
|
||||||
|
option_description_dict[option_number] = option_description
|
||||||
|
index += 2 + option_length
|
||||||
|
|
||||||
|
|
||||||
|
return option_dict, option_description_dict
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def make_human_readable(option_number, raw_value):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Takes a raw DHCP option and makes it human readable.
|
||||||
|
|
||||||
|
DHCP options:
|
||||||
|
https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
option_number: DHCP option number
|
||||||
|
raw_value: raw bytes value received with the option number
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
option_value: human readable value of the option number
|
||||||
|
option_description: description of the option
|
||||||
|
'''
|
||||||
|
if option_number in [1, 3, 50, 54, 150]:
|
||||||
|
octets = list(raw_value)
|
||||||
|
option_value = f'{octets[0]}.{octets[1]}.{octets[2]}.{octets[3]}'
|
||||||
|
elif option_number in [50, 55]:
|
||||||
|
option_value = list(raw_value)
|
||||||
|
elif option_number == 53:
|
||||||
|
option_value = list(raw_value)[0]
|
||||||
|
elif option_number in [60, 12, 61, 67, 43]:
|
||||||
|
option_value = raw_value.decode()
|
||||||
|
elif option_number in [51, 57]:
|
||||||
|
option_value = int.from_bytes(raw_value, byteorder='big')
|
||||||
|
else:
|
||||||
|
option_value = raw_value
|
||||||
|
|
||||||
|
if option_number in OPTION_DESCRIPTIONS:
|
||||||
|
option_description = OPTION_DESCRIPTIONS[option_number]
|
||||||
|
else:
|
||||||
|
option_description = 'Unknown'
|
||||||
|
|
||||||
|
return option_value, option_description
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def process_giaddr(giaddr):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Calculates IP address to offer the client and turns string giaddr into a list of 4 integers.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
giaddr: gateway or relay IP that the DHCP packet was received from (string)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
giaddr: gateway or relay IP that the DHCP packet was received from (list of 4 integers)
|
||||||
|
yiaddr: IP to offer the client (list of 4 integers)
|
||||||
|
'''
|
||||||
|
octets = giaddr.split('.')
|
||||||
|
last_octet = int(octets[3])
|
||||||
|
giaddr = [int(octets[0]), int(octets[1]), int(octets[2]), int(octets[3])]
|
||||||
|
|
||||||
|
if last_octet % 2 == 0:
|
||||||
|
yiaddr = [int(octets[0]), int(octets[1]), int(octets[2]), int(last_octet + 1)]
|
||||||
|
else:
|
||||||
|
yiaddr = [int(octets[0]), int(octets[1]), int(octets[2]), int(last_octet - 1)]
|
||||||
|
|
||||||
|
return giaddr, yiaddr
|
||||||
|
|
||||||
|
class DHCPServer():
|
||||||
|
MAX_BYTES = 1024
|
||||||
|
SERVER_IP = '172.16.0.200'
|
||||||
|
PORT = 67
|
||||||
|
|
||||||
|
def create_socket(self):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Creates a socket to allow the DHCP server to send and receive data to/from.
|
||||||
|
|
||||||
|
Socket characteristics:
|
||||||
|
AF_INET: type of socket, address format (host, port)
|
||||||
|
SOCK_DGRAM: socket protocol, UDP
|
||||||
|
SOL_SOCKET (socket options):
|
||||||
|
SO_REUSEADDR: socket address and port can be reused
|
||||||
|
SO_BROADCAST: datagrams can be broadcast from this socket
|
||||||
|
|
||||||
|
Socket bound to:
|
||||||
|
SERVER_IP
|
||||||
|
PORT
|
||||||
|
'''
|
||||||
|
_socket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1)
|
||||||
|
_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST,1)
|
||||||
|
_socket.bind((self.SERVER_IP, self.PORT))
|
||||||
|
|
||||||
|
return _socket
|
||||||
|
|
||||||
|
def run(self, topology):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Runs the DHCP server allowing packets to be sent and received through the created socket.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
topology: networkx Graph object detailing the network topology
|
||||||
|
'''
|
||||||
|
log('DHCP server is starting...', 'info')
|
||||||
|
socket = self.create_socket()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
log('Waiting for DHCP packets...', 'info')
|
||||||
|
received_packet, (source_address, source_port) = socket.recvfrom(self.MAX_BYTES)
|
||||||
|
log(f'DHCP packet received from IP {source_address} on port {source_port}', 'info')
|
||||||
|
packet_data, packet_options, option_descriptions = Packet.examine_packet(received_packet)
|
||||||
|
log(f'Received packet options:', 'info')
|
||||||
|
for option_number, option_value in packet_options.items():
|
||||||
|
log(f'{option_number} [{option_descriptions[option_number]}]: {option_value}', 'info')
|
||||||
|
|
||||||
|
if packet_options[53] == 1:
|
||||||
|
log('Received DHCP discover packet', 'info')
|
||||||
|
offer_packet = Packet.construct_reply_packet(received_packet, 'offer', source_address, topology)
|
||||||
|
log('Constructing DHCP offer packet...', 'info')
|
||||||
|
packet_data, packet_options, option_descriptions = Packet.examine_packet(offer_packet)
|
||||||
|
log(f'Offer packet options:', 'info')
|
||||||
|
for option_number, option_value in packet_options.items():
|
||||||
|
log(f'{option_number} [{option_descriptions[option_number]}]: {option_value}', 'info')
|
||||||
|
socket.sendto(offer_packet, (source_address, self.PORT))
|
||||||
|
|
||||||
|
elif packet_options[53] == 3:
|
||||||
|
log('Received DHCP request packet', 'info')
|
||||||
|
ack_packet = Packet.construct_reply_packet(offer_packet, 'ack', source_address, topology)
|
||||||
|
log('Constructing DHCP ack packet...', 'info')
|
||||||
|
packet_data, packet_options, option_descriptions = Packet.examine_packet(ack_packet)
|
||||||
|
log(f'Ack packet options:', 'info')
|
||||||
|
for option_number, option_value in packet_options.items():
|
||||||
|
log(f'{option_number} [{option_descriptions[option_number]}]: {option_value}', 'info')
|
||||||
|
socket.sendto(ack_packet, (source_address, self.PORT))
|
||||||
|
|
||||||
|
elif packet_options[53] == 7:
|
||||||
|
log('Received DHCP release packet', 'info')
|
||||||
|
log('See you around, partner\U0001F920', 'info')
|
||||||
|
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
log('Exiting...', 'info')
|
||||||
|
break
|
BIN
dhcp/packet_examples/.DS_Store
vendored
Normal file
BIN
dhcp/packet_examples/.DS_Store
vendored
Normal file
Binary file not shown.
1
dhcp/packet_examples/cisco_ios/Ack
Normal file
1
dhcp/packet_examples/cisco_ios/Ack
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x02\x01\x06\x01\x00\x00\x02+\x00\x04\x80\x00\x00\x00\x00\x00\n\x00\x00\x03\xac\x10\x00\xc8\n\x00\x00\x02\xcc\x16~/!Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x056\x04\xac\x10\x00\xc83\x04\x00\x01Q\x80\x01\x04\xff\xff\xff\xfe\x03\x04\n\x00\x00\x02\x96\x04\xac\x10\x00\xc8C\x17/configs/LAB-897VA.conf\xff'
|
1
dhcp/packet_examples/cisco_ios/Discover
Normal file
1
dhcp/packet_examples/cisco_ios/Discover
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x01\x01\x06\x01\x00\x00\x02+\x00\x04\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x02\xcc\x16~/!Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x019\x02\x04\xb0=\x19\x00cisco-cc16.7e2f.215a-Gi8\x0c\x06Router7\x0b\x01B\x06\x0f,\x03C\x0c!\x96+<\x08ciscopnp\xff'
|
1
dhcp/packet_examples/cisco_ios/Offer
Normal file
1
dhcp/packet_examples/cisco_ios/Offer
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x02\x01\x06\x01\x00\x00\x02+\x00\x04\x80\x00\x00\x00\x00\x00\n\x00\x00\x03\xac\x10\x00\xc8\n\x00\x00\x02\xcc\x16~/!Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x026\x04\xac\x10\x00\xc83\x04\x00\x01Q\x80\x01\x04\xff\xff\xff\xfe\x03\x04\n\x00\x00\x02\x96\x04\xac\x10\x00\xc8C\x17/configs/LAB-897VA.conf\xff'
|
1
dhcp/packet_examples/cisco_ios/Release
Normal file
1
dhcp/packet_examples/cisco_ios/Release
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x01\x01\x06\x00\x00\x00\x02+\x00\x00\x00\x00\n\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xcc\x16~/!Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x076\x04\xac\x10\x00\xc8=\x19\x00cisco-cc16.7e2f.215a-Gi8<\x08ciscopnp\xff'
|
1
dhcp/packet_examples/cisco_ios/Request
Normal file
1
dhcp/packet_examples/cisco_ios/Request
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x01\x01\x06\x01\x00\x00\x02+\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x02\xcc\x16~/!Z\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x039\x02\x04\xb0=\x19\x00cisco-cc16.7e2f.215a-Gi86\x04\xac\x10\x00\xc82\x04\n\x00\x00\x03\x0c\x06Router7\x0b\x01B\x06\x0f,\x03C\x0c!\x96+<\x08ciscopnp\xff'
|
1
dhcp/packet_examples/junos/Ack
Normal file
1
dhcp/packet_examples/junos/Ack
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x02\x01\x06\x01O\x00\xe0\xa1\x00\x00\x80\x00\x00\x00\x00\x00\n\x00\x00\x01\xac\x10\x00\xc8\n\x00\x00\x00\x94\xbf\x94\xb3n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x056\x04\xac\x10\x00\xc83\x04\x00\x01Q\x80\x01\x04\xff\xff\xff\xfe\x03\x04\n\x00\x00\x00\x96\x04\xac\x10\x00\xc8+ \x01\x18/configs/LAB-SRX300.conf\x03\x04tftp\xff'
|
1
dhcp/packet_examples/junos/Discover
Normal file
1
dhcp/packet_examples/junos/Discover
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x01\x01\x06\x01O\x00\xe0\xa1\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x94\xbf\x94\xb3n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x013\x04\x00\x01Q\x80\x0c\nLAB-SRX300\xff\x00'
|
1
dhcp/packet_examples/junos/Offer
Normal file
1
dhcp/packet_examples/junos/Offer
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x02\x01\x06\x01O\x00\xe0\xa1\x00\x00\x80\x00\x00\x00\x00\x00\n\x00\x00\x01\xac\x10\x00\xc8\n\x00\x00\x00\x94\xbf\x94\xb3n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc5\x01\x026\x04\xac\x10\x00\xc83\x04\x00\x01Q\x80\x01\x04\xff\xff\xff\xfe\x03\x04\n\x00\x00\x00\x96\x04\xac\x10\x00\xc8+ \x01\x18/configs/LAB-SRX300.conf\x03\x04tftp\xff'
|
1
dhcp/packet_examples/junos/Release
Normal file
1
dhcp/packet_examples/junos/Release
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x01\x01\x06\x00;\x1b\x1f\xf6\x00\x00\x00\x00\n\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\xbf\x94\xb3n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc6\x04\xac\x10\x00\xc85\x01\x07\x0c\nLAB-SRX300\xff\x00'
|
1
dhcp/packet_examples/junos/Request
Normal file
1
dhcp/packet_examples/junos/Request
Normal file
@ -0,0 +1 @@
|
|||||||
|
b'\x01\x01\x06\x01O\x00\xe0\xa1\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x94\xbf\x94\xb3n\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00c\x82Sc6\x04\xac\x10\x00\xc87\x0e\x033\x01\x0f\x06BCx,+\x96\x0c\x07*2\x04\n\x00\x00\x015\x01\x033\x04\x00\x01Q\x80\x0c\nLAB-SRX300\xff\x00'
|
2
dhcp/settings.yaml
Normal file
2
dhcp/settings.yaml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
DHCP_SERVER_IP: 172.16.0.200
|
||||||
|
TFTP_SERVER_IP: 172.16.0.200
|
BIN
logs/Cisco and Juniper.log
Normal file
BIN
logs/Cisco and Juniper.log
Normal file
Binary file not shown.
BIN
logs/Cisco.log
Normal file
BIN
logs/Cisco.log
Normal file
Binary file not shown.
57
logs/Juniper.log
Normal file
57
logs/Juniper.log
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
2023-04-07 20:17:16,743 [INFO] 👾 | Building topology...
|
||||||
|
2023-04-07 20:17:16,793 [INFO] 👾 | Topology name: Lab
|
||||||
|
2023-04-07 20:17:16,796 [INFO] 👾 | Drawing topology...
|
||||||
|
2023-04-07 20:17:17,538 [INFO] 👾 | Topology drawn, saved as: topology/topology.png
|
||||||
|
2023-04-07 20:17:17,540 [INFO] 👾 | Topology build complete
|
||||||
|
2023-04-07 20:17:17,543 [INFO] 👽 | Rendering configs...
|
||||||
|
2023-04-07 20:17:17,575 [INFO] 👽 | Rendered /srv/tftp/configs/LAB-897VA.conf
|
||||||
|
2023-04-07 20:17:17,597 [INFO] 👽 | Rendered /srv/tftp/configs/LAB-SRX300.conf
|
||||||
|
2023-04-07 20:17:17,599 [INFO] 👽 | Config rendering complete
|
||||||
|
2023-04-07 20:17:17,602 [INFO] 🤖 | DHCP server is starting...
|
||||||
|
2023-04-07 20:17:17,604 [INFO] 🤖 | Waiting for DHCP packets...
|
||||||
|
2023-04-07 20:19:23,166 [INFO] 🤖 | DHCP packet received from IP 10.0.0.0 on port 67
|
||||||
|
2023-04-07 20:19:23,169 [INFO] 🤖 | Received packet options:
|
||||||
|
2023-04-07 20:19:23,172 [INFO] 🤖 | 53 [DHCP Message Type]: 1
|
||||||
|
2023-04-07 20:19:23,175 [INFO] 🤖 | 51 [IP Address Lease Time]: 86400
|
||||||
|
2023-04-07 20:19:23,178 [INFO] 🤖 | 12 [Hostname]: LAB-SRX300
|
||||||
|
2023-04-07 20:19:23,180 [INFO] 🤖 | Received DHCP discover packet
|
||||||
|
2023-04-07 20:19:23,183 [INFO] 👾 | Saw client: LAB-SRX300
|
||||||
|
2023-04-07 20:19:23,186 [INFO] 👾 | OS: junos
|
||||||
|
2023-04-07 20:19:23,188 [INFO] 🤖 | Constructing DHCP offer packet...
|
||||||
|
2023-04-07 20:19:23,191 [INFO] 🤖 | Offer packet options:
|
||||||
|
2023-04-07 20:19:23,193 [INFO] 🤖 | 53 [DHCP Message Type]: 2
|
||||||
|
2023-04-07 20:19:23,196 [INFO] 🤖 | 54 [DHCP Server IP Address]: 172.16.0.200
|
||||||
|
2023-04-07 20:19:23,198 [INFO] 🤖 | 51 [IP Address Lease Time]: 86400
|
||||||
|
2023-04-07 20:19:23,200 [INFO] 🤖 | 1 [Subnet Mask]: 255.255.255.254
|
||||||
|
2023-04-07 20:19:23,202 [INFO] 🤖 | 3 [Default Gateway]: 10.0.0.0
|
||||||
|
2023-04-07 20:19:23,205 [INFO] 🤖 | 150 [TFTP Server IP Address]: 172.16.0.200
|
||||||
|
2023-04-07 20:19:23,207 [INFO] 🤖 | 43 [Vendor Specific Information]: /configs/LAB-SRX300.conftftp
|
||||||
|
2023-04-07 20:19:23,209 [INFO] 🤖 | Waiting for DHCP packets...
|
||||||
|
2023-04-07 20:19:23,212 [INFO] 🤖 | DHCP packet received from IP 10.0.0.0 on port 67
|
||||||
|
2023-04-07 20:19:23,215 [INFO] 🤖 | Received packet options:
|
||||||
|
2023-04-07 20:19:23,217 [INFO] 🤖 | 54 [DHCP Server IP Address]: 172.16.0.200
|
||||||
|
2023-04-07 20:19:23,219 [INFO] 🤖 | 55 [Parameter Request List]: [3, 51, 1, 15, 6, 66, 67, 120, 44, 43, 150, 12, 7, 42]
|
||||||
|
2023-04-07 20:19:23,221 [INFO] 🤖 | 50 [Requested IP Address]: 10.0.0.1
|
||||||
|
2023-04-07 20:19:23,223 [INFO] 🤖 | 53 [DHCP Message Type]: 3
|
||||||
|
2023-04-07 20:19:23,225 [INFO] 🤖 | 51 [IP Address Lease Time]: 86400
|
||||||
|
2023-04-07 20:19:23,227 [INFO] 🤖 | 12 [Hostname]: LAB-SRX300
|
||||||
|
2023-04-07 20:19:23,229 [INFO] 🤖 | Received DHCP request packet
|
||||||
|
2023-04-07 20:19:23,231 [INFO] 🤖 | Constructing DHCP ack packet...
|
||||||
|
2023-04-07 20:19:23,233 [INFO] 🤖 | Ack packet options:
|
||||||
|
2023-04-07 20:19:23,235 [INFO] 🤖 | 53 [DHCP Message Type]: 5
|
||||||
|
2023-04-07 20:19:23,237 [INFO] 🤖 | 54 [DHCP Server IP Address]: 172.16.0.200
|
||||||
|
2023-04-07 20:19:23,239 [INFO] 🤖 | 51 [IP Address Lease Time]: 86400
|
||||||
|
2023-04-07 20:19:23,241 [INFO] 🤖 | 1 [Subnet Mask]: 255.255.255.254
|
||||||
|
2023-04-07 20:19:23,243 [INFO] 🤖 | 3 [Default Gateway]: 10.0.0.0
|
||||||
|
2023-04-07 20:19:23,245 [INFO] 🤖 | 150 [TFTP Server IP Address]: 172.16.0.200
|
||||||
|
2023-04-07 20:19:23,247 [INFO] 🤖 | 43 [Vendor Specific Information]: /configs/LAB-SRX300.conftftp
|
||||||
|
2023-04-07 20:19:23,249 [INFO] 🤖 | Waiting for DHCP packets...
|
||||||
|
2023-04-07 20:22:32,406 [INFO] 🤖 | DHCP packet received from IP 10.0.0.1 on port 68
|
||||||
|
2023-04-07 20:22:32,410 [INFO] 🤖 | Received packet options:
|
||||||
|
2023-04-07 20:22:32,413 [INFO] 🤖 | 54 [DHCP Server IP Address]: 172.16.0.200
|
||||||
|
2023-04-07 20:22:32,417 [INFO] 🤖 | 53 [DHCP Message Type]: 7
|
||||||
|
2023-04-07 20:22:32,419 [INFO] 🤖 | 12 [Hostname]: LAB-SRX300
|
||||||
|
2023-04-07 20:22:32,421 [INFO] 🤖 | Received DHCP release packet
|
||||||
|
2023-04-07 20:22:32,422 [INFO] 🤖 | See you around, partner🤠
|
||||||
|
2023-04-07 20:22:32,424 [INFO] 🤖 | Waiting for DHCP packets...
|
||||||
|
2023-04-07 20:23:00,802 [INFO] 🤖 | Exiting...
|
11
logs/midas_2023-04-08_14:16:10.log
Normal file
11
logs/midas_2023-04-08_14:16:10.log
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
2023-04-08 14:16:10,531 [INFO] 👾 | Building topology...
|
||||||
|
2023-04-08 14:16:10,581 [INFO] 👾 | Topology name: Lab
|
||||||
|
2023-04-08 14:16:10,584 [INFO] 👾 | Drawing topology...
|
||||||
|
2023-04-08 14:16:11,510 [INFO] 👾 | Topology drawn, saved as: topology/topology.png
|
||||||
|
2023-04-08 14:16:11,512 [INFO] 👾 | Topology build complete
|
||||||
|
2023-04-08 14:16:11,515 [INFO] 👽 | Rendering configs...
|
||||||
|
2023-04-08 14:16:11,549 [INFO] 👽 | Rendered /srv/tftp/configs/LAB-897VA.conf
|
||||||
|
2023-04-08 14:16:11,573 [INFO] 👽 | Rendered /srv/tftp/configs/LAB-SRX300.conf
|
||||||
|
2023-04-08 14:16:11,575 [INFO] 👽 | Config rendering complete
|
||||||
|
2023-04-08 14:16:11,580 [INFO] 🤖 | DHCP server is starting...
|
||||||
|
2023-04-08 14:16:11,582 [INFO] 🤖 | Waiting for DHCP packets...
|
25
midas
Executable file
25
midas
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from utils.log import get_loggers
|
||||||
|
from topology.midast import Topology
|
||||||
|
from provisioning.midasp import Provisioning
|
||||||
|
from dhcp.midasd import DHCPServer
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
get_loggers()
|
||||||
|
topology = Topology()
|
||||||
|
topology.build()
|
||||||
|
provisioning = Provisioning()
|
||||||
|
provisioning.render(topology)
|
||||||
|
dhcp_server = DHCPServer()
|
||||||
|
dhcp_server.run(topology)
|
||||||
|
|
||||||
|
# Todo
|
||||||
|
# 1. Implement giaddr inspection as backup to packet source address, current using socket source IP instead
|
||||||
|
# Not needed? Adds further complexity and lines of code, socket already reports source IP address
|
||||||
|
# 2. Add fingerprints / persistent lease storage system (ensure that duplicate packets are only printed once, i.e. Cisco sends multiple release packets)
|
||||||
|
# 3. Work out what variables can be made more dynamic, i.e. reply packet options - build from a YAML file?
|
||||||
|
# 4. Make IP allocation & config var (YAML) generation automatic
|
||||||
|
# 5. Implement socket.inet_aton() function
|
||||||
|
# 6. PCAP from PC to capture DHCP option values that are not yet printed in a human readable format
|
||||||
|
# 7. Mount lab Pi as NFS share on valykrie Pi to create automatic backups with duplicati
|
BIN
provisioning/.DS_Store
vendored
Normal file
BIN
provisioning/.DS_Store
vendored
Normal file
Binary file not shown.
35
provisioning/midasp.py
Normal file
35
provisioning/midasp.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import yaml
|
||||||
|
|
||||||
|
from jinja2 import Environment, FileSystemLoader
|
||||||
|
from utils.log import log
|
||||||
|
|
||||||
|
class Provisioning():
|
||||||
|
def render(self, topology):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Builds a configuration file for all network devices in the topology.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
topology: networkx Graph object detailing the network topology
|
||||||
|
'''
|
||||||
|
log('Rendering configs...', 'info')
|
||||||
|
for node in topology.G.nodes:
|
||||||
|
if topology.G.nodes[node].get('no_render') == True:
|
||||||
|
continue
|
||||||
|
|
||||||
|
node_os = topology.G.nodes[node]['os']
|
||||||
|
|
||||||
|
template_path = Environment(loader=FileSystemLoader('provisioning/templates/'), trim_blocks=True, lstrip_blocks=True)
|
||||||
|
device_vars = yaml.load(open('provisioning/vars/' + node + '.yaml'), Loader=yaml.SafeLoader)
|
||||||
|
|
||||||
|
template = template_path.get_template(node_os + '.j2')
|
||||||
|
rendered_config = template.render(device_vars)
|
||||||
|
|
||||||
|
config_path = '/srv/tftp/configs/' + node + '.conf'
|
||||||
|
|
||||||
|
with open(config_path, 'w') as configuration:
|
||||||
|
configuration.write(rendered_config)
|
||||||
|
|
||||||
|
log(f'Rendered {config_path}', 'info')
|
||||||
|
|
||||||
|
log('Config rendering complete', 'info')
|
199
provisioning/templates/cisco_ios.j2
Normal file
199
provisioning/templates/cisco_ios.j2
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
service timestamps debug datetime msec
|
||||||
|
service timestamps log datetime msec
|
||||||
|
no service password-encryption
|
||||||
|
no service password-recovery
|
||||||
|
!
|
||||||
|
hostname {{ hostname }}
|
||||||
|
!
|
||||||
|
boot-start-marker
|
||||||
|
boot-end-marker
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
no aaa new-model
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
no ip domain lookup
|
||||||
|
ip domain name yourdomain.com
|
||||||
|
ip cef
|
||||||
|
no ipv6 cef
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
multilink bundle-name authenticated
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
cts logging verbose
|
||||||
|
license udi pid C897VA-K9 sn FCZ202990B6
|
||||||
|
!
|
||||||
|
!
|
||||||
|
username neteng privilege 15 secret Juniper1
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
controller VDSL 0
|
||||||
|
shutdown
|
||||||
|
lldp run
|
||||||
|
!
|
||||||
|
ip ssh version 2
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
interface Loopback0
|
||||||
|
ip address {{ loopback_ip }} 255.255.255.255
|
||||||
|
!
|
||||||
|
interface ATM0
|
||||||
|
no ip address
|
||||||
|
shutdown
|
||||||
|
no atm ilmi-keepalive
|
||||||
|
!
|
||||||
|
interface BRI0
|
||||||
|
no ip address
|
||||||
|
encapsulation hdlc
|
||||||
|
shutdown
|
||||||
|
isdn termination multidrop
|
||||||
|
!
|
||||||
|
interface Ethernet0
|
||||||
|
no ip address
|
||||||
|
shutdown
|
||||||
|
!
|
||||||
|
interface GigabitEthernet0
|
||||||
|
description "Management Network"
|
||||||
|
switchport access vlan 10
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet1
|
||||||
|
description "{{ hostname }} gigabitEthernet1 <--> ge-0/0/1 LAB-SRX300"
|
||||||
|
switchport access vlan 20
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet2
|
||||||
|
description "{{ hostname }} gigabitEthernet2 <--> ge-0/0/2 LAB-SRX300"
|
||||||
|
switchport access vlan 30
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet3
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet4
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet5
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet6
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet7
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface GigabitEthernet8
|
||||||
|
description "{{ hostname }} gigabitEthernet2 <--> gigabitEthernet1 LAB-RELAY"
|
||||||
|
ip address 10.0.0.3 255.255.255.254
|
||||||
|
duplex auto
|
||||||
|
speed auto
|
||||||
|
no shut
|
||||||
|
!
|
||||||
|
interface Vlan1
|
||||||
|
no ip address
|
||||||
|
!
|
||||||
|
interface Vlan10
|
||||||
|
ip address {{ management_ip }} 255.255.255.0
|
||||||
|
!
|
||||||
|
interface Vlan20
|
||||||
|
ip address 10.0.0.5 255.255.255.254
|
||||||
|
ip ospf authentication message-digest
|
||||||
|
ip ospf message-digest-key 1 md5 Juniper1
|
||||||
|
ip ospf network point-to-point
|
||||||
|
ip ospf 1 area 0
|
||||||
|
!
|
||||||
|
interface Vlan30
|
||||||
|
ip address 10.0.0.7 255.255.255.254
|
||||||
|
ip ospf authentication message-digest
|
||||||
|
ip ospf message-digest-key 1 md5 Juniper1
|
||||||
|
ip ospf network point-to-point
|
||||||
|
ip ospf 1 area 0
|
||||||
|
!
|
||||||
|
router ospf 1
|
||||||
|
router-id {{ loopback_ip }}
|
||||||
|
passive-interface Loopback0
|
||||||
|
!
|
||||||
|
ip forward-protocol nd
|
||||||
|
no ip http server
|
||||||
|
no ip http secure-server
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
control-plane
|
||||||
|
!
|
||||||
|
!
|
||||||
|
mgcp behavior rsip-range tgcp-only
|
||||||
|
mgcp behavior comedia-role none
|
||||||
|
mgcp behavior comedia-check-media-src disable
|
||||||
|
mgcp behavior comedia-sdp-force disable
|
||||||
|
!
|
||||||
|
mgcp profile default
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
line con 0
|
||||||
|
login local
|
||||||
|
no modem enable
|
||||||
|
line aux 0
|
||||||
|
line vty 0 4
|
||||||
|
login local
|
||||||
|
transport input ssh telnet
|
||||||
|
line vty 5 15
|
||||||
|
login local
|
||||||
|
transport input ssh telnet
|
||||||
|
!
|
||||||
|
scheduler allocate 20000 1000
|
||||||
|
!
|
||||||
|
!
|
||||||
|
!
|
||||||
|
event manager applet crypto-key
|
||||||
|
event timer cron cron-entry "@reboot"
|
||||||
|
action 1.0 cli command "enable"
|
||||||
|
action 1.1 cli command "config t"
|
||||||
|
action 1.2 cli command "file prompt quiet"
|
||||||
|
action 1.3 cli command "crypto key generate rsa modulus 2048"
|
||||||
|
action 1.4 cli command "no event manager applet crypto-key"
|
||||||
|
action 1.5 cli command "do wr mem"
|
||||||
|
!
|
||||||
|
end
|
280
provisioning/templates/junos.j2
Normal file
280
provisioning/templates/junos.j2
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
system {
|
||||||
|
host-name {{ hostname }};
|
||||||
|
root-authentication {
|
||||||
|
plain-text-password-value "Juniper1";
|
||||||
|
}
|
||||||
|
login {
|
||||||
|
user datatech {
|
||||||
|
uid 2001;
|
||||||
|
class read-only;
|
||||||
|
authentication {
|
||||||
|
plain-text-password-value "Juniper1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user neteng {
|
||||||
|
uid 2000;
|
||||||
|
class super-user;
|
||||||
|
authentication {
|
||||||
|
plain-text-password-value "Juniper1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
services {
|
||||||
|
ssh {
|
||||||
|
root-login allow;
|
||||||
|
}
|
||||||
|
netconf {
|
||||||
|
ssh;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tacplus-server {
|
||||||
|
10.10.10.10 secret "Juniper1"; ## SECRET-DATA
|
||||||
|
11.11.11.11 secret "Juniper1"; ## SECRET-DATA
|
||||||
|
}
|
||||||
|
syslog {
|
||||||
|
archive size 100k files 3;
|
||||||
|
user * {
|
||||||
|
any emergency;
|
||||||
|
}
|
||||||
|
file messages {
|
||||||
|
any notice;
|
||||||
|
authorization info;
|
||||||
|
}
|
||||||
|
file interactive-commands {
|
||||||
|
interactive-commands any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
security {
|
||||||
|
authentication-key-chains {
|
||||||
|
key-chain BGP-KC-LHR14-R101-NCL62-R2 {
|
||||||
|
key 1 {
|
||||||
|
secret "$9$Vyws4JZjq.57-YoZU.m"; ## SECRET-DATA
|
||||||
|
start-time "2022-7-1.00:00:00 +0000";
|
||||||
|
}
|
||||||
|
key 2 {
|
||||||
|
secret "$9$0Yfd1clVbs2oJdV69pOSybs2aUj5TF9AuiHP5FnpuNdVbs24aZ"; ## SECRET-DATA
|
||||||
|
start-time "2023-4-3.15:13:45 +0000";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key-chain BGP-KC-NCL62-R2-SC-FW2 {
|
||||||
|
key 1 {
|
||||||
|
secret "$9$8x0Xxdws4ZGiKM7Vs2GU"; ## SECRET-DATA
|
||||||
|
start-time "2022-7-1.00:00:00 +0000";
|
||||||
|
}
|
||||||
|
key 2 {
|
||||||
|
secret "$9$B74EeW24JikmlKDH.m3n1RhyvWxNV24JikSreKLX7-VYGjzF6"; ## SECRET-DATA
|
||||||
|
start-time "2023-4-3.15:42:04 +0000";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
key-chain BRMA-KC-LHR30-R101-NCL60-R1 {
|
||||||
|
key 1 {
|
||||||
|
apply-flags omit;
|
||||||
|
secret "$9$1HCREyeK87NbuOhrKMN-"; ## SECRET-DATA
|
||||||
|
key-name 4953bd1120ffcc31e1d044870c52d67b215c04f0c2ba1fccc970fa16d18a6b6f;
|
||||||
|
start-time "2023-3-31.14:22:24 +0000";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
forwarding-options {
|
||||||
|
family {
|
||||||
|
mpls {
|
||||||
|
mode packet-based;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
macsec {
|
||||||
|
connectivity-association BRMA-WAN-LHR30-R101-NCL60-R1 {
|
||||||
|
security-mode static-cak;
|
||||||
|
mka {
|
||||||
|
transmit-interval 6000;
|
||||||
|
sak-rekey-interval 60;
|
||||||
|
}
|
||||||
|
pre-shared-key-chain BRMA-KC-LHR30-R101-NCL60-R1;
|
||||||
|
}
|
||||||
|
interfaces {
|
||||||
|
ge-0/0/7 {
|
||||||
|
connectivity-association BRMA-WAN-LHR30-R101-NCL60-R1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interfaces {
|
||||||
|
ge-0/0/0 {
|
||||||
|
description "{{ hostname }} ge-0/0/0 <--> gigabitEthernet0 LAB-RELAY";
|
||||||
|
unit 0 {
|
||||||
|
family inet {
|
||||||
|
address 10.0.0.1/31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ge-0/0/1 {
|
||||||
|
description "{{ hostname }} ge-0/0/1 <--> gigabitEthernet1 LAB-897VA";
|
||||||
|
unit 0 {
|
||||||
|
family inet {
|
||||||
|
address 10.0.0.4/31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ge-0/0/2 {
|
||||||
|
description "{{ hostname }} ge-0/0/2 <--> gigabitEthernet2 LAB-897VA";
|
||||||
|
unit 0 {
|
||||||
|
family inet {
|
||||||
|
address 10.0.0.6/31;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ge-0/0/5 {
|
||||||
|
description "Management Network";
|
||||||
|
unit 0 {
|
||||||
|
family inet {
|
||||||
|
address {{ management_ip }}/24;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lo0 {
|
||||||
|
unit 0 {
|
||||||
|
family inet {
|
||||||
|
address {{ loopback_ip }}/32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
snmp {
|
||||||
|
v3 {
|
||||||
|
usm {
|
||||||
|
local-engine {
|
||||||
|
user snmp-user {
|
||||||
|
authentication-sha {
|
||||||
|
authentication-password "Juniper1"; ## SECRET-DATA
|
||||||
|
}
|
||||||
|
privacy-aes128 {
|
||||||
|
privacy-password "Juniper1"; ## SECRET-DATA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
policy-options {
|
||||||
|
policy-statement BN-10-RMA-EXPORT {
|
||||||
|
term BGP {
|
||||||
|
from protocol bgp;
|
||||||
|
then {
|
||||||
|
community add BN-10-RMA-TARGET;
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
term OSPF {
|
||||||
|
from protocol ospf;
|
||||||
|
then {
|
||||||
|
community add BN-10-RMA-TARGET;
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
term AGGREGATE {
|
||||||
|
from protocol aggregate;
|
||||||
|
then {
|
||||||
|
community add BN-10-RMA-TARGET;
|
||||||
|
accept;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
term REJECT-ALL {
|
||||||
|
then reject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
policy-statement BN-10-RMA-IMPORT {
|
||||||
|
term BGP {
|
||||||
|
from {
|
||||||
|
protocol bgp;
|
||||||
|
community BN-10-RMA-TARGET;
|
||||||
|
}
|
||||||
|
then accept;
|
||||||
|
}
|
||||||
|
term REJECT-ALL {
|
||||||
|
then reject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
community BN-10-RMA-TARGET members target:65100:1000;
|
||||||
|
}
|
||||||
|
access {
|
||||||
|
radius-server {
|
||||||
|
10.10.10.10 secret "Juniper1"; ## SECRET-DATA
|
||||||
|
11.11.11.11 secret "Juniper1"; ## SECRET-DATA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routing-instances {
|
||||||
|
BN-10-RMA {
|
||||||
|
protocols {
|
||||||
|
bgp {
|
||||||
|
group SEC-NET-FW {
|
||||||
|
type external;
|
||||||
|
description "eBGP to Security Network Firewall";
|
||||||
|
local-address 192.168.32.78;
|
||||||
|
hold-time 30;
|
||||||
|
peer-as 64900;
|
||||||
|
neighbor 192.168.32.79 {
|
||||||
|
description ncl62-sc-fw2;
|
||||||
|
authentication-key-chain BGP-KC-NCL62-R2-SC-FW2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traceoptions {
|
||||||
|
file bgp.log;
|
||||||
|
flag state;
|
||||||
|
}
|
||||||
|
log-updown;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance-type vrf;
|
||||||
|
route-distinguisher 65100:1000;
|
||||||
|
vrf-import BN-10-RMA-IMPORT;
|
||||||
|
vrf-export BN-10-RMA-EXPORT;
|
||||||
|
vrf-table-label;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protocols {
|
||||||
|
ospf {
|
||||||
|
area 0.0.0.0 {
|
||||||
|
interface ge-0/0/1.0 {
|
||||||
|
interface-type p2p;
|
||||||
|
authentication {
|
||||||
|
md5 1 key "Juniper1"; ## SECRET-DATA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface ge-0/0/2.0 {
|
||||||
|
interface-type p2p;
|
||||||
|
authentication {
|
||||||
|
md5 1 key "Juniper1"; ## SECRET-DATA
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interface lo0.0 {
|
||||||
|
passive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bgp {
|
||||||
|
group IBGP-FULL-MESH {
|
||||||
|
type internal;
|
||||||
|
description "IBGP Full Mesh";
|
||||||
|
hold-time 30;
|
||||||
|
multipath {
|
||||||
|
multiple-as;
|
||||||
|
}
|
||||||
|
neighbor 172.17.0.0 {
|
||||||
|
description lhr14-bn-com-agg-r101;
|
||||||
|
authentication-key-chain BGP-KC-LHR14-R101-NCL62-R2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
traceoptions {
|
||||||
|
file bgp.log;
|
||||||
|
flag state;
|
||||||
|
}
|
||||||
|
log-updown;
|
||||||
|
}
|
||||||
|
lldp {
|
||||||
|
interface all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
routing-options {
|
||||||
|
autonomous-system 65100;
|
||||||
|
}
|
3
provisioning/vars/LAB-897VA.yaml
Normal file
3
provisioning/vars/LAB-897VA.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
hostname: LAB-897VA
|
||||||
|
management_ip: 172.16.0.3
|
||||||
|
loopback_ip: 2.2.2.2
|
3
provisioning/vars/LAB-SRX300.yaml
Normal file
3
provisioning/vars/LAB-SRX300.yaml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
hostname: LAB-SRX300
|
||||||
|
management_ip: 172.16.0.1
|
||||||
|
loopback_ip: 1.1.1.1
|
87
topology/midast.py
Normal file
87
topology/midast.py
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
import networkx as nx
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
from networkx.drawing.nx_agraph import to_agraph
|
||||||
|
from utils.log import log
|
||||||
|
|
||||||
|
class Topology():
|
||||||
|
def __init__(self):
|
||||||
|
self.G = nx.MultiGraph()
|
||||||
|
|
||||||
|
def build(self):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Builds a network topology from the topology.yaml file into memory, stored in a networkx Graph object.
|
||||||
|
Once the networkx Graph object has been built, it's drawn and saved as topology.png.
|
||||||
|
|
||||||
|
Nodes: network devices
|
||||||
|
Node attributes: dict of attributes assigned to a specific node
|
||||||
|
Edges: links between network devices
|
||||||
|
Edge attributes: dict of attributes assigned to a specific edge
|
||||||
|
'''
|
||||||
|
log('Building topology...', 'info')
|
||||||
|
|
||||||
|
topology_data = yaml.load(open('topology/topology.yaml'), Loader=yaml.SafeLoader)
|
||||||
|
|
||||||
|
for topology_name, topology_vars in topology_data.items():
|
||||||
|
log(f'Topology name: {topology_name}', 'info')
|
||||||
|
|
||||||
|
for node, node_attributes in topology_vars['nodes'].items():
|
||||||
|
self.G.add_node(node)
|
||||||
|
self.G.nodes[node]['shape'] = 'box'
|
||||||
|
for attribute_name, attribute_value in node_attributes.items():
|
||||||
|
self.G.nodes[node][attribute_name] = attribute_value
|
||||||
|
|
||||||
|
for edge_number, edge_attributes in topology_vars['edges'].items():
|
||||||
|
self.G.add_edge(edge_attributes['a_end'], edge_attributes['b_end'])
|
||||||
|
nx.set_edge_attributes(
|
||||||
|
self.G,
|
||||||
|
{
|
||||||
|
(edge_attributes['a_end'],
|
||||||
|
edge_attributes['b_end'],
|
||||||
|
edge_attributes['edge_index']): edge_attributes
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
for node1, node2, edge_attributes in self.G.edges(data=True):
|
||||||
|
edge_attributes['label'] = f"""
|
||||||
|
{edge_attributes['a_end']} ({self.G.nodes[edge_attributes['a_end']]['os']})
|
||||||
|
{edge_attributes['a_end_ip']}, {edge_attributes['a_end_interface']}\n
|
||||||
|
{edge_attributes['b_end']} ({self.G.nodes[edge_attributes['b_end']]['os']})
|
||||||
|
{edge_attributes['b_end_ip']}, {edge_attributes['b_end_interface']}
|
||||||
|
"""
|
||||||
|
|
||||||
|
log('Drawing topology...', 'info')
|
||||||
|
vis = to_agraph(self.G)
|
||||||
|
vis.layout('dot')
|
||||||
|
vis_path = 'topology/topology.png'
|
||||||
|
vis.draw(vis_path)
|
||||||
|
|
||||||
|
log(f'Topology drawn, saved as: {vis_path}', 'info')
|
||||||
|
log('Topology build complete', 'info')
|
||||||
|
|
||||||
|
def get_client_calling_for_ip(self, giaddr, _type):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Gets the hostname and operating system of the client calling for an IP from the networkx Graph object.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
giaddr: Gateway or relay IP that the DHCP packet was received from
|
||||||
|
_type: Type of DHCP packet, if type is offer log the client name and OS
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
client_device_name: hostname of the client device
|
||||||
|
client_device_os: operating system of the client device
|
||||||
|
'''
|
||||||
|
for node1, node2, edge_attributes in self.G.edges(data=True):
|
||||||
|
if giaddr in edge_attributes.values():
|
||||||
|
if giaddr == edge_attributes['a_end_ip']:
|
||||||
|
client_device_name, client_device_os = edge_attributes['b_end'], self.G.nodes[edge_attributes['b_end']]['os']
|
||||||
|
elif giaddr == edge_attributes['b_end_ip']:
|
||||||
|
client_device_name, client_device_os = edge_attributes['a_end'], self.G.nodes[edge_attributes['a_end']]['os']
|
||||||
|
|
||||||
|
if _type == 'offer':
|
||||||
|
log(f'Saw client: {client_device_name}', 'info')
|
||||||
|
log(f'OS: {client_device_os}', 'info')
|
||||||
|
|
||||||
|
return client_device_name, client_device_os
|
BIN
topology/topology.png
Normal file
BIN
topology/topology.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 82 KiB |
54
topology/topology.yaml
Normal file
54
topology/topology.yaml
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
Lab:
|
||||||
|
nodes:
|
||||||
|
PI-DHCP:
|
||||||
|
os: ubuntu
|
||||||
|
no_render: True
|
||||||
|
LAB-RELAY:
|
||||||
|
os: cisco_ios
|
||||||
|
no_render: True
|
||||||
|
LAB-897VA:
|
||||||
|
os: cisco_ios
|
||||||
|
LAB-SRX300:
|
||||||
|
os: junos
|
||||||
|
edges:
|
||||||
|
1:
|
||||||
|
a_end: LAB-RELAY
|
||||||
|
b_end: LAB-SRX300
|
||||||
|
a_end_ip: 10.0.0.0
|
||||||
|
b_end_ip: 10.0.0.1
|
||||||
|
a_end_interface: GigabitEthernet0
|
||||||
|
b_end_interface: ge-0/0/0
|
||||||
|
edge_index: 0
|
||||||
|
2:
|
||||||
|
a_end: LAB-RELAY
|
||||||
|
b_end: LAB-897VA
|
||||||
|
a_end_ip: 10.0.0.2
|
||||||
|
b_end_ip: 10.0.0.3
|
||||||
|
a_end_interface: GigabitEthernet1
|
||||||
|
b_end_interface: GigabitEthernet8
|
||||||
|
edge_index: 0
|
||||||
|
3:
|
||||||
|
a_end: PI-DHCP
|
||||||
|
b_end: LAB-RELAY
|
||||||
|
a_end_ip: 172.16.0.200
|
||||||
|
b_end_ip: 172.16.0.2
|
||||||
|
a_end_interface: eth0
|
||||||
|
b_end_interface: GigabitEthernet8
|
||||||
|
edge_index: 0
|
||||||
|
4:
|
||||||
|
a_end: LAB-897VA
|
||||||
|
b_end: LAB-SRX300
|
||||||
|
a_end_ip: 10.0.0.5
|
||||||
|
b_end_ip: 10.0.0.4
|
||||||
|
a_end_interface: GigabitEthernet1
|
||||||
|
b_end_interface: ge-0/0/1
|
||||||
|
edge_index: 0
|
||||||
|
5:
|
||||||
|
a_end: LAB-897VA
|
||||||
|
b_end: LAB-SRX300
|
||||||
|
a_end_ip: 10.0.0.7
|
||||||
|
b_end_ip: 10.0.0.6
|
||||||
|
a_end_interface: GigabitEthernet2
|
||||||
|
b_end_interface: ge-0/0/2
|
||||||
|
edge_index: 1
|
||||||
|
|
63
utils/log.py
Normal file
63
utils/log.py
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import logging
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import inspect
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def get_loggers():
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Creates 3 loggers with the same datetime; midasd-logger (DHCP), midast-logger (Topology), midasp-logger (Provisioning).
|
||||||
|
Each logger has a different emoji to make log sources easily identifiable.
|
||||||
|
'''
|
||||||
|
now = datetime.now().strftime("%Y-%m-%d_%H:%M:%S")
|
||||||
|
|
||||||
|
for logger_name in ['midasd-logger', 'midast-logger', 'midasp-logger']:
|
||||||
|
logger = logging.getLogger(logger_name)
|
||||||
|
|
||||||
|
logger.setLevel(logging.DEBUG)
|
||||||
|
if logger_name == 'midasd-logger':
|
||||||
|
formatter = logging.Formatter('%(asctime)s [%(levelname)s] \U0001F916 | %(message)s')
|
||||||
|
elif logger_name == 'midast-logger':
|
||||||
|
formatter = logging.Formatter('%(asctime)s [%(levelname)s] \U0001F47E | %(message)s')
|
||||||
|
elif logger_name == 'midasp-logger':
|
||||||
|
formatter = logging.Formatter('%(asctime)s [%(levelname)s] \U0001F47D | %(message)s')
|
||||||
|
|
||||||
|
file_handler = logging.FileHandler(os.getcwd() + '/logs/midas_' + now + '.log')
|
||||||
|
file_handler.setLevel(logging.DEBUG)
|
||||||
|
file_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
stream_handler = logging.StreamHandler(sys.stdout)
|
||||||
|
stream_handler.setFormatter(formatter)
|
||||||
|
|
||||||
|
logger.addHandler(file_handler)
|
||||||
|
logger.addHandler(stream_handler)
|
||||||
|
|
||||||
|
def log(log_message, level):
|
||||||
|
'''
|
||||||
|
Summary:
|
||||||
|
Logs information to logfile at the specified level.
|
||||||
|
|
||||||
|
Takes:
|
||||||
|
log_message: Information to log
|
||||||
|
level: Level of which to log the information at
|
||||||
|
'''
|
||||||
|
caller_filename = inspect.stack()[1].filename
|
||||||
|
|
||||||
|
if 'midasd' in caller_filename:
|
||||||
|
logger = logging.getLogger('midasd-logger')
|
||||||
|
elif 'midast' in caller_filename:
|
||||||
|
logger = logging.getLogger('midast-logger')
|
||||||
|
elif 'midasp' in caller_filename:
|
||||||
|
logger = logging.getLogger('midasp-logger')
|
||||||
|
|
||||||
|
log_message_types = {
|
||||||
|
'debug': logger.debug,
|
||||||
|
'info': logger.info,
|
||||||
|
'warning': logger.warning,
|
||||||
|
'error': logger.error,
|
||||||
|
'critical': logger.critical
|
||||||
|
}
|
||||||
|
|
||||||
|
log_message_types[level](log_message)
|
Loading…
x
Reference in New Issue
Block a user