plugin.go 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. package clientport
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "net"
  8. "strings"
  9. "github.com/coredhcp/coredhcp/handler"
  10. "github.com/coredhcp/coredhcp/logger"
  11. "github.com/coredhcp/coredhcp/plugins"
  12. "github.com/insomniacslk/dhcp/dhcpv4"
  13. "github.com/insomniacslk/dhcp/dhcpv6"
  14. )
  15. var log = logger.GetLogger()
  16. func init() {
  17. plugins.RegisterPlugin("file", setupFile6, setupFile4)
  18. }
  19. // StaticRecords holds a MAC -> IP address mapping
  20. var StaticRecords map[string]net.IP
  21. // DHCPv6Records and DHCPv4Records are mappings between MAC addresses in
  22. // form of a string, to network configurations.
  23. var (
  24. DHCPv6Records map[string]net.IP
  25. DHCPv4Records map[string]net.IP
  26. )
  27. // LoadDHCPv4Records loads the DHCPv4Records global map with records stored on
  28. // the specified file. The records have to be one per line, a mac address and an
  29. // IPv4 address.
  30. func LoadDHCPv4Records(filename string) (map[string]net.IP, error) {
  31. log.Printf("plugins/file: reading leases from %s", filename)
  32. data, err := ioutil.ReadFile(filename)
  33. if err != nil {
  34. return nil, err
  35. }
  36. records := make(map[string]net.IP)
  37. for _, lineBytes := range bytes.Split(data, []byte{'\n'}) {
  38. line := string(lineBytes)
  39. if len(line) == 0 {
  40. continue
  41. }
  42. tokens := strings.Fields(line)
  43. if len(tokens) != 2 {
  44. return nil, fmt.Errorf("malformed line, want 2 fields, got %d: %s", len(tokens), line)
  45. }
  46. hwaddr, err := net.ParseMAC(tokens[0])
  47. if err != nil {
  48. return nil, fmt.Errorf("malformed hardware address: %s", tokens[0])
  49. }
  50. ipaddr := net.ParseIP(tokens[1])
  51. if ipaddr.To4() == nil {
  52. return nil, fmt.Errorf("expected an IPv4 address, got: %v", ipaddr)
  53. }
  54. records[hwaddr.String()] = ipaddr
  55. }
  56. return records, nil
  57. }
  58. // LoadDHCPv6Records loads the DHCPv6Records global map with records stored on
  59. // the specified file. The records have to be one per line, a mac address and an
  60. // IPv6 address.
  61. func LoadDHCPv6Records(filename string) (map[string]net.IP, error) {
  62. log.Printf("plugins/file: reading leases from %s", filename)
  63. data, err := ioutil.ReadFile(filename)
  64. if err != nil {
  65. return nil, err
  66. }
  67. records := make(map[string]net.IP)
  68. // TODO ignore comments
  69. for _, lineBytes := range bytes.Split(data, []byte{'\n'}) {
  70. line := string(lineBytes)
  71. if len(line) == 0 {
  72. continue
  73. }
  74. tokens := strings.Fields(line)
  75. if len(tokens) != 2 {
  76. return nil, fmt.Errorf("plugins/file: malformed line: %s", line)
  77. }
  78. hwaddr, err := net.ParseMAC(tokens[0])
  79. if err != nil {
  80. return nil, fmt.Errorf("plugins/file: malformed hardware address: %s", tokens[0])
  81. }
  82. ipaddr := net.ParseIP(tokens[1])
  83. if ipaddr.To16() == nil {
  84. return nil, fmt.Errorf("plugins/file: expected an IPv6 address, got: %v", ipaddr)
  85. }
  86. records[hwaddr.String()] = ipaddr
  87. }
  88. return records, nil
  89. }
  90. // Handler6 handles DHCPv6 packets for the file plugin
  91. func Handler6(req, resp dhcpv6.DHCPv6) (dhcpv6.DHCPv6, bool) {
  92. mac, err := dhcpv6.ExtractMAC(req)
  93. if err != nil {
  94. return nil, false
  95. }
  96. log.Printf("plugins/file: looking up an IP address for MAC %s", mac.String())
  97. ipaddr, ok := StaticRecords[mac.String()]
  98. if !ok {
  99. log.Warningf("plugins/file: MAC address %s is unknown", mac.String())
  100. return nil, false
  101. }
  102. log.Printf("plugins/file: found IP address %s for MAC %s", ipaddr, mac.String())
  103. resp.AddOption(&dhcpv6.OptIANA{
  104. // FIXME copy this field from the client, reject/drop if missing
  105. IaId: [4]byte{0xaa, 0xbb, 0xcc, 0xdd},
  106. Options: []dhcpv6.Option{
  107. &dhcpv6.OptIAAddress{
  108. IPv6Addr: ipaddr,
  109. PreferredLifetime: 3600,
  110. ValidLifetime: 3600,
  111. },
  112. },
  113. })
  114. resp.AddOption(&dhcpv6.OptDNSRecursiveNameServer{
  115. NameServers: []net.IP{
  116. // FIXME this must be read from the config file
  117. net.ParseIP("2001:4860:4860::8888"),
  118. net.ParseIP("2001:4860:4860::4444"),
  119. },
  120. })
  121. if oro := req.GetOption(dhcpv6.OptionORO); len(oro) > 0 {
  122. for _, code := range oro[0].(*dhcpv6.OptRequestedOption).RequestedOptions() {
  123. if code == dhcpv6.OptionBootfileURL {
  124. // bootfile URL is requested
  125. // FIXME this field should come from the configuration, not
  126. // being hardcoded
  127. resp.AddOption(
  128. &dhcpv6.OptBootFileURL{BootFileURL: []byte("http://[2001:db8::0:1]/nbp")},
  129. )
  130. }
  131. }
  132. }
  133. return resp, true
  134. }
  135. // Handler4 handles DHCPv4 packets for the file plugin
  136. func Handler4(req, resp *dhcpv4.DHCPv4) (*dhcpv4.DHCPv4, bool) {
  137. ipaddr, ok := StaticRecords[req.ClientHWAddr.String()]
  138. if !ok {
  139. log.Warningf("plugins/file: MAC address %s is unknown", req.ClientHWAddr.String())
  140. return resp, false
  141. }
  142. resp.YourIPAddr = ipaddr
  143. log.Printf("plugins/file: found IP address %s for MAC %s", ipaddr, req.ClientHWAddr.String())
  144. return resp, true
  145. }
  146. func setupFile6(args ...string) (handler.Handler6, error) {
  147. h6, _, err := setupFile(true, args...)
  148. return h6, err
  149. }
  150. func setupFile4(args ...string) (handler.Handler4, error) {
  151. _, h4, err := setupFile(false, args...)
  152. return h4, err
  153. }
  154. func setupFile(v6 bool, args ...string) (handler.Handler6, handler.Handler4, error) {
  155. var err error
  156. var records map[string]net.IP
  157. if len(args) < 1 {
  158. return nil, nil, errors.New("plugins/file: need a file name")
  159. }
  160. filename := args[0]
  161. if filename == "" {
  162. return nil, nil, errors.New("plugins/file: got empty file name")
  163. }
  164. if v6 {
  165. records, err = LoadDHCPv6Records(filename)
  166. } else {
  167. records, err = LoadDHCPv4Records(filename)
  168. }
  169. if err != nil {
  170. return nil, nil, fmt.Errorf("failed to load DHCPv6 records: %v", err)
  171. }
  172. StaticRecords = records
  173. log.Printf("plugins/file: loaded %d leases from %s", len(records), filename)
  174. return Handler6, Handler4, nil
  175. }