// Copyright 2020 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 (
	
	
	
	
	

	
)

// NetProtocolStats stores the contents from /proc/net/protocols.
type NetProtocolStats map[string]NetProtocolStatLine

// NetProtocolStatLine contains a single line parsed from /proc/net/protocols. We
// only care about the first six columns as the rest are not likely to change
// and only serve to provide a set of capabilities for each protocol.
type NetProtocolStatLine struct {
	Name         string // 0 The name of the protocol
	Size         uint64 // 1 The size, in bytes, of a given protocol structure. e.g. sizeof(struct tcp_sock) or sizeof(struct unix_sock)
	Sockets      int64  // 2 Number of sockets in use by this protocol
	Memory       int64  // 3 Number of 4KB pages allocated by all sockets of this protocol
	Pressure     int    // 4 This is either yes, no, or NI (not implemented). For the sake of simplicity we treat NI as not experiencing memory pressure.
	MaxHeader    uint64 // 5 Protocol specific max header size
	Slab         bool   // 6 Indicates whether or not memory is allocated from the SLAB
	ModuleName   string // 7 The name of the module that implemented this protocol or "kernel" if not from a module
	Capabilities NetProtocolCapabilities
}

// NetProtocolCapabilities contains a list of capabilities for each protocol.
type NetProtocolCapabilities struct {
	Close               bool // 8
	Connect             bool // 9
	Disconnect          bool // 10
	Accept              bool // 11
	IoCtl               bool // 12
	Init                bool // 13
	Destroy             bool // 14
	Shutdown            bool // 15
	SetSockOpt          bool // 16
	GetSockOpt          bool // 17
	SendMsg             bool // 18
	RecvMsg             bool // 19
	SendPage            bool // 20
	Bind                bool // 21
	BacklogRcv          bool // 22
	Hash                bool // 23
	UnHash              bool // 24
	GetPort             bool // 25
	EnterMemoryPressure bool // 26
}

// NetProtocols reads stats from /proc/net/protocols and returns a map of
// PortocolStatLine entries. As of this writing no official Linux Documentation
// exists, however the source is fairly self-explanatory and the format seems
// stable since its introduction in 2.6.12-rc2
// Linux 2.6.12-rc2 - https://elixir.bootlin.com/linux/v2.6.12-rc2/source/net/core/sock.c#L1452
// Linux 5.10 - https://elixir.bootlin.com/linux/v5.10.4/source/net/core/sock.c#L3586
func ( FS) () (NetProtocolStats, error) {
	,  := util.ReadFileNoStat(.proc.Path("net/protocols"))
	if  != nil {
		return NetProtocolStats{}, 
	}
	return parseNetProtocols(bufio.NewScanner(bytes.NewReader()))
}

func parseNetProtocols( *bufio.Scanner) (NetProtocolStats, error) {
	 := NetProtocolStats{}

	// Skip the header line
	.Scan()

	for .Scan() {
		,  := .parseLine(.Text())
		if  != nil {
			return NetProtocolStats{}, 
		}

		[.Name] = *
	}
	return , nil
}

func ( NetProtocolStats) ( string) (*NetProtocolStatLine, error) {
	 := &NetProtocolStatLine{Capabilities: NetProtocolCapabilities{}}
	var  error
	const  = "yes"
	const  = "no"

	 := strings.Fields()
	.Name = [0]
	.Size,  = strconv.ParseUint([1], 10, 64)
	if  != nil {
		return nil, 
	}
	.Sockets,  = strconv.ParseInt([2], 10, 64)
	if  != nil {
		return nil, 
	}
	.Memory,  = strconv.ParseInt([3], 10, 64)
	if  != nil {
		return nil, 
	}
	switch [4] {
	case :
		.Pressure = 1
	case :
		.Pressure = 0
	default:
		.Pressure = -1
	}
	.MaxHeader,  = strconv.ParseUint([5], 10, 64)
	if  != nil {
		return nil, 
	}
	switch [6] {
	case :
		.Slab = true
	case :
		.Slab = false
	default:
		return nil, fmt.Errorf("%w: capability for protocol: %s", ErrFileParse, .Name)
	}
	.ModuleName = [7]

	 = .Capabilities.parseCapabilities([8:])
	if  != nil {
		return nil, 
	}

	return , nil
}

func ( *NetProtocolCapabilities) ( []string) error {
	// The capabilities are all bools so we can loop over to map them
	 := [...]*bool{
		&.Close,
		&.Connect,
		&.Disconnect,
		&.Accept,
		&.IoCtl,
		&.Init,
		&.Destroy,
		&.Shutdown,
		&.SetSockOpt,
		&.GetSockOpt,
		&.SendMsg,
		&.RecvMsg,
		&.SendPage,
		&.Bind,
		&.BacklogRcv,
		&.Hash,
		&.UnHash,
		&.GetPort,
		&.EnterMemoryPressure,
	}

	for  := 0;  < len(); ++ {
		switch [] {
		case "y":
			*[] = true
		case "n":
			*[] = false
		default:
			return fmt.Errorf("%w: capability block for protocol: position %d", ErrFileParse, )
		}
	}
	return nil
}