File size: 3,194 Bytes
55e2289
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import ipaddress
from typing import List, Dict, Set
from Decipher.models import NetworkTopology

def validate_topology(topology: NetworkTopology) -> List[str]:
    """
    Performs logical validation on the network topology and returns a list of warnings/errors.
    """
    issues = []
    device_names = {d.name for d in topology.devices}
    ip_to_interface = {} # ip -> (device, interface)

    # 1. Check for IP Conflicts and basic interface sanity
    for device in topology.devices:
        for intf in device.interfaces:
            if intf.ip:
                # Check for duplicate IPs
                if intf.ip in ip_to_interface:
                    other_dev, other_intf = ip_to_interface[intf.ip]
                    issues.append(f"IP Conflict: {intf.ip} is assigned to both {device.name} ({intf.name}) and {other_dev} ({other_intf})")
                else:
                    ip_to_interface[intf.ip] = (device.name, intf.name)
                
                # Basic IP format validation
                try:
                    if "/" in intf.ip:
                        ipaddress.ip_interface(intf.ip)
                    else:
                        ipaddress.ip_address(intf.ip)
                except ValueError:
                    issues.append(f"Invalid IP Format: '{intf.ip}' on {device.name} ({intf.name})")

    # 2. Ensure Link Endpoints Exist
    for link in topology.physical_links:
        if link.from_device not in device_names:
            issues.append(f"Broken Link: Source device '{link.from_device}' does not exist.")
        if link.to_device not in device_names:
            issues.append(f"Broken Link: Destination device '{link.to_device}' does not exist.")

    # 3. Verify Routing Protocols (Basic Sanity)
    for device in topology.devices:
        if device.routing and device.routing.ospf:
            device_ips = [intf.ip for intf in device.interfaces if intf.ip]
            for ospf in device.routing.ospf:
                for net_statement in ospf.networks:
                    # Check if the OSPF network statement matches at least one interface IP segment
                    try:
                        # network statement is usually "network 10.0.0.0 0.0.0.255 area 0"
                        # we extract the network and wildcard mask
                        parts = net_statement.split()
                        if "network" in parts:
                            net_addr = parts[parts.index("network") + 1]
                            # Simple check: does any device IP start with the same prefix?
                            # This is a heuristic check
                            found_match = False
                            for ip in device_ips:
                                if ip.split('.')[0:2] == net_addr.split('.')[0:2]:
                                    found_match = True
                                    break
                            if not found_match:
                                issues.append(f"OSPF Warning: Network statement '{net_statement}' on {device.name} doesn't seem to match any local interface IPs.")
                    except Exception:
                        pass

    return issues