Skip to content

Instantly share code, notes, and snippets.

@Natolumin
Created February 6, 2020 10:50
Show Gist options
  • Save Natolumin/2acf759a51becb73080c244b6867d5d9 to your computer and use it in GitHub Desktop.
Save Natolumin/2acf759a51becb73080c244b6867d5d9 to your computer and use it in GitHub Desktop.
Coredhcp configuration mockup examples
# v4/v6 could be in the form of blocks or be in multiple files ?
dhcpv6 {
# "assignment plugin" parameters
# These blocks define allocation strategies, ie choosing addresses to give to the clients
# assign <plugin-name>:<instance-name>
assign range:prefix-delegation {
prefix 2001:db8:ffff::/48
assign-bounds 56 64 # for prefix delegation
lease-time 12h
allocator-strategy increment # random, compact, etc
hint ignore
# hint length-only ## for prefix delegation only, just consider the hinted prefix length instead of also the address
# hint best-effort
# hint mandatory ## will return an error if the hint cannot be satisfied
# possible extension ideas
limit 1 per duid # per mac, per request, etc
}
# a few other examples based on existing plugins
assign file:reservations {
path /path/to/reservation/file
lease-time forever
# by default file: would not store leases (since they are statically
# assigned), but eg if sharing a prefix with another plugin it
# could be useful to force storing the assigned lease
force-store
}
assign redis:redis-reservations {
key mac-address # or duid, etc
server
}
# lease-storage plugins store allocated leases.
# They are linked with an assignment plugin so the plugin can track already-allocated addresses.
lease-storage redis:redis-leases {
server-address fd00:2345::1
user dhcp
password password
}
lease-storage sql:db {
flavor postgres
connect "user:password@postgres-server:5432"
}
# Link between allocation plugins and lease-storage plugins: a plugin that returns already-allocated leases when a client matches it
assign stored-lease:preassigned {
storage-plugin @redis-leases
}
# handle instanciates a new server pipeline and binds it to one or more addresses or interfaces
# handle [<listen stanza> ...] {
handle eno1 224.0.0.12 {
# not sure about this one ? It's a property of the server but we could
# also have different ones between multiple pipelines, it's
# necessary, but we can have sane defaults (generate and store in a
# well-known location), it's a property to add to returned requests but
# also a match/filter to drop some requests
server-id uuid bd09ac90-679c-43f1-8ee9-d8d1880d67c0
# adding various dhcp options that are not request/reply but general simple properties is done with property
# property [override|append] <plugin-name> [plugin-args ...}
property preference 220
property rapid-commit
# assign <IA_type> @plugin-reference
assign prefix @prefix-delegation
# We could think of "short-form" plugins where most parameters use the default, but this is maybe unnecessary complexity
assign nontemporary,temporary range 2001:db8::/48
# multiple "assign" statements will only answer to unfulfilled
# requests. (or requests with an error), and rules are traversed
# in-order, except that assigns for different ia-types may be dispatched
# concurrently
assign temporary range fd00:1234::/64
# match plugins
# match <plugin-name> [plugin-args ...] {
match relayed-by 2001:db8:df:1:: {
property dns f2001:db8:9999::ffde
property override preference 150
assign prefix range 2001:db8:df:8000::/58
} else {
# either else or negative matches ("match not" ?), not sure which is better
}
match client-id enterprise 0 {
# There are a few control verbs as well
drop
# reject status UnspecFail
# commit ## This sends the answer without going through the rest of the rules
]
# lease storage is a bit weird,
# maybe bind it to an assign statement or stanza ?
store @redis-leases
# store @ephemeral
# store @local-disk
# store none ## This should raise warnings if any
# There are other kinds of plugins, that just don't match into existing
# plugin types. Mostly they're for doing all sorts of other shit, like
# monitoring, or ddns updates, or whatever else that's not exactly dhcp
prometheus :9001
# Finally, there are things that are done (either in plugins or in the
# core), that do not appear in the configuration, or happen even if the
# configuration doesn't mention them. Those are usually behaviours
# mandated by the RFCs.
# Examples: Adding Reconfigure Message option in reconfigure messages,
# echoing client-id, accepting rapid-commit
}
@insomniacslk
Copy link

insomniacslk commented Feb 9, 2020

As discussed on Slack, an alternative approach could be the following, that:

  • ties a functionality to a plugin name
  • every functionality is described by an interface, and plugins have to implement it, without direct access to the packet
  • every functionality not captured by an existing interface would be implementable with a generic interface

Still to clarify how to fallback to a different plugin.

I am using a custom format (that reminds the Corefile of CoreDNS), but this can probably be mapped to YAML or other formats that don't require custom parsers. The functionality and plugin are indicated as functionality:plugin_name (e.g. storage:mysql ) but this can be done in a better way. (edited to use subsections instead)

dhcpv6 {
  listen {
    interfaces: eth0, eth1
    addresses: ...
  }
  storage:mysql {
    host: ::1
    port: 3306
    user: mysql
    password: blah
  }
  lease {
    static {
    00:11:22:33:44:55 fd00:abcd::10/64 fd00:abce::10/64
    00:11:22:33:44:56 fd00:abcd::12/64
    }
    range {
      fd00:abcd::10-fd00:abcd::ff:ff
    }
  }
  dns {
    static {
    2001:4860:4860::8888 2001:4860:4860::8844
    }
  }
  generic {
    router {
    value: fd00:abcd::1/64
    }
  }
}

In this example the mac addresses defined under lease:static would be checked first, and the associated IP addresses will be leased. Otherwise it goes to the next lease plugin (range).
In general, the first plugin of a type that can provide an answer will be used, and the others will be ignored.

An example of interface (e.g. for DNS plugins) would be the following:

type DNSPlugin interface {
    GetDNS() ([]net.IP, error)
    GetSearchDomain() ([]string, error)
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment