143 lines
3.9 KiB
Go
143 lines
3.9 KiB
Go
package dhcp
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/gopacket/layers"
|
|
|
|
"github.com/google/gopacket"
|
|
gl "github.com/google/gopacket/layers"
|
|
)
|
|
|
|
func doDiscoverV4(address string) (map[string]string, error) {
|
|
|
|
localAddr, err := net.ResolveUDPAddr("udp", address+":68")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn, err := net.ListenUDP("udp", localAddr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn.SetDeadline(time.Now().Add(time.Second * 3))
|
|
defer func() {
|
|
conn.Close()
|
|
}()
|
|
|
|
sendDiscover(conn) // broadcast 255.255.255.255:67
|
|
return readOffer(conn)
|
|
|
|
}
|
|
|
|
func sendDiscover(conn *net.UDPConn) error {
|
|
|
|
server := fmt.Sprintf("%s:%d", "255.255.255.255", 67)
|
|
serverAddr, err := net.ResolveUDPAddr("udp", server)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
dhcp := &gl.DHCPv4{Operation: gl.DHCPOpRequest, HardwareType: gl.LinkTypeEthernet, Xid: 0x12345678,
|
|
ClientIP: net.IP{0, 0, 0, 0}, YourClientIP: net.IP{0, 0, 0, 0}, NextServerIP: net.IP{0, 0, 0, 0}, RelayAgentIP: net.IP{0, 0, 0, 0},
|
|
ClientHWAddr: net.HardwareAddr{0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc},
|
|
ServerName: make([]byte, 64), File: make([]byte, 128)}
|
|
|
|
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptMessageType, []byte{byte(gl.DHCPMsgTypeDiscover)}))
|
|
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptHostname, []byte{'o', 'v', 'e', 'r', 'f', 'l', 'o', 'w', '.', 'c', 'o', 'm'}))
|
|
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptPad, nil))
|
|
dhcp.Options = append(dhcp.Options, gl.NewDHCPOption(gl.DHCPOptParamsRequest,
|
|
[]byte{byte(gl.DHCPOptSubnetMask), byte(gl.DHCPOptBroadcastAddr), byte(gl.DHCPOptTimeOffset),
|
|
byte(gl.DHCPOptRouter), byte(gl.DHCPOptDomainName), byte(gl.DHCPOptDNS), byte(gl.DHCPOptDomainSearch),
|
|
byte(gl.DHCPOptHostname), byte(gl.DHCPOptNetBIOSTCPNS), byte(gl.DHCPOptInterfaceMTU), byte(gl.DHCPOptClasslessStaticRoute),
|
|
byte(gl.DHCPOptNTPServers)}))
|
|
|
|
buf := gopacket.NewSerializeBuffer()
|
|
opts := gopacket.SerializeOptions{FixLengths: true}
|
|
err = gopacket.SerializeLayers(buf, opts, dhcp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = conn.WriteToUDP(buf.Bytes(), serverAddr)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func readOffer(conn *net.UDPConn) (map[string]string, error) {
|
|
offerOptions := make(map[string]string)
|
|
|
|
buf := make([]byte, 1024)
|
|
n, _, err := conn.ReadFromUDP(buf)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if n <= 0 {
|
|
return nil, fmt.Errorf("%s", "Emptry response.")
|
|
}
|
|
|
|
var dhcp gl.DHCPv4
|
|
decoded := []gopacket.LayerType{}
|
|
parser := gopacket.NewDecodingLayerParser(gl.LayerTypeDHCPv4, &dhcp)
|
|
if err = parser.DecodeLayers(buf, &decoded); err != nil {
|
|
return nil, err
|
|
}
|
|
for _, layerType := range decoded {
|
|
switch layerType {
|
|
case layers.LayerTypeDHCPv4:
|
|
for _, opt := range dhcp.Options {
|
|
switch opt.Type {
|
|
case gl.DHCPOptServerID:
|
|
offerOptions["DHCP_SERVER"] = optDataToIPString(opt.Data)
|
|
break
|
|
case gl.DHCPOptLeaseTime:
|
|
var sec int32
|
|
if err = binary.Read(bytes.NewReader(opt.Data), binary.BigEndian, &sec); err == nil {
|
|
offerOptions["LEASE_TIME"] = fmt.Sprintf("%d", sec)
|
|
}
|
|
break
|
|
case gl.DHCPOptSubnetMask:
|
|
offerOptions["SUBNET_MASK"] = optDataToIPString(opt.Data)
|
|
break
|
|
case gl.DHCPOptRouter:
|
|
offerOptions["ROUTER"] = optDataToIPString(opt.Data)
|
|
break
|
|
case gl.DHCPOptDNS:
|
|
cnt := len(opt.Data) / 4
|
|
var dns []string
|
|
ipStr := optDataToIPString(opt.Data[0:4])
|
|
dns = append(dns, ipStr)
|
|
if cnt > 1 {
|
|
ipStr := optDataToIPString(opt.Data[4:8])
|
|
dns = append(dns, ipStr)
|
|
}
|
|
offerOptions["DOMAIN_NAME_SERVER"] = strings.Join(dns, ",")
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return offerOptions, nil
|
|
}
|
|
|
|
func optDataToIPString(b []byte) string {
|
|
var ipStr string
|
|
len := len(b)
|
|
for i := 0; i < len; i++ {
|
|
v := b[i]
|
|
ipStr += fmt.Sprintf("%d", v)
|
|
if i < len-1 {
|
|
ipStr += "."
|
|
}
|
|
}
|
|
|
|
return ipStr
|
|
}
|