// Copyright 2018 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package procfs

import (
	
	
	
	
	
	
	
	
	
	

	
)

// IPVSStats holds IPVS statistics, as exposed by the kernel in `/proc/net/ip_vs_stats`.
type IPVSStats struct {
	// Total count of connections.
	Connections uint64
	// Total incoming packages processed.
	IncomingPackets uint64
	// Total outgoing packages processed.
	OutgoingPackets uint64
	// Total incoming traffic.
	IncomingBytes uint64
	// Total outgoing traffic.
	OutgoingBytes uint64
}

// IPVSBackendStatus holds current metrics of one virtual / real address pair.
type IPVSBackendStatus struct {
	// The local (virtual) IP address.
	LocalAddress net.IP
	// The remote (real) IP address.
	RemoteAddress net.IP
	// The local (virtual) port.
	LocalPort uint16
	// The remote (real) port.
	RemotePort uint16
	// The local firewall mark
	LocalMark string
	// The transport protocol (TCP, UDP).
	Proto string
	// The current number of active connections for this virtual/real address pair.
	ActiveConn uint64
	// The current number of inactive connections for this virtual/real address pair.
	InactConn uint64
	// The current weight of this virtual/real address pair.
	Weight uint64
}

// IPVSStats reads the IPVS statistics from the specified `proc` filesystem.
func ( FS) () (IPVSStats, error) {
	,  := util.ReadFileNoStat(.proc.Path("net/ip_vs_stats"))
	if  != nil {
		return IPVSStats{}, 
	}

	return parseIPVSStats(bytes.NewReader())
}

// parseIPVSStats performs the actual parsing of `ip_vs_stats`.
func parseIPVSStats( io.Reader) (IPVSStats, error) {
	var (
		 []byte
		   []string
		  []string
		       IPVSStats
	)

	,  := io.ReadAll()
	if  != nil {
		return IPVSStats{}, 
	}

	 = strings.SplitN(string(), "\n", 4)
	if len() != 4 {
		return IPVSStats{}, errors.New("ip_vs_stats corrupt: too short")
	}

	 = strings.Fields([2])
	if len() != 5 {
		return IPVSStats{}, errors.New("ip_vs_stats corrupt: unexpected number of fields")
	}

	.Connections,  = strconv.ParseUint([0], 16, 64)
	if  != nil {
		return IPVSStats{}, 
	}
	.IncomingPackets,  = strconv.ParseUint([1], 16, 64)
	if  != nil {
		return IPVSStats{}, 
	}
	.OutgoingPackets,  = strconv.ParseUint([2], 16, 64)
	if  != nil {
		return IPVSStats{}, 
	}
	.IncomingBytes,  = strconv.ParseUint([3], 16, 64)
	if  != nil {
		return IPVSStats{}, 
	}
	.OutgoingBytes,  = strconv.ParseUint([4], 16, 64)
	if  != nil {
		return IPVSStats{}, 
	}

	return , nil
}

// IPVSBackendStatus reads and returns the status of all (virtual,real) server pairs from the specified `proc` filesystem.
func ( FS) () ([]IPVSBackendStatus, error) {
	,  := os.Open(.proc.Path("net/ip_vs"))
	if  != nil {
		return nil, 
	}
	defer .Close()

	return parseIPVSBackendStatus()
}

func parseIPVSBackendStatus( io.Reader) ([]IPVSBackendStatus, error) {
	var (
		       []IPVSBackendStatus
		      = bufio.NewScanner()
		        string
		    string
		 net.IP
		    uint16
		          error
	)

	for .Scan() {
		 := strings.Fields(.Text())
		if len() == 0 {
			continue
		}
		switch {
		case [0] == "IP" || [0] == "Prot" || [1] == "RemoteAddress:Port":
			continue
		case [0] == "TCP" || [0] == "UDP":
			if len() < 2 {
				continue
			}
			 = [0]
			 = ""
			, ,  = parseIPPort([1])
			if  != nil {
				return nil, 
			}
		case [0] == "FWM":
			if len() < 2 {
				continue
			}
			 = [0]
			 = [1]
			 = nil
			 = 0
		case [0] == "->":
			if len() < 6 {
				continue
			}
			, ,  := parseIPPort([1])
			if  != nil {
				return nil, 
			}
			,  := strconv.ParseUint([3], 10, 64)
			if  != nil {
				return nil, 
			}
			,  := strconv.ParseUint([4], 10, 64)
			if  != nil {
				return nil, 
			}
			,  := strconv.ParseUint([5], 10, 64)
			if  != nil {
				return nil, 
			}
			 = append(, IPVSBackendStatus{
				LocalAddress:  ,
				LocalPort:     ,
				LocalMark:     ,
				RemoteAddress: ,
				RemotePort:    ,
				Proto:         ,
				Weight:        ,
				ActiveConn:    ,
				InactConn:     ,
			})
		}
	}
	return , nil
}

func parseIPPort( string) (net.IP, uint16, error) {
	var (
		  net.IP
		 error
	)

	switch len() {
	case 13:
		,  = hex.DecodeString([0:8])
		if  != nil {
			return nil, 0, 
		}
	case 46:
		 = net.ParseIP([1:40])
		if  == nil {
			return nil, 0, fmt.Errorf("%w: Invalid IPv6 addr %s: %w", ErrFileParse, [1:40], )
		}
	default:
		return nil, 0, fmt.Errorf("%w: Unexpected IP:Port %s: %w", ErrFileParse, , )
	}

	 := [len()-4:]
	if len() != 4 {
		return nil, 0,
			fmt.Errorf("%w: Unexpected port string format %s: %w", ErrFileParse, , )
	}
	,  := strconv.ParseUint(, 16, 16)
	if  != nil {
		return nil, 0, 
	}

	return , uint16(), nil
}