From eaaeff9d7affdff0b8f2ad834ea73dc7980b52aa Mon Sep 17 00:00:00 2001 From: kworm83 <21957311+kworm83@users.noreply.github.com> Date: Wed, 20 Nov 2019 08:35:43 -0600 Subject: [PATCH] Proposed redis plugin (#64) * simplify boot file URL Signed-off-by: kevin --- cmds/coredhcp/main.go | 1 + go.mod | 3 +- go.sum | 4 ++ plugins/redis/plugin.go | 155 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 162 insertions(+), 1 deletion(-) create mode 100644 plugins/redis/plugin.go diff --git a/cmds/coredhcp/main.go b/cmds/coredhcp/main.go index 9cd9290..e40e59e 100644 --- a/cmds/coredhcp/main.go +++ b/cmds/coredhcp/main.go @@ -15,6 +15,7 @@ import ( _ "github.com/coredhcp/coredhcp/plugins/file" _ "github.com/coredhcp/coredhcp/plugins/netmask" _ "github.com/coredhcp/coredhcp/plugins/range" + _ "github.com/coredhcp/coredhcp/plugins/redis" _ "github.com/coredhcp/coredhcp/plugins/router" _ "github.com/coredhcp/coredhcp/plugins/server_id" ) diff --git a/go.mod b/go.mod index 23b36b1..3babae8 100644 --- a/go.mod +++ b/go.mod @@ -4,8 +4,9 @@ go 1.12 require ( github.com/chappjc/logrus-prefix v0.0.0-20180227015900-3a1d64819adb + github.com/gomodule/redigo v2.0.0+incompatible github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 // indirect - github.com/insomniacslk/dhcp v0.0.0-20191002211418-651e0846f780 + github.com/insomniacslk/dhcp v0.0.0-20191112140002-c05874014012 github.com/mattn/go-colorable v0.1.2 // indirect github.com/mdlayher/ethernet v0.0.0-20190606142754-0394541c37b7 // indirect github.com/mdlayher/raw v0.0.0-20190606144222-a54781e5f38f // indirect diff --git a/go.sum b/go.sum index 49e8e70..af61c1c 100644 --- a/go.sum +++ b/go.sum @@ -35,6 +35,8 @@ github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfb github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= @@ -51,6 +53,8 @@ github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714 h1:/jC7qQFrv8 github.com/hugelgupf/socketpair v0.0.0-20190730060125-05d35a94e714/go.mod h1:2Goc3h8EklBH5mspfHFxBnEoURQCGzQQH1ga9Myjvis= github.com/insomniacslk/dhcp v0.0.0-20191002211418-651e0846f780 h1:ty8rDh2kTAoxelUsFLMwjcQ3YW7lF7NHHcpUGuKMMNE= github.com/insomniacslk/dhcp v0.0.0-20191002211418-651e0846f780/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw= +github.com/insomniacslk/dhcp v0.0.0-20191112140002-c05874014012 h1:FPWfO4cQp0BS5WKI9Yoak1IlPNU9IUzGUmFZEfVG7V4= +github.com/insomniacslk/dhcp v0.0.0-20191112140002-c05874014012/go.mod h1:CfMdguCK66I5DAUJgGKyNz8aB6vO5dZzkm9Xep6WGvw= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= diff --git a/plugins/redis/plugin.go b/plugins/redis/plugin.go new file mode 100644 index 0000000..f35ff78 --- /dev/null +++ b/plugins/redis/plugin.go @@ -0,0 +1,155 @@ +// Copyright 2018-present the CoreDHCP Authors. All rights reserved +// This source code is licensed under the MIT license found in the +// LICENSE file in the root directory of this source tree. + +package redisplugin + +import ( + "errors" + "fmt" + "net" + "strings" + "time" + + "github.com/coredhcp/coredhcp/handler" + "github.com/coredhcp/coredhcp/logger" + "github.com/coredhcp/coredhcp/plugins" + "github.com/gomodule/redigo/redis" + "github.com/insomniacslk/dhcp/dhcpv4" + "github.com/insomniacslk/dhcp/dhcpv6" +) + +// various global variables +var ( + log = logger.GetLogger("plugins/redis") + pool *redis.Pool +) + +func init() { + plugins.RegisterPlugin("redis", setupRedis6, setupRedis4) +} + +// Handler6 handles DHCPv6 packets for the redis plugin +func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) { + // TODO add IPv6 support + return nil, true +} + +// Handler4 handles DHCPv4 packets for the redis plugin +func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) { + // Get redis connection from pool + conn := pool.Get() + + // defer redis connection close so we don't leak connections + defer conn.Close() + + // Get all options for a MAC + options, err := redis.StringMap(conn.Do("HGETALL", "mac:"+req.ClientHWAddr.String())) + + // Handle redis error + if err != nil { + log.Printf("Redis error: %s...dropping request", err) + return nil, true + } + + // Handle no hash found + if len(options) == 0 { + log.Printf("MAC %s not found...dropping request", req.ClientHWAddr.String()) + return nil, true + } + + // Handle no ipv4 field + if options["ipv4"] == "" { + log.Printf("MAC %s has no ipv4 field...dropping request", req.ClientHWAddr.String()) + return nil, true + } + + // Loop through options returned and assign as needed + for option, value := range options { + switch option { + case "ipv4": + ipaddr, ipnet, err := net.ParseCIDR(value) + if err != nil { + log.Printf("MAC %s malformed IP %s error: %s...dropping request", req.ClientHWAddr.String(), value, err) + return nil, true + } + resp.YourIPAddr = ipaddr + resp.Options.Update(dhcpv4.OptSubnetMask(ipnet.Mask)) + log.Printf("MAC %s assigned IPv4 address %s", req.ClientHWAddr.String(), value) + + case "router": + router := net.ParseIP(value) + if router.To4() == nil { + log.Printf("MAC %s Invalid router option: %s...option skipped", req.ClientHWAddr.String(), value) + break + } + resp.Options.Update(dhcpv4.OptRouter(router)) + + case "dns": + var dnsServers4 []net.IP + servers := strings.Split(value, ",") + for _, server := range servers { + DNSServer := net.ParseIP(server) + if DNSServer.To4() == nil { + log.Printf("MAC %s Invalid dns server: %s...dropping request", req.ClientHWAddr.String(), server) + return nil, true + } + dnsServers4 = append(dnsServers4, DNSServer) + } + if req.IsOptionRequested(dhcpv4.OptionDomainNameServer) { + resp.Options.Update(dhcpv4.OptDNS(dnsServers4...)) + } + + case "leaseTime": + lt, err := time.ParseDuration(value) + if err != nil { + log.Printf("MAC %s invalid lease time %s...option skipped", req.ClientHWAddr.String(), value) + break + } + // Set lease time + resp.Options.Update(dhcpv4.OptIPAddressLeaseTime(lt)) + + default: + log.Printf("MAC %s found un-handled option %s...option skipped", req.ClientHWAddr.String(), option) + } + } + + return resp, false +} + +func setupRedis6(args ...string) (handler.Handler6, error) { + // TODO setup function for IPv6 + log.Warning("not implemented for IPv6") + return Handler6, nil +} + +func setupRedis4(args ...string) (handler.Handler4, error) { + _, h4, err := setupRedis(false, args...) + return h4, err +} + +func setupRedis(v6 bool, args ...string) (handler.Handler6, handler.Handler4, error) { + if len(args) < 1 { + return nil, nil, fmt.Errorf("invalid number of arguments, want: 1 (redis server:port), got: %d", len(args)) + } + if args[0] == "" { + return nil, nil, errors.New("Redis server can't be empty") + } + + if v6 { + log.Printf("Using redis server %s for DHCPv6 static leases", args[0]) + } else { + log.Printf("Using redis server %s for DHCPv4 static leases", args[0]) + } + + // Initialize Redis Pool + pool = &redis.Pool{ + MaxIdle: 10, + IdleTimeout: 240 * time.Second, + Dial: func() (redis.Conn, error) { + return redis.Dial("tcp", args[0]) + }, + } + + return Handler6, Handler4, nil +}