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 }