func GetsockoptTCPInfo()

in unix/syscall_zos_s390x.go [906:1103]


func GetsockoptTCPInfo(fd, level, opt int) (*TCPInfo, error) {
	jobname := []byte("\x5c\x40\x40\x40\x40\x40\x40\x40") // "*"
	responseBuffer := [4096]byte{0}
	var bufferAlet, reasonCode uint32 = 0, 0
	var bufferLen, returnValue, returnCode int32 = 4096, 0, 0

	dsa := [18]uint64{0}
	var argv [7]unsafe.Pointer
	argv[0] = unsafe.Pointer(&jobname[0])
	argv[1] = unsafe.Pointer(&responseBuffer[0])
	argv[2] = unsafe.Pointer(&bufferAlet)
	argv[3] = unsafe.Pointer(&bufferLen)
	argv[4] = unsafe.Pointer(&returnValue)
	argv[5] = unsafe.Pointer(&returnCode)
	argv[6] = unsafe.Pointer(&reasonCode)

	request := (*struct {
		header nwmHeader
		filter nwmFilter
	})(unsafe.Pointer(&responseBuffer[0]))

	EZBNMIF4 := svcLoad(&svcNameTable[svc_EZBNMIF4][0])
	if EZBNMIF4 == nil {
		return nil, errnoErr(EINVAL)
	}

	// GetGlobalStats EZBNMIF4 call
	request.header.ident = nwmHeaderIdentifier
	request.header.length = uint32(unsafe.Sizeof(request.header))
	request.header.version = nwmCurrentVer
	request.header.nwmType = nwmGlobalStatsType
	request.header.options = 0x80000000

	svcCall(EZBNMIF4, &argv[0], &dsa[0])

	// outputDesc field is filled by EZBNMIF4 on success
	if returnCode != 0 || request.header.outputDesc.offset == 0 {
		return nil, errnoErr(EINVAL)
	}

	// Check that EZBNMIF4 returned a nwmRecHeader
	recHeader := (*nwmRecHeader)(unsafe.Pointer(&responseBuffer[request.header.outputDesc.offset]))
	if recHeader.ident != nwmRecHeaderIdentifier {
		return nil, errnoErr(EINVAL)
	}

	// Parse nwmTriplets to get offsets of returned entries
	var sections []*uint64
	var sectionDesc *nwmTriplet = (*nwmTriplet)(unsafe.Pointer(&responseBuffer[0]))
	for i := uint32(0); i < uint32(recHeader.number); i++ {
		offset := request.header.outputDesc.offset + uint32(unsafe.Sizeof(*recHeader)) + i*uint32(unsafe.Sizeof(*sectionDesc))
		sectionDesc = (*nwmTriplet)(unsafe.Pointer(&responseBuffer[offset]))
		for j := uint32(0); j < sectionDesc.number; j++ {
			offset = request.header.outputDesc.offset + sectionDesc.offset + j*sectionDesc.length
			sections = append(sections, (*uint64)(unsafe.Pointer(&responseBuffer[offset])))
		}
	}

	// Find nwmTCPStatsEntry in returned entries
	var tcpStats *nwmTCPStatsEntry = nil
	for _, ptr := range sections {
		switch *ptr {
		case nwmTCPStatsIdentifier:
			if tcpStats != nil {
				return nil, errnoErr(EINVAL)
			}
			tcpStats = (*nwmTCPStatsEntry)(unsafe.Pointer(ptr))
		case nwmIPStatsIdentifier:
		case nwmIPGStatsIdentifier:
		case nwmUDPStatsIdentifier:
		case nwmICMPGStatsEntry:
		case nwmICMPTStatsEntry:
		default:
			return nil, errnoErr(EINVAL)
		}
	}
	if tcpStats == nil {
		return nil, errnoErr(EINVAL)
	}

	// GetConnectionDetail EZBNMIF4 call
	responseBuffer = [4096]byte{0}
	dsa = [18]uint64{0}
	bufferAlet, reasonCode = 0, 0
	bufferLen, returnValue, returnCode = 4096, 0, 0
	nameptr := (*uint32)(unsafe.Pointer(uintptr(0x21c))) // Get jobname of current process
	nameptr = (*uint32)(unsafe.Pointer(uintptr(*nameptr + 12)))
	argv[0] = unsafe.Pointer(uintptr(*nameptr))

	request.header.ident = nwmHeaderIdentifier
	request.header.length = uint32(unsafe.Sizeof(request.header))
	request.header.version = nwmCurrentVer
	request.header.nwmType = nwmTCPConnType
	request.header.options = 0x80000000

	request.filter.ident = nwmFilterIdentifier

	var localSockaddr RawSockaddrAny
	socklen := _Socklen(SizeofSockaddrAny)
	err := getsockname(fd, &localSockaddr, &socklen)
	if err != nil {
		return nil, errnoErr(EINVAL)
	}
	if localSockaddr.Addr.Family == AF_INET {
		localSockaddr := (*RawSockaddrInet4)(unsafe.Pointer(&localSockaddr.Addr))
		localSockFilter := (*RawSockaddrInet4)(unsafe.Pointer(&request.filter.local[0]))
		localSockFilter.Family = AF_INET
		var i int
		for i = 0; i < 4; i++ {
			if localSockaddr.Addr[i] != 0 {
				break
			}
		}
		if i != 4 {
			request.filter.flags |= nwmFilterLclAddrMask
			for i = 0; i < 4; i++ {
				localSockFilter.Addr[i] = localSockaddr.Addr[i]
			}
		}
		if localSockaddr.Port != 0 {
			request.filter.flags |= nwmFilterLclPortMask
			localSockFilter.Port = localSockaddr.Port
		}
	} else if localSockaddr.Addr.Family == AF_INET6 {
		localSockaddr := (*RawSockaddrInet6)(unsafe.Pointer(&localSockaddr.Addr))
		localSockFilter := (*RawSockaddrInet6)(unsafe.Pointer(&request.filter.local[0]))
		localSockFilter.Family = AF_INET6
		var i int
		for i = 0; i < 16; i++ {
			if localSockaddr.Addr[i] != 0 {
				break
			}
		}
		if i != 16 {
			request.filter.flags |= nwmFilterLclAddrMask
			for i = 0; i < 16; i++ {
				localSockFilter.Addr[i] = localSockaddr.Addr[i]
			}
		}
		if localSockaddr.Port != 0 {
			request.filter.flags |= nwmFilterLclPortMask
			localSockFilter.Port = localSockaddr.Port
		}
	}

	svcCall(EZBNMIF4, &argv[0], &dsa[0])

	// outputDesc field is filled by EZBNMIF4 on success
	if returnCode != 0 || request.header.outputDesc.offset == 0 {
		return nil, errnoErr(EINVAL)
	}

	// Check that EZBNMIF4 returned a nwmConnEntry
	conn := (*nwmConnEntry)(unsafe.Pointer(&responseBuffer[request.header.outputDesc.offset]))
	if conn.ident != nwmTCPConnIdentifier {
		return nil, errnoErr(EINVAL)
	}

	// Copy data from the returned data structures into tcpInfo
	// Stats from nwmConnEntry are specific to that connection.
	// Stats from nwmTCPStatsEntry are global (to the interface?)
	// Fields may not be an exact match. Some fields have no equivalent.
	var tcpinfo TCPInfo
	tcpinfo.State = uint8(conn.state)
	tcpinfo.Ca_state = 0 // dummy
	tcpinfo.Retransmits = uint8(tcpStats.retransSegs)
	tcpinfo.Probes = uint8(tcpStats.outWinProbes)
	tcpinfo.Backoff = 0 // dummy
	tcpinfo.Options = 0 // dummy
	tcpinfo.Rto = tcpStats.retransTimeouts
	tcpinfo.Ato = tcpStats.outDelayAcks
	tcpinfo.Snd_mss = conn.sendMSS
	tcpinfo.Rcv_mss = conn.sendMSS // dummy
	tcpinfo.Unacked = 0            // dummy
	tcpinfo.Sacked = 0             // dummy
	tcpinfo.Lost = 0               // dummy
	tcpinfo.Retrans = conn.reXmtCount
	tcpinfo.Fackets = 0 // dummy
	tcpinfo.Last_data_sent = uint32(*(*uint64)(unsafe.Pointer(&conn.lastActivity[0])))
	tcpinfo.Last_ack_sent = uint32(*(*uint64)(unsafe.Pointer(&conn.outOldestTime[0])))
	tcpinfo.Last_data_recv = uint32(*(*uint64)(unsafe.Pointer(&conn.inOldestTime[0])))
	tcpinfo.Last_ack_recv = uint32(*(*uint64)(unsafe.Pointer(&conn.inOldestTime[0])))
	tcpinfo.Pmtu = conn.sendMSS // dummy, NWMIfRouteMtu is a candidate
	tcpinfo.Rcv_ssthresh = conn.ssThresh
	tcpinfo.Rtt = conn.roundTripTime
	tcpinfo.Rttvar = conn.roundTripVar
	tcpinfo.Snd_ssthresh = conn.ssThresh // dummy
	tcpinfo.Snd_cwnd = conn.congestionWnd
	tcpinfo.Advmss = conn.sendMSS        // dummy
	tcpinfo.Reordering = 0               // dummy
	tcpinfo.Rcv_rtt = conn.roundTripTime // dummy
	tcpinfo.Rcv_space = conn.sendMSS     // dummy
	tcpinfo.Total_retrans = conn.reXmtCount

	svcUnload(&svcNameTable[svc_EZBNMIF4][0], EZBNMIF4)

	return &tcpinfo, nil
}