Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for rfc8572 DHCP option encoding. #128

Merged
merged 1 commit into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions dhcp/dhcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ const confTemplate = `
server6:
plugins:
- server_id: LL {{ .IntfMacAddr }}
{{ if .BootzURL }}
- bootz: {{ .BootzURL }}
{{ if .BootzURLs }}
- bootz: {{ .BootzURLs }}
{{ end }}
{{ if .DNSv6 }}
- DNS: {{ .DNSv6 }}
Expand All @@ -53,8 +53,8 @@ server4:
plugins:
- lease_time: 3600s
- server_id: {{ .IntfIPAddr }}
{{ if .BootzURL }}
- bootz: {{ .BootzURL }}
{{ if .BootzURLs }}
- bootz: {{ .BootzURLs }}
{{ end }}
{{ if .DNSv4 }}
- DNS: {{ .DNSv4 }}
Expand All @@ -77,7 +77,7 @@ type Config struct {
Interface string
DNS []string
AddressMap map[string]*Entry
BootzURL string
BootzURLs []string
}

// Entry represents a dhcp record.
Expand Down Expand Up @@ -158,7 +158,7 @@ func generateConfigFile(conf *Config) (string, error) {

intf, err := net.InterfaceByName(conf.Interface)
if err != nil {
return "", fmt.Errorf("unknown interface %v", *intf)
return "", fmt.Errorf("unknown interface %v: %v", conf.Interface, err)
}

IPv4Addr := getIPv4Address(intf)
Expand Down Expand Up @@ -191,15 +191,15 @@ func generateConfigFile(conf *Config) (string, error) {
DNSv6 string
IPv4Leases string
IPv6Leases string
BootzURL string
BootzURLs string
}{
IntfIPAddr: IPv4Addr.String(),
IntfMacAddr: intf.HardwareAddr.String(),
DNSv4: strings.Join(DNSv4, " "),
DNSv6: strings.Join(DNSv6, " "),
IPv4Leases: strings.Join(v4Records, " "),
IPv6Leases: strings.Join(v6Records, " "),
BootzURL: conf.BootzURL,
BootzURLs: strings.Join(conf.BootzURLs, " "),
}); err != nil {
return "", fmt.Errorf("error generating configuration template: %v", err)
}
Expand Down
4 changes: 2 additions & 2 deletions dhcp/main/dhcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var (
intf = flag.String("i", "eth7", "Network interface to use for dhcp server.")
records = flag.String("records", "4c:5d:3c:ef:de:60,5.78.26.27/16,5.78.0.1;FOX2506P2QT,5::10/64", "List of dhcp records separated by a semi-colon.")
dns = flag.String("dns", "5.38.4.124", "List of dns servers separated by a semi-colon.")
bootz = flag.String("bootz_url", "bootz://dev-mgbl-lnx6.cisco.com:50052/grpc", "Bootz server URL.")
bootz = flag.String("bootz_urls", "bootz://dev-mgbl-lnx6.cisco.com:50052/grpc;bootz://dev-mgbl-lnx2.cisco.com:50052/grpc", "List of Bootz server URLs separated by a semi-colon.")
)

func main() {
Expand Down Expand Up @@ -57,7 +57,7 @@ func main() {
Interface: *intf,
DNS: strings.Split(*dns, ";"),
AddressMap: addressMap,
BootzURL: *bootz,
BootzURLs: strings.Split(*bootz, ";"),
}

go func() {
Expand Down
44 changes: 36 additions & 8 deletions dhcp/plugins/bootz/bootz.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package bootz

import (
"encoding/binary"
"fmt"
"net/url"

Expand Down Expand Up @@ -44,33 +45,60 @@ var (
ztpV6Opt dhcpv6.Option
)

func parseArgs(args ...string) (*url.URL, error) {
if len(args) != 1 {
return nil, fmt.Errorf("exactly one argument must be passed to BootZ plugin, got %d", len(args))
func encodeBootstrapServerList(urls []string) []byte {
// From RFC 8572 section 8.3:
//
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
// | uri-length | URI |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
//
// * uri-length: 2 octets long; specifies the length of the URI data.
// * URI: URI of the SZTP bootstrap server.
b := []byte{}
for _, u := range urls {
b = binary.BigEndian.AppendUint16(b, uint16(len(u)))
b = append(b, []byte(u)...)
}
return url.Parse(args[0])
return b
}

// Verifies that the passed Bootz servers are valid URLs and returns them as a list of strings.
func parseArgs(args ...string) ([]string, error) {
if len(args) < 1 {
return nil, fmt.Errorf("at least one argument must be passed to BootZ plugin, got %d", len(args))
}
urls := make([]string, len(args))
for i, arg := range args {
u, err := url.Parse(arg)
if err != nil {
return nil, err
}
urls[i] = u.String()
}
return urls, nil
}

func setup4(args ...string) (handler.Handler4, error) {
url, err := parseArgs(args...)
urls, err := parseArgs(args...)
if err != nil {
return nil, err
}

ztpV4Opt = &dhcpv4.Option{
Code: dhcpv4.GenericOptionCode(OPTION_V4_SZTP_REDIRECT),
Value: dhcpv4.String(url.String()),
Value: dhcpv4.String(string(encodeBootstrapServerList(urls))),
}
return handler4, nil
}

func setup6(args ...string) (handler.Handler6, error) {
url, err := parseArgs(args...)
urls, err := parseArgs(args...)
if err != nil {
return nil, err
}
ztpV6Opt = &dhcpv6.OptionGeneric{
OptionCode: dhcpv6.OptionCode(OPTION_V6_SZTP_REDIRECT),
OptionData: []byte(url.String()),
OptionData: encodeBootstrapServerList(urls),
}
return handler6, nil
}
Expand Down
Loading