plugin

package
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: GPL-3.0 Imports: 31 Imported by: 0

Documentation

Index

Constants

View Source
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.

View Source
const (
	ModeBridge  = "bridge"
	ModeMacvlan = "macvlan"
	ModeIPvlan  = "ipvlan"
)

Network attachment modes selected by the `mode` driver option.

View Source
const CLIOptionsKey string = "com.docker.network.generic"

CLIOptionsKey is the key used in create network options by the CLI for custom options

View Source
const DriverName string = "net-dhcp"

DriverName is the name of the Docker Network Driver

Variables

This section is empty.

Functions

func IsDHCPPlugin

func IsDHCPPlugin(driver string) bool

IsDHCPPlugin checks if a Docker network driver is an instance of this plugin

Types

type CapabilitiesResponse

type CapabilitiesResponse struct {
	Scope             string
	ConnectivityScope string
}

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

type DeleteEndpointRequest struct {
	NetworkID  string
	EndpointID string
}

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

type EndpointInterface struct {
	Address     string
	AddressIPv6 string
	MacAddress  string
}

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

type InfoRequest struct {
	NetworkID  string
	EndpointID string
}

InfoRequest is sent by the daemon when querying endpoint information

type InfoResponse

type InfoResponse struct {
	Value map[string]string
}

InfoResponse is endpoint information sent in response to an InfoRequest

type InterfaceName

type InterfaceName struct {
	SrcName   string
	DstPrefix string
	DstName   string
}

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

type LeaveRequest struct {
	NetworkID  string
	EndpointID string
}

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

func NewPlugin(opts Options) (*Plugin, error)

NewPlugin creates a new Plugin. Zero-valued Options fields take the documented defaults, so NewPlugin(Options{}) is a valid production configuration.

func (*Plugin) Close

func (p *Plugin) Close() error

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

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)

func (*Plugin) Leave

func (p *Plugin) Leave(ctx context.Context, r LeaveRequest) error

Leave stops the persistent DHCP client for an endpoint

func (*Plugin) Listen

func (p *Plugin) Listen(bindSock string) error

Listen starts the plugin server

type StaticRoute

type StaticRoute struct {
	Destination string
	RouteType   int
	NextHop     string
}

StaticRoute contains static route information

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL