Documentation
¶
Index ¶
- Constants
- func IsDHCPPlugin(driver string) bool
- type CapabilitiesResponse
- type CreateEndpointRequest
- type CreateEndpointResponse
- type CreateNetworkRequest
- type DHCPNetworkOptions
- type DeleteEndpointRequest
- type DeleteNetworkRequest
- type EndpointInterface
- type HealthResponse
- type IPAMData
- type InfoRequest
- type InfoResponse
- type InterfaceName
- type JoinRequest
- type JoinResponse
- type LeaveRequest
- type Options
- type Plugin
- func (p *Plugin) Close() error
- func (p *Plugin) CreateEndpoint(ctx context.Context, r CreateEndpointRequest) (CreateEndpointResponse, error)
- func (p *Plugin) CreateNetwork(r CreateNetworkRequest) error
- func (p *Plugin) DeleteEndpoint(ctx context.Context, r DeleteEndpointRequest) error
- func (p *Plugin) DeleteNetwork(r DeleteNetworkRequest) error
- func (p *Plugin) EndpointOperInfo(ctx context.Context, r InfoRequest) (InfoResponse, error)
- func (p *Plugin) Join(ctx context.Context, r JoinRequest) (JoinResponse, error)
- func (p *Plugin) Leave(ctx context.Context, r LeaveRequest) error
- func (p *Plugin) Listen(bindSock string) error
- type StaticRoute
Constants ¶
const ( RouteTypeNextHop = 0 RouteTypeOnLink = 1 )
libnetwork's route-type encoding for StaticRoute.RouteType. See https://github.com/moby/libnetwork/blob/master/docs/remote.md — 0 ("via gateway") expects a NextHop; 1 ("on-link / connected") has no next hop.
const ( ModeBridge = "bridge" ModeMacvlan = "macvlan" ModeIPvlan = "ipvlan" )
Network attachment modes selected by the `mode` driver option.
const CLIOptionsKey string = "com.docker.network.generic"
CLIOptionsKey is the key used in create network options by the CLI for custom options
const DriverName string = "net-dhcp"
DriverName is the name of the Docker Network Driver
Variables ¶
This section is empty.
Functions ¶
func IsDHCPPlugin ¶
IsDHCPPlugin checks if a Docker network driver is an instance of this plugin
Types ¶
type CapabilitiesResponse ¶
CapabilitiesResponse returns whether or not this network is global or local
type CreateEndpointRequest ¶
type CreateEndpointRequest struct {
NetworkID string
EndpointID string
Interface *EndpointInterface
Options map[string]interface{}
}
CreateEndpointRequest is sent by the daemon when an endpoint should be created
type CreateEndpointResponse ¶
type CreateEndpointResponse struct {
Interface *EndpointInterface
}
CreateEndpointResponse is sent as a response to a CreateEndpointRequest
type CreateNetworkRequest ¶
type CreateNetworkRequest struct {
NetworkID string
Options map[string]interface{}
IPv4Data []*IPAMData
IPv6Data []*IPAMData
}
CreateNetworkRequest is sent by the daemon when a network needs to be created
type DHCPNetworkOptions ¶
type DHCPNetworkOptions struct {
// Mode selects the attachment strategy: "bridge" (default, requires
// `bridge`) or "macvlan" (requires `parent`).
Mode string `mapstructure:"mode"`
Bridge string
Parent string `mapstructure:"parent"`
// Gateway, if set, overrides the default gateway returned by the
// upstream DHCP server. Useful for split-horizon LANs where
// containers should egress via a different router than the one
// the DHCP server advertises (e.g. VPN gateway).
Gateway string
IPv6 bool
LeaseTimeout time.Duration `mapstructure:"lease_timeout"`
IgnoreConflicts bool `mapstructure:"ignore_conflicts"`
SkipRoutes bool `mapstructure:"skip_routes"`
// PropagateDNS, when true, makes the plugin write DHCP option 6
// (v4 DNS server list) or option 23 (v6) into the container's
// /etc/resolv.conf on every bind/renew with a non-empty list.
// Default false to preserve historical behaviour where Docker's
// embedded resolver handled DNS — flipping this on means LAN-DNS
// names suddenly resolve from inside containers.
PropagateDNS bool `mapstructure:"propagate_dns"`
// PropagateMTU, when true, makes the plugin set the container link's
// MTU to DHCP option 26 on every bind/renew with a non-zero value.
// Default false because some networks advertise non-standard MTUs
// for reasons unrelated to host capability (e.g. hand-rolled tunnel
// fragments) and silently re-MTU'ing a container could surprise an
// operator. Opt-in keeps the behaviour change visible.
PropagateMTU bool `mapstructure:"propagate_mtu"`
// ClientID, when non-empty, overrides the derived DHCP option 61
// (Client Identifier) for every endpoint on this network. Bytes go
// on the wire prefixed with type byte 0x00 (RFC 2132 opaque).
//
// Default empty = derive per endpoint: from the MAC in bridge and
// macvlan (unique, and preserved across a restart, so the lease
// survives), from the Docker endpoint ID in ipvlan (whose slaves
// share the parent MAC). See resolveClientID.
//
// Operator caveat: a static ClientID across containers means the
// upstream DHCP server can't differentiate them — each new
// container will appear to be the same logical client and may
// receive the same lease. Typically only useful when paired with
// VendorClass to drive class-based policy that doesn't depend on
// per-client identity.
ClientID string `mapstructure:"client_id"`
// VendorClass, when non-empty, overrides the default DHCP option
// 60 (Vendor Class Identifier) value of "docker-net-dhcp" for
// every endpoint on this network. Lets DHCP servers using
// class-based policy (Cisco / Aruba / etc.) differentiate
// net-dhcp containers from other clients on the same LAN —
// for example to issue a different gateway or option set to
// containers tagged with a known vendor string.
VendorClass string `mapstructure:"vendor_class"`
// ValidateDHCP, when true, makes CreateNetwork run a one-shot
// DHCP probe on the parent NIC before the network is created,
// failing fast with a clear error if no DHCP server answers
// within the budget (see preflightProbeBudget). Catches
// misconfigurations (parent isolated from any DHCP server,
// firewall blocking UDP/67-68, broken VLAN tag) at create time
// rather than the first `docker run` attempt.
//
// macvlan / ipvlan modes only — bridge mode's "parent" is an
// existing Linux bridge, where the probe semantics are different
// and not yet implemented.
//
// The probe runs a full DHCPDISCOVER → REQUEST → ACK cycle
// (dhcpcd has no DISCOVER-only mode), so the upstream
// pool briefly sees one extra lease per `docker network create`
// with this opt-in. The probe MAC is random (locally-administered
// bit set) so it doesn't collide with anything stable upstream;
// the lease times out naturally rather than dragging CreateNetwork
// on a slow release path.
ValidateDHCP bool `mapstructure:"validate_dhcp"`
// RegisterDNS, when true, makes every endpoint on this network send
// the DHCP FQDN option (81 v4 / 39 v6, dhcpcd `fqdn both`) built from
// its resolved hostname, asking the DHCP server to register that name
// in DNS (forward + reverse). Default false: dynamic-DNS registration
// is a network-policy decision, never silent. Best-effort and advisory
// — many consumer routers ignore option 81, so this requests
// registration, it does not guarantee resolution. Reuses the same
// hostname already sent as the option-12 hint (#261).
RegisterDNS bool `mapstructure:"register_dns"`
// AuditLog, when true, appends every lease-lifecycle event on
// this network (bound / renew / release, plus release_failed when
// the DHCPRELEASE didn't complete) to STATE_DIR/leases.jsonl —
// an append-only JSONL audit trail answering "which IP did this
// container hold last Tuesday?" without dnsmasq-log archaeology
// (#109). Rotated at 16 MB or 30 days, whichever first; one
// rotated generation is kept. Default false: the ledger costs a
// disk write per lease event, and container-ID/IP correlation on
// disk is privacy-relevant in some environments — operators opt
// in deliberately. Append failures bump ledger_write_failures on
// /Plugin.Health and never affect lease handling.
AuditLog bool `mapstructure:"audit_log"`
}
DHCPNetworkOptions contains options for the DHCP network driver
type DeleteEndpointRequest ¶
DeleteEndpointRequest is sent by the daemon when an endpoint needs to be removed
type DeleteNetworkRequest ¶
type DeleteNetworkRequest struct {
NetworkID string
}
DeleteNetworkRequest is sent by the daemon when a network needs to be removed
type EndpointInterface ¶
EndpointInterface contains endpoint interface information
type HealthResponse ¶
type HealthResponse struct {
Healthy bool `json:"healthy"`
// InstanceID identifies the plugin process that served this
// response. Every counter below is in-memory and returns to zero
// when the process does, so two reads are only comparable as a
// delta when their InstanceID matches (#405).
//
// uptime_seconds is a weaker version of the same signal: it does
// reset, but a plugin that restarts early in a long window and then
// runs longer than the first reading shows uptime going *up* across
// the pair, and the reset goes unnoticed. Comparing ids has no such
// blind spot.
InstanceID string `json:"instance_id"`
UptimeSeconds float64 `json:"uptime_seconds"`
ActiveEndpoints int `json:"active_endpoints"`
PendingHints int `json:"pending_hints"`
RecoveredOK int32 `json:"recovered_ok"`
// RecoveryFailed counts post-restart recoveries that failed for a
// container that was still running: it has no renewal client and
// will lose its lease at expiry. Healthy-affecting.
//
// Two conditions were folded into this counter historically and are
// now split out, because neither leaves a running container without
// a renewal client and both are routine after a daemon restart:
// RecoveryDeferred (#383) and RecoveryAbortedContainerGone (#376).
RecoveryFailed int32 `json:"recovery_failed"`
// RecoveryDeferred counts the times recovery met a daemon that was
// not serving yet and was retried once the socket came up (#383).
// Docker respawns the plugin during its own startup, so this is the
// expected state at that moment, not a fault — NOT Healthy-affecting.
// A rise paired with recovery_failed means the retry ran out too:
// that pair is the signal that endpoints really are unrecovered.
RecoveryDeferred int32 `json:"recovery_deferred"`
// RecoveryAbortedContainerGone counts recoveries abandoned because
// the container had already exited or been removed (#376). Not
// Healthy-affecting: nothing is running without a renewal client.
// The recovery-side twin of JoinAbortedContainerGone, and normal
// after a daemon restart that outlived some containers.
RecoveryAbortedContainerGone int32 `json:"recovery_aborted_container_gone"`
// JoinStartFailures counts persistent-client Start failures at
// Join time (#317): a running container with no renewal client.
// Healthy-affecting — same operator action as recovery_failed
// (find the cause in the plugin log, restart the container).
JoinStartFailures int32 `json:"join_start_failures"`
// JoinAbortedContainerGone counts attaches abandoned because the
// container exited before the persistent client was up (#373). Not
// Healthy-affecting: there is no running container without a
// renewal client. Worth watching anyway — a rise means containers
// are dying seconds after start.
JoinAbortedContainerGone int32 `json:"join_aborted_container_gone"`
// JoinAttachSlow counts attaches that succeeded only after
// outlasting AwaitTimeout, waiting on a daemon that was busy with
// the container being attached. Not healthy-affecting — these are
// successes — but a rising count is the visible form of #406.
JoinAttachSlow int32 `json:"join_attach_slow"`
// RestartLinkUpWaited counts child links brought up only after
// waiting out the departing link's hold on the address (#408). Not
// healthy-affecting: this is the fix working, and it is counted so
// the window is visible rather than inferred — the same reason
// JoinAttachSlow exists.
RestartLinkUpWaited int32 `json:"restart_link_up_waited"`
// RestartLinkUpTimeouts counts that wait outlasting its budget. The
// restart then fails with `address already in use`. Not
// healthy-affecting despite being a real failure: it surfaces
// through CreateEndpoint to the operator directly, and `healthy`
// is for faults nothing else reports (#422).
RestartLinkUpTimeouts int32 `json:"restart_link_up_timeouts"`
// JoinAbortedEndpointLeft counts attaches cancelled because the
// endpoint left while the attach was still running. Not
// healthy-affecting: there is no running container missing a
// renewal client.
JoinAbortedEndpointLeft int32 `json:"join_aborted_endpoint_left"`
TombstoneWriteFailures int32 `json:"tombstone_write_failures"`
// TombstonesConsumed counts CreateEndpoints that replayed a fresh
// tombstone and so handed a recreated container its previous
// MAC/IP. Not Healthy-affecting: this is the address-stability
// mechanism working.
//
// It is the counterpart to RecoveredOK. Between them they say which
// of the two paths preserved an address across a restart, which is
// what makes "the address survived, but via neither path" a
// detectable state rather than a silent pass (#386).
TombstonesConsumed int32 `json:"tombstones_consumed"`
// LeaseChanged counts renewals where dhcpcd returned a different
// IP than the manager last recorded. Not Healthy-affecting (it
// doesn't break Docker's view fatally — see plugin.go for the
// truthfulness-gap discussion), but worth alerting on for
// long-running containers.
LeaseChanged int32 `json:"lease_changed"`
// DHCP-wire counters (T2-4). Naming intentionally drops the
// Prometheus `_total` suffix to stay consistent with the
// existing fields above; the issue's proposal listed them with
// `_total` for documentation clarity but the wire field is the
// shorter form.
LeasesObtained int32 `json:"leases_obtained"`
LeasesRenewed int32 `json:"leases_renewed"`
DHCPTimeouts int32 `json:"dhcp_timeouts"`
LeaseReleaseFailures int32 `json:"lease_release_failures"`
// NAKsReceived counts server NAKs on renewal/rebind. Not
// Healthy-affecting on its own — dhcpcd recovers by
// re-DISCOVERing — but each NAK-triggered re-bind widens the
// docker-inspect divergence tracked by lease_changed (#128).
NAKsReceived int32 `json:"naks_received"`
// DisplacedStops counts managers displaced at Join — a Join that
// found a recovery-registered manager still in the registry for
// the same endpoint (plugin restart racing a container restart).
// Not Healthy-affecting: the displaced client is stopped and
// released, and the new one takes over. A climbing value means
// containers are restarting into a plugin that had recovered them,
// so pair it with recovered_ok when diagnosing a restart loop.
DisplacedStops int32 `json:"displaced_stops"`
// OrphanedLeasesReleased / OrphanedLeaseReleaseFailures cover the
// lease acquired by the CreateEndpoint one-shot when no persistent
// client ever took ownership of it, because the container exited
// before Join's async Start could attach (#370). The plugin
// synthesises a release rather than leaving the address held until
// its own expiry.
//
// Neither is Healthy-affecting. A short-lived container is an
// ordinary lifecycle, and a failed synthesised release costs one
// lease until it expires — alert on the failure rate, not on a
// latched unhealthy. Read the two together: releases climbing with
// failures flat is the mechanism working.
OrphanedLeasesReleased int32 `json:"orphaned_leases_released"`
OrphanedLeaseReleaseFailures int32 `json:"orphaned_lease_release_failures"`
// LedgerWriteFailures counts failed appends to the audit_log
// lease ledger (#109). Not Healthy-affecting — a lost audit line
// degrades forensics, not networking; operators using audit_log
// alert on this directly.
LedgerWriteFailures int32 `json:"ledger_write_failures"`
// Per-family (IPv6) breakdown of the wire counters (#212). Each
// counts only the v6 client's events; the un-suffixed fields above
// remain v4+v6 aggregates, so the v4 share is the aggregate minus
// the matching *_v6 value. On a dual-stack host this isolates the
// v6-specific failure signal (NAK/timeout) the aggregate hides.
LeaseChangedV6 int32 `json:"lease_changed_v6"`
LeasesObtainedV6 int32 `json:"leases_obtained_v6"`
LeasesRenewedV6 int32 `json:"leases_renewed_v6"`
DHCPTimeoutsV6 int32 `json:"dhcp_timeouts_v6"`
NAKsReceivedV6 int32 `json:"naks_received_v6"`
}
HealthResponse is the payload returned by /Plugin.Health.
Healthy is false when at least one plugin-restart recovery failed — the plugin keeps serving requests for fresh attaches, but containers that were running before the restart and got a recovery failure are now running without lease renewal and will lose their IP at lease expiry. Operators should restart those containers (which produces a fresh CreateEndpoint and gets them back into the persistent map).
type IPAMData ¶
type IPAMData struct {
AddressSpace string
Pool string
Gateway string
AuxAddresses map[string]interface{}
}
IPAMData contains IPv4 or IPv6 addressing information
type InfoRequest ¶
InfoRequest is sent by the daemon when querying endpoint information
type InfoResponse ¶
InfoResponse is endpoint information sent in response to an InfoRequest
type InterfaceName ¶
InterfaceName consists of the name of the interface in the global netns and the desired prefix to be appended to the interface inside the container netns.
DstName, when non-empty, asks libnetwork for that exact name inside the container instead of DstPrefix+index. The remote-driver API has carried the field for years, but as of moby master the remote proxy drops it (drivers/remote/driver.go calls `iface.SetNames(SrcName, DstPrefix, "")`), so engines do not yet apply it for plugin drivers — built-in drivers got per-driver interface_name support in engine 28, remote drivers were left out. We return it anyway: it is the documented response shape, costs nothing on engines that ignore it, and activates the moment the upstream pass-through lands (#125).
type JoinRequest ¶
type JoinRequest struct {
NetworkID string
EndpointID string
SandboxKey string
Options map[string]interface{}
}
JoinRequest is sent by the Daemon when an endpoint needs be joined to a network
type JoinResponse ¶
type JoinResponse struct {
InterfaceName InterfaceName
Gateway string
GatewayIPv6 string
StaticRoutes []*StaticRoute
DisableGatewayService bool
}
JoinResponse is sent in response to a JoinRequest
type LeaveRequest ¶
LeaveRequest is sent by the daemon when a endpoint is leaving a network
type Options ¶
type Options struct {
// AwaitTimeout caps the polling helpers (sandbox readiness, link
// rename, netns appearance). AWAIT_TIMEOUT, default 10s.
AwaitTimeout time.Duration
// OutageTick is how often the DHCP-outage watchdog re-checks, and
// so the resolution of dhcp_timeouts. OUTAGE_TICK, default 30s.
OutageTick time.Duration
// OutageGrace is the settling time before the watchdog will call an
// outage. It must stay comfortably above how long a healthy client
// takes to acquire its first lease — below that, ordinary start-up
// registers as an outage. OUTAGE_GRACE, default 25s.
OutageGrace time.Duration
}
Options carries the plugin's runtime knobs. Every field is sourced from an environment variable declared in config.json and parsed in cmd/net-dhcp; a zero field means "unset", and NewPlugin substitutes the documented default. Grouping them beats growing NewPlugin's parameter list one knob at a time.
type Plugin ¶
type Plugin struct {
// contains filtered or unexported fields
}
Plugin is the DHCP network plugin
func NewPlugin ¶
NewPlugin creates a new Plugin. Zero-valued Options fields take the documented defaults, so NewPlugin(Options{}) is a valid production configuration.
func (*Plugin) Close ¶
Close stops the plugin. The HTTP server is shut down FIRST so no new Join can register a manager while (or after) we stop the existing ones — with the old ordering a Join dispatched during the stop fan-out installed a manager into the fresh registry that nobody ever stopped, orphaning its lease (no DHCPRELEASE) and its dhcpcd. Persistent DHCP clients are then stopped before process exit so they get a chance to send DHCPRELEASE for their leases — otherwise plugin upgrade or `docker plugin disable` would orphan every active lease at the upstream DHCP server, defeating the release-on-stop contract Leave normally honors.
func (*Plugin) CreateEndpoint ¶
func (p *Plugin) CreateEndpoint(ctx context.Context, r CreateEndpointRequest) (CreateEndpointResponse, error)
CreateEndpoint creates the per-endpoint host-side network plumbing (veth pair in bridge mode, macvlan child in macvlan mode), runs dhcpcd once to acquire an initial lease, and stashes the result for Join. Docker moves the link into the container's netns when it acts on our Join response.
func (*Plugin) CreateNetwork ¶
func (p *Plugin) CreateNetwork(r CreateNetworkRequest) error
CreateNetwork validates network creation: option shape (pure), then existence of the parent interface (bridge or NIC depending on mode), the null IPAM driver requirement, and — for bridge mode — that no other Docker network already owns this bridge's address space.
func (*Plugin) DeleteEndpoint ¶
func (p *Plugin) DeleteEndpoint(ctx context.Context, r DeleteEndpointRequest) error
DeleteEndpoint deletes the host-side network plumbing for an endpoint. In bridge mode that's the veth pair (deleting one side removes the peer). In macvlan mode the link has typically already been moved into the container netns and reaped with it, so cleanup is best-effort.
func (*Plugin) DeleteNetwork ¶
func (p *Plugin) DeleteNetwork(r DeleteNetworkRequest) error
DeleteNetwork "deletes" a DHCP network (the bridge is managed by the user). We also evict any persistent DHCP managers attached to this network: libnetwork doesn't issue Leave for endpoints in stopped containers when the network is removed, so without this prune they linger as ghost entries in /Plugin.Health.active_endpoints. Stop is safe to call against a manager whose underlying netns is gone — it just unblocks the dhcpcd-events loop and returns; dhcpcd itself may have already self-exited because its netns vanished.
func (*Plugin) EndpointOperInfo ¶
func (p *Plugin) EndpointOperInfo(ctx context.Context, r InfoRequest) (InfoResponse, error)
EndpointOperInfo retrieves some info about an existing endpoint
func (*Plugin) Join ¶
func (p *Plugin) Join(ctx context.Context, r JoinRequest) (JoinResponse, error)
type StaticRoute ¶
StaticRoute contains static route information