plugin.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package rangeplugin
  2. import (
  3. "bytes"
  4. "encoding/binary"
  5. "errors"
  6. "fmt"
  7. "io/ioutil"
  8. "math/rand"
  9. "net"
  10. "os"
  11. "strings"
  12. "time"
  13. "github.com/coredhcp/coredhcp/handler"
  14. "github.com/coredhcp/coredhcp/logger"
  15. "github.com/coredhcp/coredhcp/plugins"
  16. "github.com/insomniacslk/dhcp/dhcpv4"
  17. "github.com/insomniacslk/dhcp/dhcpv6"
  18. )
  19. var log = logger.GetLogger()
  20. func init() {
  21. plugins.RegisterPlugin("range", setupRange6, setupRange4)
  22. }
  23. //Record holds an IP lease record
  24. type Record struct {
  25. IP net.IP
  26. expires time.Time
  27. }
  28. // Records holds a MAC -> IP address and lease time mapping
  29. var Records map[string]*Record
  30. // DHCPv6Records and DHCPv4Records are mappings between MAC addresses in
  31. // form of a string, to network configurations.
  32. var (
  33. // TODO change DHCPv6Records to Record
  34. DHCPv6Records map[string]*Record
  35. DHCPv4Records map[string]*Record
  36. LeaseTime time.Duration
  37. filename string
  38. ipRangeStart net.IP
  39. ipRangeEnd net.IP
  40. )
  41. // LoadDHCPv6Records loads the DHCPv6Records global map with records stored on
  42. // the specified file. The records have to be one per line, a mac address and an
  43. // IPv6 address.
  44. func LoadDHCPv6Records(filename string) (map[string]*Record, error) {
  45. // TODO load function for IPv6
  46. return nil, errors.New("not implemented for IPv6")
  47. }
  48. // LoadDHCPv4Records loads the DHCPv4Records global map with records stored on
  49. // the specified file. The records have to be one per line, a mac address and an
  50. // IPv4 address.
  51. func LoadDHCPv4Records(filename string) (map[string]*Record, error) {
  52. log.Printf("plugins/range: reading leases from %s", filename)
  53. data, err := ioutil.ReadFile(filename)
  54. if err != nil {
  55. return nil, err
  56. }
  57. records := make(map[string]*Record)
  58. for _, lineBytes := range bytes.Split(data, []byte{'\n'}) {
  59. line := string(lineBytes)
  60. if len(line) == 0 {
  61. continue
  62. }
  63. tokens := strings.Fields(line)
  64. if len(tokens) != 3 {
  65. return nil, fmt.Errorf("malformed line, want 3 fields, got %d: %s", len(tokens), line)
  66. }
  67. hwaddr, err := net.ParseMAC(tokens[0])
  68. if err != nil {
  69. return nil, fmt.Errorf("malformed hardware address: %s", tokens[0])
  70. }
  71. ipaddr := net.ParseIP(tokens[1])
  72. if ipaddr.To4() == nil {
  73. return nil, fmt.Errorf("expected an IPv4 address, got: %v", ipaddr)
  74. }
  75. expires, err := time.Parse(time.RFC3339, tokens[2])
  76. if err != nil {
  77. return nil, fmt.Errorf("expected time of exipry in RFC3339 format, got: %v", tokens[2])
  78. }
  79. records[hwaddr.String()] = &Record{IP: ipaddr, expires: expires}
  80. }
  81. return records, nil
  82. }
  83. // Handler6 handles DHCPv6 packets for the file plugin
  84. func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
  85. // TODO add IPv6 netmask to the response
  86. return resp, false
  87. }
  88. // Handler4 handles DHCPv4 packets for the range plugin
  89. func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
  90. record, ok := Records[req.ClientHWAddr.String()]
  91. if !ok {
  92. log.Printf("plugins/file: MAC address %s is new, leasing new IP address", req.ClientHWAddr.String())
  93. rec, err := createIP(ipRangeStart, ipRangeEnd)
  94. if err != nil {
  95. log.Error(err)
  96. return nil, true
  97. }
  98. err = saveIPAddress(req.ClientHWAddr, rec)
  99. if err != nil {
  100. log.Printf("plugins/file: SaveIPAddress failed: %v", err)
  101. }
  102. Records[req.ClientHWAddr.String()] = rec
  103. record = rec
  104. }
  105. resp.YourIPAddr = record.IP
  106. resp.Options.Update(dhcpv4.OptIPAddressLeaseTime(LeaseTime))
  107. log.Printf("plugins/file: found IP address %s for MAC %s", record.IP, req.ClientHWAddr.String())
  108. return resp, false
  109. }
  110. func setupRange6(args ...string) (handler.Handler6, error) {
  111. // TODO setup function for IPv6
  112. log.Warning("plugins/range: not implemented for IPv6")
  113. return Handler6, nil
  114. }
  115. func setupRange4(args ...string) (handler.Handler4, error) {
  116. _, h4, err := setupRange(false, args...)
  117. return h4, err
  118. }
  119. func setupRange(v6 bool, args ...string) (handler.Handler6, handler.Handler4, error) {
  120. var err error
  121. if len(args) < 4 {
  122. return nil, nil, errors.New("need a file name, start of the IP range, end og the IP range and a lease time")
  123. }
  124. filename = args[0]
  125. if filename == "" {
  126. return nil, nil, errors.New("got empty file name")
  127. }
  128. ipRangeStart = net.ParseIP(args[1])
  129. if ipRangeStart.To4() == nil {
  130. return nil, nil, errors.New("expected an IP address, got: " + args[1])
  131. }
  132. ipRangeEnd = net.ParseIP(args[2])
  133. if ipRangeEnd.To4() == nil {
  134. return nil, nil, errors.New("expected an IP address, got: " + args[2])
  135. }
  136. if binary.BigEndian.Uint32(ipRangeStart.To4()) >= binary.BigEndian.Uint32(ipRangeEnd.To4()) {
  137. return nil, nil, errors.New("start of IP range has to be lower than the end fo an IP range")
  138. }
  139. LeaseTime, err = time.ParseDuration(args[3])
  140. if err != nil {
  141. return Handler6, Handler4, errors.New("expected an uint32, got: " + args[3])
  142. }
  143. if v6 {
  144. Records, err = LoadDHCPv6Records(filename)
  145. } else {
  146. Records, err = LoadDHCPv4Records(filename)
  147. }
  148. if err != nil {
  149. return nil, nil, fmt.Errorf("failed to load DHCPv4 records: %v", err)
  150. }
  151. rand.Seed(time.Now().Unix())
  152. log.Printf("plugins/range: loaded %d leases from %s", len(Records), filename)
  153. return Handler6, Handler4, nil
  154. }
  155. func createIP(rangeStart net.IP, rangeEnd net.IP) (*Record, error) {
  156. ip := make([]byte, 4)
  157. rangeStartInt := binary.BigEndian.Uint32(rangeStart.To4())
  158. rangeEndInt := binary.BigEndian.Uint32(rangeEnd.To4())
  159. binary.BigEndian.PutUint32(ip, random(rangeStartInt, rangeEndInt))
  160. taken := checkIfTaken(ip)
  161. for taken {
  162. ipInt := binary.BigEndian.Uint32(ip)
  163. ipInt++
  164. binary.BigEndian.PutUint32(ip, ipInt)
  165. if ipInt > rangeEndInt {
  166. break
  167. }
  168. taken = checkIfTaken(ip)
  169. }
  170. for taken {
  171. ipInt := binary.BigEndian.Uint32(ip)
  172. ipInt--
  173. binary.BigEndian.PutUint32(ip, ipInt)
  174. if ipInt < rangeStartInt {
  175. return &Record{}, errors.New("no new IP addresses available")
  176. }
  177. taken = checkIfTaken(ip)
  178. }
  179. return &Record{IP: ip, expires: time.Now().Add(LeaseTime)}, nil
  180. }
  181. func random(min uint32, max uint32) uint32 {
  182. return uint32(rand.Intn(int(max-min))) + min
  183. }
  184. func checkIfTaken(ip net.IP) bool {
  185. taken := false
  186. for _, v := range Records {
  187. if v.IP.String() == ip.String() && (v.expires.After(time.Now())) {
  188. taken = true
  189. break
  190. }
  191. }
  192. return taken
  193. }
  194. func saveIPAddress(mac net.HardwareAddr, record *Record) error {
  195. f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
  196. if err != nil {
  197. return err
  198. }
  199. defer f.Close()
  200. _, err = f.WriteString(mac.String() + " " + record.IP.String() + " " + record.expires.Format(time.RFC3339) + "\n")
  201. if err != nil {
  202. return err
  203. }
  204. err = f.Sync()
  205. if err != nil {
  206. return err
  207. }
  208. return nil
  209. }