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…
Reference in New Issue
Block a user