dhcplease 0.2.0

A cross-platform DHCP server library and CLI
Documentation
# DHCP Sequence Diagrams


## Full Lease Lifecycle


```mermaid
sequenceDiagram
    participant Client
    participant Server
    participant Leases

    Note over Client: Needs IP address

    Client->>Server: DISCOVER (broadcast from 0.0.0.0)
    Server->>Leases: allocate_ip(client_id)
    Leases-->>Server: 192.168.1.100 (pending 60s)
    Server->>Client: OFFER (yiaddr=192.168.1.100, T1, T2, options)

    Client->>Server: REQUEST (server_id, requested_ip)
    Server->>Leases: create_lease(client_id, ip)
    Leases-->>Server: lease committed
    Server->>Client: ACK (full configuration)

    Note over Client: Uses network with leased IP

    Note over Client: T1 timer (50%) expires
    Client->>Server: REQUEST (unicast renewal)
    Server->>Leases: renew_lease(client_id)
    Leases-->>Server: lease extended
    Server->>Client: ACK (lease renewed)

    Note over Client: Done with IP

    Client->>Server: RELEASE (ciaddr)
    Server->>Leases: release_lease(client_id)
    Leases-->>Server: IP returned to pool
```

## DISCOVER → OFFER Detail


```mermaid
sequenceDiagram
    participant Client
    participant RateLimiter
    participant Parser
    participant Handler
    participant Leases

    Client->>RateLimiter: UDP packet (port 67)
    RateLimiter->>RateLimiter: check(mac) < 10/sec?
    RateLimiter->>Parser: OK

    Parser->>Parser: DhcpPacket::parse()
    Note over Parser: Validate magic cookie<br/>Extract header fields<br/>Decode options (TLV)

    Parser->>Handler: msg_type = DISCOVER

    Handler->>Leases: allocate_ip(client_id)
    Note over Leases: 1. Check static binding<br/>2. Check existing lease<br/>3. Check pending offer<br/>4. Find first free IP<br/>5. Track pending (60s)
    Leases-->>Handler: 192.168.1.100

    Handler->>Handler: build_offer_options()
    Note over Handler: subnet, gateway, dns,<br/>lease time, T1, T2,<br/>server_id, broadcast

    Handler->>Client: OFFER packet
```

## REQUEST → ACK/NAK


```mermaid
sequenceDiagram
    participant Client
    participant Server
    participant Leases

    Client->>Server: REQUEST (server_id, requested_ip)

    alt Server ID doesn't match us
        Note over Server: Ignore (client chose different server)
    else Server ID matches
        Server->>Server: Validate requested IP

        alt IP not in pool and not static binding
            Server->>Client: NAK
            Note over Client: Must restart DORA
        else IP valid
            Server->>Server: negotiate_lease_time()
            Server->>Leases: create_lease(client_id, ip, duration)

            alt IP already leased to different client
                Leases-->>Server: Error
                Server->>Client: NAK
            else Success
                Leases-->>Server: lease committed
                Server->>Client: ACK (full config)
            end
        end
    end
```

## Relay Agent Flow


```mermaid
sequenceDiagram
    participant Client
    participant Relay
    participant Server

    Client->>Relay: DISCOVER (broadcast)

    Note over Relay: Sets giaddr=192.168.2.1<br/>Adds Option 82 (circuit info)

    Relay->>Server: DISCOVER (unicast to server)

    Note over Server: Sees giaddr is set<br/>Allocates from correct subnet<br/>Will echo Option 82

    Server->>Relay: OFFER (dst=giaddr:67, Option 82 echoed)
    Relay->>Client: OFFER (broadcast/unicast)

    Client->>Relay: REQUEST (broadcast)
    Relay->>Server: REQUEST (unicast, giaddr, Option 82)
    Server->>Relay: ACK (dst=giaddr:67)
    Relay->>Client: ACK
```

## T2 Rebinding (Primary Server Down)


```mermaid
sequenceDiagram
    participant Client
    participant ServerA as Server A (primary)
    participant ServerB as Server B (backup)

    Note over Client: Has lease from Server A

    Note over Client: T1 (50%) - attempt renewal
    Client->>ServerA: REQUEST (unicast)

    Note over ServerA: Server down/unreachable

    Note over Client: No response... timeout

    Note over Client: T2 (87.5%) - attempt rebind
    Client->>ServerA: REQUEST (broadcast)
    Client->>ServerB: REQUEST (broadcast)

    Note over ServerB: Can respond if it<br/>recognizes this client

    ServerB->>Client: ACK (lease renewed)

    Note over Client: Now bound to Server B
```

## DECLINE (IP Conflict)


```mermaid
sequenceDiagram
    participant Client
    participant Server
    participant Leases

    Note over Client: Received ACK for 192.168.1.100

    Client->>Client: ARP probe for 192.168.1.100
    Note over Client: Someone responds!<br/>IP already in use!

    Client->>Server: DECLINE (requested_ip=192.168.1.100)

    Server->>Leases: decline_ip(ip, client_id)
    Note over Leases: 1. Remove client's lease<br/>2. Add IP to declined_ips<br/>3. Quarantine for 1 hour
    Leases-->>Server: done

    Note over Server: No response sent to DECLINE

    Note over Client: Must restart DORA
    Client->>Server: DISCOVER
    Server->>Client: OFFER (different IP)
```

## Response Destination Logic


```mermaid
flowchart TD
    A[Send Reply] --> B{giaddr set?}
    B -->|Yes| C[Send to giaddr:67<br/>Relay agent]
    B -->|No| D{Is NAK?}
    D -->|Yes| E[Broadcast to<br/>255.255.255.255:68]
    D -->|No| F{Broadcast flag set?}
    F -->|Yes| E
    F -->|No| G{ciaddr == 0.0.0.0?}
    G -->|Yes| E
    G -->|No| H[Unicast to<br/>ciaddr:68]
```

## IP Allocation Algorithm


```mermaid
flowchart TD
    A[allocate_ip] --> B{Static binding<br/>for this MAC?}
    B -->|Yes| C[Return static IP]
    B -->|No| D{Existing non-expired<br/>lease?}
    D -->|Yes| E[Return leased IP]
    D -->|No| F{Pending offer<br/>for this client?}
    F -->|Yes| G[Return pending IP]
    F -->|No| H[Cleanup expired leases]
    H --> I[Cleanup expired declined IPs]
    I --> J{Free IP available?}
    J -->|Yes| K[Track as pending offer<br/>60 second hold]
    K --> L[Return IP]
    J -->|No| M[Error: Pool Exhausted]
```