mirror of https://github.com/grpc/grpc-go.git
channelz: stage 4 - add security and socket option info with appengine build tags (#2149)
This commit is contained in:
parent
8e18752766
commit
7268ca41d3
|
@ -0,0 +1,105 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sockoptToProto(skopts *channelz.SocketOptionData) []*channelzpb.SocketOption {
|
||||||
|
var opts []*channelzpb.SocketOption
|
||||||
|
if skopts.Linger != nil {
|
||||||
|
additional, err := ptypes.MarshalAny(&channelzpb.SocketOptionLinger{
|
||||||
|
Active: skopts.Linger.Onoff != 0,
|
||||||
|
Duration: convertToPtypesDuration(int64(skopts.Linger.Linger), 0),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
opts = append(opts, &channelzpb.SocketOption{
|
||||||
|
Name: "SO_LINGER",
|
||||||
|
Additional: additional,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if skopts.RecvTimeout != nil {
|
||||||
|
additional, err := ptypes.MarshalAny(&channelzpb.SocketOptionTimeout{
|
||||||
|
Duration: convertToPtypesDuration(int64(skopts.RecvTimeout.Sec), int64(skopts.RecvTimeout.Usec)),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
opts = append(opts, &channelzpb.SocketOption{
|
||||||
|
Name: "SO_RCVTIMEO",
|
||||||
|
Additional: additional,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if skopts.SendTimeout != nil {
|
||||||
|
additional, err := ptypes.MarshalAny(&channelzpb.SocketOptionTimeout{
|
||||||
|
Duration: convertToPtypesDuration(int64(skopts.SendTimeout.Sec), int64(skopts.SendTimeout.Usec)),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
opts = append(opts, &channelzpb.SocketOption{
|
||||||
|
Name: "SO_SNDTIMEO",
|
||||||
|
Additional: additional,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if skopts.TCPInfo != nil {
|
||||||
|
additional, err := ptypes.MarshalAny(&channelzpb.SocketOptionTcpInfo{
|
||||||
|
TcpiState: uint32(skopts.TCPInfo.State),
|
||||||
|
TcpiCaState: uint32(skopts.TCPInfo.Ca_state),
|
||||||
|
TcpiRetransmits: uint32(skopts.TCPInfo.Retransmits),
|
||||||
|
TcpiProbes: uint32(skopts.TCPInfo.Probes),
|
||||||
|
TcpiBackoff: uint32(skopts.TCPInfo.Backoff),
|
||||||
|
TcpiOptions: uint32(skopts.TCPInfo.Options),
|
||||||
|
// https://golang.org/pkg/syscall/#TCPInfo
|
||||||
|
// TCPInfo struct does not contain info about TcpiSndWscale and TcpiRcvWscale.
|
||||||
|
TcpiRto: skopts.TCPInfo.Rto,
|
||||||
|
TcpiAto: skopts.TCPInfo.Ato,
|
||||||
|
TcpiSndMss: skopts.TCPInfo.Snd_mss,
|
||||||
|
TcpiRcvMss: skopts.TCPInfo.Rcv_mss,
|
||||||
|
TcpiUnacked: skopts.TCPInfo.Unacked,
|
||||||
|
TcpiSacked: skopts.TCPInfo.Sacked,
|
||||||
|
TcpiLost: skopts.TCPInfo.Lost,
|
||||||
|
TcpiRetrans: skopts.TCPInfo.Retrans,
|
||||||
|
TcpiFackets: skopts.TCPInfo.Fackets,
|
||||||
|
TcpiLastDataSent: skopts.TCPInfo.Last_data_sent,
|
||||||
|
TcpiLastAckSent: skopts.TCPInfo.Last_ack_sent,
|
||||||
|
TcpiLastDataRecv: skopts.TCPInfo.Last_data_recv,
|
||||||
|
TcpiLastAckRecv: skopts.TCPInfo.Last_ack_recv,
|
||||||
|
TcpiPmtu: skopts.TCPInfo.Pmtu,
|
||||||
|
TcpiRcvSsthresh: skopts.TCPInfo.Rcv_ssthresh,
|
||||||
|
TcpiRtt: skopts.TCPInfo.Rtt,
|
||||||
|
TcpiRttvar: skopts.TCPInfo.Rttvar,
|
||||||
|
TcpiSndSsthresh: skopts.TCPInfo.Snd_ssthresh,
|
||||||
|
TcpiSndCwnd: skopts.TCPInfo.Snd_cwnd,
|
||||||
|
TcpiAdvmss: skopts.TCPInfo.Advmss,
|
||||||
|
TcpiReordering: skopts.TCPInfo.Reordering,
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
opts = append(opts, &channelzpb.SocketOption{
|
||||||
|
Name: "TCP_INFO",
|
||||||
|
Additional: additional,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return opts
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
// +build !linux appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 service
|
||||||
|
|
||||||
|
import (
|
||||||
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func sockoptToProto(skopts *channelz.SocketOptionData) []*channelzpb.SocketOption {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -23,17 +23,24 @@ package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang/protobuf/ptypes"
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
durpb "github.com/golang/protobuf/ptypes/duration"
|
||||||
wrpb "github.com/golang/protobuf/ptypes/wrappers"
|
wrpb "github.com/golang/protobuf/ptypes/wrappers"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
channelzgrpc "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
channelzgrpc "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
"google.golang.org/grpc/connectivity"
|
"google.golang.org/grpc/connectivity"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func convertToPtypesDuration(sec int64, usec int64) *durpb.Duration {
|
||||||
|
return ptypes.DurationProto(time.Duration(sec*1e9 + usec*1e3))
|
||||||
|
}
|
||||||
|
|
||||||
// RegisterChannelzServiceToServer registers the channelz service to the given server.
|
// RegisterChannelzServiceToServer registers the channelz service to the given server.
|
||||||
func RegisterChannelzServiceToServer(s *grpc.Server) {
|
func RegisterChannelzServiceToServer(s *grpc.Server) {
|
||||||
channelzgrpc.RegisterChannelzServer(s, &serverImpl{})
|
channelzgrpc.RegisterChannelzServer(s, &serverImpl{})
|
||||||
|
@ -130,6 +137,26 @@ func subChannelMetricToProto(cm *channelz.SubChannelMetric) *channelzpb.Subchann
|
||||||
return sc
|
return sc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func securityToProto(se credentials.ChannelzSecurityValue) *channelzpb.Security {
|
||||||
|
switch v := se.(type) {
|
||||||
|
case *credentials.TLSChannelzSecurityValue:
|
||||||
|
return &channelzpb.Security{Model: &channelzpb.Security_Tls_{Tls: &channelzpb.Security_Tls{
|
||||||
|
CipherSuite: &channelzpb.Security_Tls_StandardName{StandardName: v.StandardName},
|
||||||
|
LocalCertificate: v.LocalCertificate,
|
||||||
|
RemoteCertificate: v.RemoteCertificate,
|
||||||
|
}}}
|
||||||
|
case *credentials.OtherChannelzSecurityValue:
|
||||||
|
otherSecurity := &channelzpb.Security_OtherSecurity{
|
||||||
|
Name: v.Name,
|
||||||
|
}
|
||||||
|
if anyval, err := ptypes.MarshalAny(v.Value); err == nil {
|
||||||
|
otherSecurity.Value = anyval
|
||||||
|
}
|
||||||
|
return &channelzpb.Security{Model: &channelzpb.Security_Other{Other: otherSecurity}}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func addrToProto(a net.Addr) *channelzpb.Address {
|
func addrToProto(a net.Addr) *channelzpb.Address {
|
||||||
switch a.Network() {
|
switch a.Network() {
|
||||||
case "udp":
|
case "udp":
|
||||||
|
@ -177,6 +204,13 @@ func socketMetricToProto(sm *channelz.SocketMetric) *channelzpb.Socket {
|
||||||
s.Data.LocalFlowControlWindow = &wrpb.Int64Value{Value: sm.SocketData.LocalFlowControlWindow}
|
s.Data.LocalFlowControlWindow = &wrpb.Int64Value{Value: sm.SocketData.LocalFlowControlWindow}
|
||||||
s.Data.RemoteFlowControlWindow = &wrpb.Int64Value{Value: sm.SocketData.RemoteFlowControlWindow}
|
s.Data.RemoteFlowControlWindow = &wrpb.Int64Value{Value: sm.SocketData.RemoteFlowControlWindow}
|
||||||
|
|
||||||
|
if sm.SocketData.SocketOptions != nil {
|
||||||
|
s.Data.Option = sockoptToProto(sm.SocketData.SocketOptions)
|
||||||
|
}
|
||||||
|
if sm.SocketData.Security != nil {
|
||||||
|
s.Security = securityToProto(sm.SocketData.Security)
|
||||||
|
}
|
||||||
|
|
||||||
if sm.SocketData.LocalAddr != nil {
|
if sm.SocketData.LocalAddr != nil {
|
||||||
s.Local = addrToProto(sm.SocketData.LocalAddr)
|
s.Local = addrToProto(sm.SocketData.LocalAddr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,196 @@
|
||||||
|
// +build linux,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// SocketOptions is only supported on linux system. The functions defined in
|
||||||
|
// this file are to parse the socket option field and the test is specifically
|
||||||
|
// to verify the behavior of socket option parsing.
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
durpb "github.com/golang/protobuf/ptypes/duration"
|
||||||
|
"golang.org/x/net/context"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func convertToDuration(d *durpb.Duration) (sec int64, usec int64) {
|
||||||
|
if d != nil {
|
||||||
|
if dur, err := ptypes.Duration(d); err == nil {
|
||||||
|
sec = int64(int64(dur) / 1e9)
|
||||||
|
usec = (int64(dur) - sec*1e9) / 1e3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func protoToLinger(protoLinger *channelzpb.SocketOptionLinger) *unix.Linger {
|
||||||
|
linger := &unix.Linger{}
|
||||||
|
if protoLinger.GetActive() {
|
||||||
|
linger.Onoff = 1
|
||||||
|
}
|
||||||
|
lv, _ := convertToDuration(protoLinger.GetDuration())
|
||||||
|
linger.Linger = int32(lv)
|
||||||
|
return linger
|
||||||
|
}
|
||||||
|
|
||||||
|
func protoToSocketOption(skopts []*channelzpb.SocketOption) *channelz.SocketOptionData {
|
||||||
|
skdata := &channelz.SocketOptionData{}
|
||||||
|
for _, opt := range skopts {
|
||||||
|
switch opt.GetName() {
|
||||||
|
case "SO_LINGER":
|
||||||
|
protoLinger := &channelzpb.SocketOptionLinger{}
|
||||||
|
err := ptypes.UnmarshalAny(opt.GetAdditional(), protoLinger)
|
||||||
|
if err == nil {
|
||||||
|
skdata.Linger = protoToLinger(protoLinger)
|
||||||
|
}
|
||||||
|
case "SO_RCVTIMEO":
|
||||||
|
protoTimeout := &channelzpb.SocketOptionTimeout{}
|
||||||
|
err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout)
|
||||||
|
if err == nil {
|
||||||
|
skdata.RecvTimeout = protoToTime(protoTimeout)
|
||||||
|
}
|
||||||
|
case "SO_SNDTIMEO":
|
||||||
|
protoTimeout := &channelzpb.SocketOptionTimeout{}
|
||||||
|
err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout)
|
||||||
|
if err == nil {
|
||||||
|
skdata.SendTimeout = protoToTime(protoTimeout)
|
||||||
|
}
|
||||||
|
case "TCP_INFO":
|
||||||
|
tcpi := &channelzpb.SocketOptionTcpInfo{}
|
||||||
|
err := ptypes.UnmarshalAny(opt.GetAdditional(), tcpi)
|
||||||
|
if err == nil {
|
||||||
|
skdata.TCPInfo = &unix.TCPInfo{
|
||||||
|
State: uint8(tcpi.TcpiState),
|
||||||
|
Ca_state: uint8(tcpi.TcpiCaState),
|
||||||
|
Retransmits: uint8(tcpi.TcpiRetransmits),
|
||||||
|
Probes: uint8(tcpi.TcpiProbes),
|
||||||
|
Backoff: uint8(tcpi.TcpiBackoff),
|
||||||
|
Options: uint8(tcpi.TcpiOptions),
|
||||||
|
Rto: tcpi.TcpiRto,
|
||||||
|
Ato: tcpi.TcpiAto,
|
||||||
|
Snd_mss: tcpi.TcpiSndMss,
|
||||||
|
Rcv_mss: tcpi.TcpiRcvMss,
|
||||||
|
Unacked: tcpi.TcpiUnacked,
|
||||||
|
Sacked: tcpi.TcpiSacked,
|
||||||
|
Lost: tcpi.TcpiLost,
|
||||||
|
Retrans: tcpi.TcpiRetrans,
|
||||||
|
Fackets: tcpi.TcpiFackets,
|
||||||
|
Last_data_sent: tcpi.TcpiLastDataSent,
|
||||||
|
Last_ack_sent: tcpi.TcpiLastAckSent,
|
||||||
|
Last_data_recv: tcpi.TcpiLastDataRecv,
|
||||||
|
Last_ack_recv: tcpi.TcpiLastAckRecv,
|
||||||
|
Pmtu: tcpi.TcpiPmtu,
|
||||||
|
Rcv_ssthresh: tcpi.TcpiRcvSsthresh,
|
||||||
|
Rtt: tcpi.TcpiRtt,
|
||||||
|
Rttvar: tcpi.TcpiRttvar,
|
||||||
|
Snd_ssthresh: tcpi.TcpiSndSsthresh,
|
||||||
|
Snd_cwnd: tcpi.TcpiSndCwnd,
|
||||||
|
Advmss: tcpi.TcpiAdvmss,
|
||||||
|
Reordering: tcpi.TcpiReordering}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return skdata
|
||||||
|
}
|
||||||
|
|
||||||
|
func socketProtoToStruct(s *channelzpb.Socket) *dummySocket {
|
||||||
|
ds := &dummySocket{}
|
||||||
|
pdata := s.GetData()
|
||||||
|
ds.streamsStarted = pdata.GetStreamsStarted()
|
||||||
|
ds.streamsSucceeded = pdata.GetStreamsSucceeded()
|
||||||
|
ds.streamsFailed = pdata.GetStreamsFailed()
|
||||||
|
ds.messagesSent = pdata.GetMessagesSent()
|
||||||
|
ds.messagesReceived = pdata.GetMessagesReceived()
|
||||||
|
ds.keepAlivesSent = pdata.GetKeepAlivesSent()
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastLocalStreamCreatedTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastLocalStreamCreatedTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastRemoteStreamCreatedTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastRemoteStreamCreatedTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastMessageSentTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastMessageSentTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastMessageReceivedTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastMessageReceivedTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v := pdata.GetLocalFlowControlWindow(); v != nil {
|
||||||
|
ds.localFlowControlWindow = v.Value
|
||||||
|
}
|
||||||
|
if v := pdata.GetRemoteFlowControlWindow(); v != nil {
|
||||||
|
ds.remoteFlowControlWindow = v.Value
|
||||||
|
}
|
||||||
|
if v := pdata.GetOption(); v != nil {
|
||||||
|
ds.socketOptions = protoToSocketOption(v)
|
||||||
|
}
|
||||||
|
if v := s.GetSecurity(); v != nil {
|
||||||
|
ds.security = protoToSecurity(v)
|
||||||
|
}
|
||||||
|
if local := s.GetLocal(); local != nil {
|
||||||
|
ds.localAddr = protoToAddr(local)
|
||||||
|
}
|
||||||
|
if remote := s.GetRemote(); remote != nil {
|
||||||
|
ds.remoteAddr = protoToAddr(remote)
|
||||||
|
}
|
||||||
|
ds.remoteName = s.GetRemoteName()
|
||||||
|
return ds
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSocketOptions(t *testing.T) {
|
||||||
|
channelz.NewChannelzStorage()
|
||||||
|
ss := []*dummySocket{
|
||||||
|
{
|
||||||
|
socketOptions: &channelz.SocketOptionData{
|
||||||
|
Linger: &unix.Linger{Onoff: 1, Linger: 2},
|
||||||
|
RecvTimeout: &unix.Timeval{Sec: 10, Usec: 1},
|
||||||
|
SendTimeout: &unix.Timeval{},
|
||||||
|
TCPInfo: &unix.TCPInfo{State: 1},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
svr := newCZServer()
|
||||||
|
ids := make([]int64, len(ss))
|
||||||
|
svrID := channelz.RegisterServer(&dummyServer{}, "")
|
||||||
|
for i, s := range ss {
|
||||||
|
ids[i] = channelz.RegisterNormalSocket(s, svrID, strconv.Itoa(i))
|
||||||
|
}
|
||||||
|
for i, s := range ss {
|
||||||
|
resp, _ := svr.GetSocket(context.Background(), &channelzpb.GetSocketRequest{SocketId: ids[i]})
|
||||||
|
metrics := resp.GetSocket()
|
||||||
|
if !reflect.DeepEqual(metrics.GetRef(), &channelzpb.SocketRef{SocketId: ids[i], Name: strconv.Itoa(i)}) || !reflect.DeepEqual(socketProtoToStruct(metrics), s) {
|
||||||
|
t.Fatalf("resp.GetSocket() want: metrics.GetRef() = %#v and %#v, got: metrics.GetRef() = %#v and %#v", &channelzpb.SocketRef{SocketId: ids[i], Name: strconv.Itoa(i)}, s, metrics.GetRef(), socketProtoToStruct(metrics))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
// +build !linux appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Non-linux system does not support socket option. Therefore, the function
|
||||||
|
// socketProtoToStruct defined in this file skips the parsing of socket option field.
|
||||||
|
|
||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/golang/protobuf/ptypes"
|
||||||
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func socketProtoToStruct(s *channelzpb.Socket) *dummySocket {
|
||||||
|
ds := &dummySocket{}
|
||||||
|
pdata := s.GetData()
|
||||||
|
ds.streamsStarted = pdata.GetStreamsStarted()
|
||||||
|
ds.streamsSucceeded = pdata.GetStreamsSucceeded()
|
||||||
|
ds.streamsFailed = pdata.GetStreamsFailed()
|
||||||
|
ds.messagesSent = pdata.GetMessagesSent()
|
||||||
|
ds.messagesReceived = pdata.GetMessagesReceived()
|
||||||
|
ds.keepAlivesSent = pdata.GetKeepAlivesSent()
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastLocalStreamCreatedTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastLocalStreamCreatedTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastRemoteStreamCreatedTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastRemoteStreamCreatedTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastMessageSentTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastMessageSentTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if t, err := ptypes.Timestamp(pdata.GetLastMessageReceivedTimestamp()); err == nil {
|
||||||
|
if !t.Equal(emptyTime) {
|
||||||
|
ds.lastMessageReceivedTimestamp = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if v := pdata.GetLocalFlowControlWindow(); v != nil {
|
||||||
|
ds.localFlowControlWindow = v.Value
|
||||||
|
}
|
||||||
|
if v := pdata.GetRemoteFlowControlWindow(); v != nil {
|
||||||
|
ds.remoteFlowControlWindow = v.Value
|
||||||
|
}
|
||||||
|
if v := s.GetSecurity(); v != nil {
|
||||||
|
ds.security = protoToSecurity(v)
|
||||||
|
}
|
||||||
|
if local := s.GetLocal(); local != nil {
|
||||||
|
ds.localAddr = protoToAddr(local)
|
||||||
|
}
|
||||||
|
if remote := s.GetRemote(); remote != nil {
|
||||||
|
ds.remoteAddr = protoToAddr(remote)
|
||||||
|
}
|
||||||
|
ds.remoteName = s.GetRemoteName()
|
||||||
|
return ds
|
||||||
|
}
|
|
@ -25,10 +25,12 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
"github.com/golang/protobuf/ptypes"
|
"github.com/golang/protobuf/ptypes"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
"google.golang.org/grpc/connectivity"
|
"google.golang.org/grpc/connectivity"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/internal/channelz"
|
"google.golang.org/grpc/internal/channelz"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -92,10 +94,10 @@ type dummySocket struct {
|
||||||
lastMessageReceivedTimestamp time.Time
|
lastMessageReceivedTimestamp time.Time
|
||||||
localFlowControlWindow int64
|
localFlowControlWindow int64
|
||||||
remoteFlowControlWindow int64
|
remoteFlowControlWindow int64
|
||||||
//socket options
|
socketOptions *channelz.SocketOptionData
|
||||||
localAddr net.Addr
|
localAddr net.Addr
|
||||||
remoteAddr net.Addr
|
remoteAddr net.Addr
|
||||||
// Security
|
security credentials.ChannelzSecurityValue
|
||||||
remoteName string
|
remoteName string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,10 +115,10 @@ func (d *dummySocket) ChannelzMetric() *channelz.SocketInternalMetric {
|
||||||
LastMessageReceivedTimestamp: d.lastMessageReceivedTimestamp,
|
LastMessageReceivedTimestamp: d.lastMessageReceivedTimestamp,
|
||||||
LocalFlowControlWindow: d.localFlowControlWindow,
|
LocalFlowControlWindow: d.localFlowControlWindow,
|
||||||
RemoteFlowControlWindow: d.remoteFlowControlWindow,
|
RemoteFlowControlWindow: d.remoteFlowControlWindow,
|
||||||
//socket options
|
SocketOptions: d.socketOptions,
|
||||||
LocalAddr: d.localAddr,
|
LocalAddr: d.localAddr,
|
||||||
RemoteAddr: d.remoteAddr,
|
RemoteAddr: d.remoteAddr,
|
||||||
// Security
|
Security: d.security,
|
||||||
RemoteName: d.remoteName,
|
RemoteName: d.remoteName,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -164,6 +166,21 @@ func serverProtoToStruct(s *channelzpb.Server) *dummyServer {
|
||||||
return ds
|
return ds
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func protoToSecurity(protoSecurity *channelzpb.Security) credentials.ChannelzSecurityValue {
|
||||||
|
switch v := protoSecurity.Model.(type) {
|
||||||
|
case *channelzpb.Security_Tls_:
|
||||||
|
return &credentials.TLSChannelzSecurityValue{StandardName: v.Tls.GetStandardName(), LocalCertificate: v.Tls.GetLocalCertificate(), RemoteCertificate: v.Tls.GetRemoteCertificate()}
|
||||||
|
case *channelzpb.Security_Other:
|
||||||
|
sv := &credentials.OtherChannelzSecurityValue{Name: v.Other.GetName()}
|
||||||
|
var x ptypes.DynamicAny
|
||||||
|
if err := ptypes.UnmarshalAny(v.Other.GetValue(), &x); err == nil {
|
||||||
|
sv.Value = x.Message
|
||||||
|
}
|
||||||
|
return sv
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func protoToAddr(a *channelzpb.Address) net.Addr {
|
func protoToAddr(a *channelzpb.Address) net.Addr {
|
||||||
switch v := a.Address.(type) {
|
switch v := a.Address.(type) {
|
||||||
case *channelzpb.Address_TcpipAddress:
|
case *channelzpb.Address_TcpipAddress:
|
||||||
|
@ -179,51 +196,6 @@ func protoToAddr(a *channelzpb.Address) net.Addr {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func socketProtoToStruct(s *channelzpb.Socket) *dummySocket {
|
|
||||||
ds := &dummySocket{}
|
|
||||||
pdata := s.GetData()
|
|
||||||
ds.streamsStarted = pdata.GetStreamsStarted()
|
|
||||||
ds.streamsSucceeded = pdata.GetStreamsSucceeded()
|
|
||||||
ds.streamsFailed = pdata.GetStreamsFailed()
|
|
||||||
ds.messagesSent = pdata.GetMessagesSent()
|
|
||||||
ds.messagesReceived = pdata.GetMessagesReceived()
|
|
||||||
ds.keepAlivesSent = pdata.GetKeepAlivesSent()
|
|
||||||
if t, err := ptypes.Timestamp(pdata.GetLastLocalStreamCreatedTimestamp()); err == nil {
|
|
||||||
if !t.Equal(emptyTime) {
|
|
||||||
ds.lastLocalStreamCreatedTimestamp = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t, err := ptypes.Timestamp(pdata.GetLastRemoteStreamCreatedTimestamp()); err == nil {
|
|
||||||
if !t.Equal(emptyTime) {
|
|
||||||
ds.lastRemoteStreamCreatedTimestamp = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t, err := ptypes.Timestamp(pdata.GetLastMessageSentTimestamp()); err == nil {
|
|
||||||
if !t.Equal(emptyTime) {
|
|
||||||
ds.lastMessageSentTimestamp = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if t, err := ptypes.Timestamp(pdata.GetLastMessageReceivedTimestamp()); err == nil {
|
|
||||||
if !t.Equal(emptyTime) {
|
|
||||||
ds.lastMessageReceivedTimestamp = t
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if v := pdata.GetLocalFlowControlWindow(); v != nil {
|
|
||||||
ds.localFlowControlWindow = v.Value
|
|
||||||
}
|
|
||||||
if v := pdata.GetRemoteFlowControlWindow(); v != nil {
|
|
||||||
ds.remoteFlowControlWindow = v.Value
|
|
||||||
}
|
|
||||||
if local := s.GetLocal(); local != nil {
|
|
||||||
ds.localAddr = protoToAddr(local)
|
|
||||||
}
|
|
||||||
if remote := s.GetRemote(); remote != nil {
|
|
||||||
ds.remoteAddr = protoToAddr(remote)
|
|
||||||
}
|
|
||||||
ds.remoteName = s.GetRemoteName()
|
|
||||||
return ds
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertSocketRefSliceToMap(sktRefs []*channelzpb.SocketRef) map[int64]string {
|
func convertSocketRefSliceToMap(sktRefs []*channelzpb.SocketRef) map[int64]string {
|
||||||
m := make(map[int64]string)
|
m := make(map[int64]string)
|
||||||
for _, sr := range sktRefs {
|
for _, sr := range sktRefs {
|
||||||
|
@ -232,6 +204,20 @@ func convertSocketRefSliceToMap(sktRefs []*channelzpb.SocketRef) map[int64]strin
|
||||||
return m
|
return m
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OtherSecurityValue struct {
|
||||||
|
LocalCertificate []byte `protobuf:"bytes,1,opt,name=local_certificate,json=localCertificate,proto3" json:"local_certificate,omitempty"`
|
||||||
|
RemoteCertificate []byte `protobuf:"bytes,2,opt,name=remote_certificate,json=remoteCertificate,proto3" json:"remote_certificate,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *OtherSecurityValue) Reset() { *m = OtherSecurityValue{} }
|
||||||
|
func (m *OtherSecurityValue) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*OtherSecurityValue) ProtoMessage() {}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Ad-hoc registering the proto type here to facilitate UnmarshalAny of OtherSecurityValue.
|
||||||
|
proto.RegisterType((*OtherSecurityValue)(nil), "grpc.credentials.OtherChannelzSecurityValue")
|
||||||
|
}
|
||||||
|
|
||||||
func TestGetTopChannels(t *testing.T) {
|
func TestGetTopChannels(t *testing.T) {
|
||||||
tcs := []*dummyChannel{
|
tcs := []*dummyChannel{
|
||||||
{
|
{
|
||||||
|
@ -460,6 +446,23 @@ func TestGetSocket(t *testing.T) {
|
||||||
{
|
{
|
||||||
localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 10001},
|
localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 10001},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
security: &credentials.TLSChannelzSecurityValue{
|
||||||
|
StandardName: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
RemoteCertificate: []byte{48, 130, 2, 156, 48, 130, 2, 5, 160},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
security: &credentials.OtherChannelzSecurityValue{
|
||||||
|
Name: "XXXX",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
security: &credentials.OtherChannelzSecurityValue{
|
||||||
|
Name: "YYYY",
|
||||||
|
Value: &OtherSecurityValue{LocalCertificate: []byte{1, 2, 3}, RemoteCertificate: []byte{4, 5, 6}},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
svr := newCZServer()
|
svr := newCZServer()
|
||||||
ids := make([]int64, len(ss))
|
ids := make([]int64, len(ss))
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
// +build 386,linux,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func protoToTime(protoTime *channelzpb.SocketOptionTimeout) *unix.Timeval {
|
||||||
|
timeout := &unix.Timeval{}
|
||||||
|
sec, usec := convertToDuration(protoTime.GetDuration())
|
||||||
|
timeout.Sec, timeout.Usec = int32(sec), int32(usec)
|
||||||
|
return timeout
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// +build amd64,linux,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func protoToTime(protoTime *channelzpb.SocketOptionTimeout) *unix.Timeval {
|
||||||
|
timeout := &unix.Timeval{}
|
||||||
|
timeout.Sec, timeout.Usec = convertToDuration(protoTime.GetDuration())
|
||||||
|
return timeout
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SocketOptionData defines the struct to hold socket option data, and related
|
||||||
|
// getter function to obtain info from fd.
|
||||||
|
type SocketOptionData struct {
|
||||||
|
Linger *unix.Linger
|
||||||
|
RecvTimeout *unix.Timeval
|
||||||
|
SendTimeout *unix.Timeval
|
||||||
|
TCPInfo *unix.TCPInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getsockopt defines the function to get socket options requested by channelz.
|
||||||
|
// It is to be passed to syscall.RawConn.Control().
|
||||||
|
func (s *SocketOptionData) Getsockopt(fd uintptr) {
|
||||||
|
if v, err := unix.GetsockoptLinger(int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER); err == nil {
|
||||||
|
s.Linger = v
|
||||||
|
}
|
||||||
|
if v, err := unix.GetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO); err == nil {
|
||||||
|
s.RecvTimeout = v
|
||||||
|
}
|
||||||
|
if v, err := unix.GetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO); err == nil {
|
||||||
|
s.SendTimeout = v
|
||||||
|
}
|
||||||
|
if v, err := unix.GetsockoptTCPInfo(int(fd), syscall.SOL_TCP, syscall.TCP_INFO); err == nil {
|
||||||
|
s.TCPInfo = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// +build !linux appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
import "google.golang.org/grpc/grpclog"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
grpclog.Infof("Channelz: socket options are not supported on non-linux os and appengine.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SocketOptionData defines the struct to hold socket option data, and related
|
||||||
|
// getter function to obtain info from fd.
|
||||||
|
// Windows OS doesn't support Socket Option
|
||||||
|
type SocketOptionData struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getsockopt defines the function to get socket options requested by channelz.
|
||||||
|
// It is to be passed to syscall.RawConn.Control().
|
||||||
|
// Windows OS doesn't support Socket Option
|
||||||
|
func (s *SocketOptionData) Getsockopt(fd uintptr) {}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// +build linux,go1.9,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSocketOption gets the socket option info of the conn.
|
||||||
|
func GetSocketOption(socket interface{}) *SocketOptionData {
|
||||||
|
c, ok := socket.(syscall.Conn)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data := &SocketOptionData{}
|
||||||
|
if rawConn, err := c.SyscallConn(); err == nil {
|
||||||
|
rawConn.Control(data.Getsockopt)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// +build !linux !go1.9 appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
// GetSocketOption gets the socket option info of the conn.
|
||||||
|
func GetSocketOption(c interface{}) *SocketOptionData {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
// +build linux,go1.10,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The test in this file should be run in an environment that has go1.10 or later,
|
||||||
|
// as the function SyscallConn() (required to get socket option) was introduced
|
||||||
|
// to net.TCPListener in go1.10.
|
||||||
|
|
||||||
|
package channelz_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetSocketOpt(t *testing.T) {
|
||||||
|
network, addr := "tcp", ":0"
|
||||||
|
ln, err := net.Listen(network, addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("net.Listen(%s,%s) failed with err: %v", network, addr, err)
|
||||||
|
}
|
||||||
|
defer ln.Close()
|
||||||
|
go func() {
|
||||||
|
ln.Accept()
|
||||||
|
}()
|
||||||
|
conn, _ := net.Dial(network, ln.Addr().String())
|
||||||
|
defer conn.Close()
|
||||||
|
tcpc := conn.(*net.TCPConn)
|
||||||
|
raw, err := tcpc.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SyscallConn() failed due to %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &unix.Linger{Onoff: 1, Linger: 5}
|
||||||
|
recvTimout := &unix.Timeval{Sec: 100}
|
||||||
|
sendTimeout := &unix.Timeval{Sec: 8888}
|
||||||
|
raw.Control(func(fd uintptr) {
|
||||||
|
var err error
|
||||||
|
err = unix.SetsockoptLinger(int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, l)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to SetsockoptLinger(%v,%v,%v,%v) due to %v", int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, l, err)
|
||||||
|
}
|
||||||
|
err = unix.SetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, recvTimout)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to SetsockoptTimeval(%v,%v,%v,%v) due to %v", int(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, recvTimout, err)
|
||||||
|
}
|
||||||
|
err = unix.SetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, sendTimeout)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to SetsockoptTimeval(%v,%v,%v,%v) due to %v", int(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, sendTimeout, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
sktopt := channelz.GetSocketOption(conn)
|
||||||
|
if !reflect.DeepEqual(sktopt.Linger, l) {
|
||||||
|
t.Fatalf("get socket option linger, want: %v, got %v", l, sktopt.Linger)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(sktopt.RecvTimeout, recvTimout) {
|
||||||
|
t.Logf("get socket option recv timeout, want: %v, got %v, may be caused by system allowing non or partial setting of this value", recvTimout, sktopt.RecvTimeout)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(sktopt.SendTimeout, sendTimeout) {
|
||||||
|
t.Logf("get socket option send timeout, want: %v, got %v, may be caused by system allowing non or partial setting of this value", sendTimeout, sktopt.SendTimeout)
|
||||||
|
}
|
||||||
|
if sktopt == nil || sktopt.TCPInfo != nil && sktopt.TCPInfo.State != 1 {
|
||||||
|
t.Fatalf("TCPInfo.State want 1 (TCP_ESTABLISHED), got %v", sktopt)
|
||||||
|
}
|
||||||
|
|
||||||
|
sktopt = channelz.GetSocketOption(ln)
|
||||||
|
if sktopt == nil || sktopt.TCPInfo == nil || sktopt.TCPInfo.State != 10 {
|
||||||
|
t.Fatalf("TCPInfo.State want 10 (TCP_LISTEN), got %v", sktopt)
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -118,6 +119,18 @@ func (t TLSInfo) AuthType() string {
|
||||||
return "tls"
|
return "tls"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChannelzSecurityValue returns security info requested by channelz.
|
||||||
|
func (t TLSInfo) GetChannelzSecurityValue() ChannelzSecurityValue {
|
||||||
|
v := &TLSChannelzSecurityValue{
|
||||||
|
StandardName: cipherSuiteLookup[t.State.CipherSuite],
|
||||||
|
}
|
||||||
|
// Currently there's no way to get LocalCertificate info from tls package.
|
||||||
|
if len(t.State.PeerCertificates) > 0 {
|
||||||
|
v.RemoteCertificate = t.State.PeerCertificates[0].Raw
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
// tlsCreds is the credentials required for authenticating a connection using TLS.
|
// tlsCreds is the credentials required for authenticating a connection using TLS.
|
||||||
type tlsCreds struct {
|
type tlsCreds struct {
|
||||||
// TLS configuration
|
// TLS configuration
|
||||||
|
@ -155,7 +168,7 @@ func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawCon
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
return nil, nil, ctx.Err()
|
return nil, nil, ctx.Err()
|
||||||
}
|
}
|
||||||
return conn, TLSInfo{conn.ConnectionState()}, nil
|
return tlsConn{Conn: conn, rawConn: rawConn}, TLSInfo{conn.ConnectionState()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) {
|
||||||
|
@ -163,7 +176,7 @@ func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error)
|
||||||
if err := conn.Handshake(); err != nil {
|
if err := conn.Handshake(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
return conn, TLSInfo{conn.ConnectionState()}, nil
|
return tlsConn{Conn: conn, rawConn: rawConn}, TLSInfo{conn.ConnectionState()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *tlsCreds) Clone() TransportCredentials {
|
func (c *tlsCreds) Clone() TransportCredentials {
|
||||||
|
@ -218,3 +231,37 @@ func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error
|
||||||
}
|
}
|
||||||
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
|
return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ChannelzSecurityInfo defines the interface that security protocols should implement
|
||||||
|
// in order to provide security info to channelz.
|
||||||
|
type ChannelzSecurityInfo interface {
|
||||||
|
GetSecurityValue() ChannelzSecurityValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ChannelzSecurityValue defines the interface that GetSecurityValue() return value
|
||||||
|
// should satisfy. This interface should only be satisfied by *TLSChannelzSecurityValue
|
||||||
|
// and *OtherChannelzSecurityValue.
|
||||||
|
type ChannelzSecurityValue interface {
|
||||||
|
isChannelzSecurityValue()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TLSChannelzSecurityValue defines the struct that TLS protocol should return
|
||||||
|
// from GetSecurityValue(), containing security info like cipher and certificate used.
|
||||||
|
type TLSChannelzSecurityValue struct {
|
||||||
|
StandardName string
|
||||||
|
LocalCertificate []byte
|
||||||
|
RemoteCertificate []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*TLSChannelzSecurityValue) isChannelzSecurityValue() {}
|
||||||
|
|
||||||
|
// OtherChannelzSecurityValue defines the struct that non-TLS protocol should return
|
||||||
|
// from GetSecurityValue(), which contains protocol specific security info. Note
|
||||||
|
// the Value field will be sent to users of channelz requesting channel info, and
|
||||||
|
// thus sensitive info should better be avoided.
|
||||||
|
type OtherChannelzSecurityValue struct {
|
||||||
|
Name string
|
||||||
|
Value proto.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (*OtherChannelzSecurityValue) isChannelzSecurityValue() {}
|
||||||
|
|
|
@ -24,6 +24,32 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var cipherSuiteLookup = map[uint16]string{
|
||||||
|
tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA",
|
||||||
|
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_RSA_WITH_AES_128_CBC_SHA256: "TLS_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
|
||||||
|
tls.TLS_FALLBACK_SCSV: "TLS_FALLBACK_SCSV",
|
||||||
|
}
|
||||||
|
|
||||||
// cloneTLSConfig returns a shallow clone of the exported
|
// cloneTLSConfig returns a shallow clone of the exported
|
||||||
// fields of cfg, ignoring the unexported sync.Once, which
|
// fields of cfg, ignoring the unexported sync.Once, which
|
||||||
// contains a mutex and must not be copied.
|
// contains a mutex and must not be copied.
|
||||||
|
|
|
@ -0,0 +1,42 @@
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"errors"
|
||||||
|
"net"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tlsConn struct {
|
||||||
|
*tls.Conn
|
||||||
|
rawConn net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
// implements the syscall.Conn interface
|
||||||
|
func (c tlsConn) SyscallConn() (syscall.RawConn, error) {
|
||||||
|
conn, ok := c.rawConn.(syscall.Conn)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("RawConn does not implement syscall.Conn")
|
||||||
|
}
|
||||||
|
return conn.SyscallConn()
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// +build !go1.8
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 credentials
|
||||||
|
|
||||||
|
import "crypto/tls"
|
||||||
|
|
||||||
|
var cipherSuiteLookup = map[uint16]string{
|
||||||
|
tls.TLS_RSA_WITH_RC4_128_SHA: "TLS_RSA_WITH_RC4_128_SHA",
|
||||||
|
tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
tls.TLS_RSA_WITH_AES_128_CBC_SHA: "TLS_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_RSA_WITH_AES_256_CBC_SHA: "TLS_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_RSA_WITH_AES_128_GCM_SHA256: "TLS_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_RSA_WITH_AES_256_GCM_SHA384: "TLS_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA: "TLS_ECDHE_RSA_WITH_RC4_128_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: "TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256: "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
|
||||||
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384: "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384",
|
||||||
|
tls.TLS_FALLBACK_SCSV: "TLS_FALLBACK_SCSV",
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
// +build !go1.9
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 credentials
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tlsConn struct {
|
||||||
|
*tls.Conn
|
||||||
|
rawConn net.Conn
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"google.golang.org/grpc/connectivity"
|
"google.golang.org/grpc/connectivity"
|
||||||
|
"google.golang.org/grpc/credentials"
|
||||||
"google.golang.org/grpc/grpclog"
|
"google.golang.org/grpc/grpclog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -282,8 +283,8 @@ type SocketInternalMetric struct {
|
||||||
// Optional, represents the name of the remote endpoint, if different than
|
// Optional, represents the name of the remote endpoint, if different than
|
||||||
// the original target name.
|
// the original target name.
|
||||||
RemoteName string
|
RemoteName string
|
||||||
//TODO: socket options
|
SocketOptions *SocketOptionData
|
||||||
//TODO: Security
|
Security credentials.ChannelzSecurityValue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Socket is the interface that should be satisfied in order to be tracked by
|
// Socket is the interface that should be satisfied in order to be tracked by
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
// +build !appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SocketOptionData defines the struct to hold socket option data, and related
|
||||||
|
// getter function to obtain info from fd.
|
||||||
|
type SocketOptionData struct {
|
||||||
|
Linger *unix.Linger
|
||||||
|
RecvTimeout *unix.Timeval
|
||||||
|
SendTimeout *unix.Timeval
|
||||||
|
TCPInfo *unix.TCPInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getsockopt defines the function to get socket options requested by channelz.
|
||||||
|
// It is to be passed to syscall.RawConn.Control().
|
||||||
|
func (s *SocketOptionData) Getsockopt(fd uintptr) {
|
||||||
|
if v, err := unix.GetsockoptLinger(int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER); err == nil {
|
||||||
|
s.Linger = v
|
||||||
|
}
|
||||||
|
if v, err := unix.GetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO); err == nil {
|
||||||
|
s.RecvTimeout = v
|
||||||
|
}
|
||||||
|
if v, err := unix.GetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO); err == nil {
|
||||||
|
s.SendTimeout = v
|
||||||
|
}
|
||||||
|
if v, err := unix.GetsockoptTCPInfo(int(fd), syscall.SOL_TCP, syscall.TCP_INFO); err == nil {
|
||||||
|
s.TCPInfo = v
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
// +build !linux appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
import "google.golang.org/grpc/grpclog"
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
grpclog.Infof("Channelz: socket options are not supported on non-linux os and appengine.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// SocketOptionData defines the struct to hold socket option data, and related
|
||||||
|
// getter function to obtain info from fd.
|
||||||
|
// Windows OS doesn't support Socket Option
|
||||||
|
type SocketOptionData struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getsockopt defines the function to get socket options requested by channelz.
|
||||||
|
// It is to be passed to syscall.RawConn.Control().
|
||||||
|
// Windows OS doesn't support Socket Option
|
||||||
|
func (s *SocketOptionData) Getsockopt(fd uintptr) {}
|
|
@ -0,0 +1,39 @@
|
||||||
|
// +build linux,go1.9,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSocketOption gets the socket option info of the conn.
|
||||||
|
func GetSocketOption(socket interface{}) *SocketOptionData {
|
||||||
|
c, ok := socket.(syscall.Conn)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data := &SocketOptionData{}
|
||||||
|
if rawConn, err := c.SyscallConn(); err == nil {
|
||||||
|
rawConn.Control(data.Getsockopt)
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
// +build !linux !go1.9 appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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 channelz
|
||||||
|
|
||||||
|
// GetSocketOption gets the socket option info of the conn.
|
||||||
|
func GetSocketOption(c interface{}) *SocketOptionData {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,91 @@
|
||||||
|
// +build linux,go1.10,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The test in this file should be run in an environment that has go1.10 or later,
|
||||||
|
// as the function SyscallConn() (required to get socket option) was introduced
|
||||||
|
// to net.TCPListener in go1.10.
|
||||||
|
|
||||||
|
package channelz_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"reflect"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGetSocketOpt(t *testing.T) {
|
||||||
|
network, addr := "tcp", ":0"
|
||||||
|
ln, err := net.Listen(network, addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("net.Listen(%s,%s) failed with err: %v", network, addr, err)
|
||||||
|
}
|
||||||
|
defer ln.Close()
|
||||||
|
go func() {
|
||||||
|
ln.Accept()
|
||||||
|
}()
|
||||||
|
conn, _ := net.Dial(network, ln.Addr().String())
|
||||||
|
defer conn.Close()
|
||||||
|
tcpc := conn.(*net.TCPConn)
|
||||||
|
raw, err := tcpc.SyscallConn()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("SyscallConn() failed due to %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l := &unix.Linger{Onoff: 1, Linger: 5}
|
||||||
|
recvTimout := &unix.Timeval{Sec: 100}
|
||||||
|
sendTimeout := &unix.Timeval{Sec: 8888}
|
||||||
|
raw.Control(func(fd uintptr) {
|
||||||
|
var err error
|
||||||
|
err = unix.SetsockoptLinger(int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, l)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to SetsockoptLinger(%v,%v,%v,%v) due to %v", int(fd), syscall.SOL_SOCKET, syscall.SO_LINGER, l, err)
|
||||||
|
}
|
||||||
|
err = unix.SetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, recvTimout)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to SetsockoptTimeval(%v,%v,%v,%v) due to %v", int(fd), syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, recvTimout, err)
|
||||||
|
}
|
||||||
|
err = unix.SetsockoptTimeval(int(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, sendTimeout)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to SetsockoptTimeval(%v,%v,%v,%v) due to %v", int(fd), syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, sendTimeout, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
sktopt := channelz.GetSocketOption(conn)
|
||||||
|
if !reflect.DeepEqual(sktopt.Linger, l) {
|
||||||
|
t.Fatalf("get socket option linger, want: %v, got %v", l, sktopt.Linger)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(sktopt.RecvTimeout, recvTimout) {
|
||||||
|
t.Logf("get socket option recv timeout, want: %v, got %v, may be caused by system allowing non or partial setting of this value", recvTimout, sktopt.RecvTimeout)
|
||||||
|
}
|
||||||
|
if !reflect.DeepEqual(sktopt.SendTimeout, sendTimeout) {
|
||||||
|
t.Logf("get socket option send timeout, want: %v, got %v, may be caused by system allowing non or partial setting of this value", sendTimeout, sktopt.SendTimeout)
|
||||||
|
}
|
||||||
|
if sktopt == nil || sktopt.TCPInfo != nil && sktopt.TCPInfo.State != 1 {
|
||||||
|
t.Fatalf("TCPInfo.State want 1 (TCP_ESTABLISHED), got %v", sktopt)
|
||||||
|
}
|
||||||
|
|
||||||
|
sktopt = channelz.GetSocketOption(ln)
|
||||||
|
if sktopt == nil || sktopt.TCPInfo == nil || sktopt.TCPInfo.State != 10 {
|
||||||
|
t.Fatalf("TCPInfo.State want 10 (TCP_LISTEN), got %v", sktopt)
|
||||||
|
}
|
||||||
|
}
|
|
@ -481,6 +481,7 @@ type listenSocket struct {
|
||||||
|
|
||||||
func (l *listenSocket) ChannelzMetric() *channelz.SocketInternalMetric {
|
func (l *listenSocket) ChannelzMetric() *channelz.SocketInternalMetric {
|
||||||
return &channelz.SocketInternalMetric{
|
return &channelz.SocketInternalMetric{
|
||||||
|
SocketOptions: channelz.GetSocketOption(l.Listener),
|
||||||
LocalAddr: l.Listener.Addr(),
|
LocalAddr: l.Listener.Addr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
// +build go1.10,linux,!appengine
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* Copyright 2018 gRPC 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// The test in this file should be run in an environment that has go1.10 or later,
|
||||||
|
// as the function SyscallConn() (required to get socket option) was
|
||||||
|
// introduced to net.TCPListener in go1.10.
|
||||||
|
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"google.golang.org/grpc/internal/channelz"
|
||||||
|
"google.golang.org/grpc/internal/leakcheck"
|
||||||
|
testpb "google.golang.org/grpc/test/grpc_testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCZSocketMetricsSocketOption(t *testing.T) {
|
||||||
|
envs := []env{tcpClearRREnv, tcpTLSRREnv}
|
||||||
|
for _, e := range envs {
|
||||||
|
testCZSocketMetricsSocketOption(t, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCZSocketMetricsSocketOption(t *testing.T, e env) {
|
||||||
|
defer leakcheck.Check(t)
|
||||||
|
channelz.NewChannelzStorage()
|
||||||
|
te := newTest(t, e)
|
||||||
|
te.startServer(&testServer{security: e.security})
|
||||||
|
defer te.tearDown()
|
||||||
|
cc := te.clientConn()
|
||||||
|
tc := testpb.NewTestServiceClient(cc)
|
||||||
|
doSuccessfulUnaryCall(tc, t)
|
||||||
|
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
ss, _ := channelz.GetServers(0)
|
||||||
|
if len(ss) != 1 {
|
||||||
|
t.Fatalf("There should be one server, not %d", len(ss))
|
||||||
|
}
|
||||||
|
if len(ss[0].ListenSockets) != 1 {
|
||||||
|
t.Fatalf("There should be one listen socket, not %d", len(ss[0].ListenSockets))
|
||||||
|
}
|
||||||
|
for id := range ss[0].ListenSockets {
|
||||||
|
sm := channelz.GetSocket(id)
|
||||||
|
if sm == nil || sm.SocketData == nil || sm.SocketData.SocketOptions == nil {
|
||||||
|
t.Fatalf("Unable to get server listen socket options")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ns, _ := channelz.GetServerSockets(ss[0].ID, 0)
|
||||||
|
if len(ns) != 1 {
|
||||||
|
t.Fatalf("There should be one server normal socket, not %d", len(ns))
|
||||||
|
}
|
||||||
|
if ns[0] == nil || ns[0].SocketData == nil || ns[0].SocketData.SocketOptions == nil {
|
||||||
|
t.Fatalf("Unable to get server normal socket options")
|
||||||
|
}
|
||||||
|
|
||||||
|
tchan, _ := channelz.GetTopChannels(0)
|
||||||
|
if len(tchan) != 1 {
|
||||||
|
t.Fatalf("There should only be one top channel, not %d", len(tchan))
|
||||||
|
}
|
||||||
|
if len(tchan[0].SubChans) != 1 {
|
||||||
|
t.Fatalf("There should only be one subchannel under top channel %d, not %d", tchan[0].ID, len(tchan[0].SubChans))
|
||||||
|
}
|
||||||
|
var id int64
|
||||||
|
for id = range tchan[0].SubChans {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
sc := channelz.GetSubChannel(id)
|
||||||
|
if sc == nil {
|
||||||
|
t.Fatalf("There should only be one socket under subchannel %d, not 0", id)
|
||||||
|
}
|
||||||
|
if len(sc.Sockets) != 1 {
|
||||||
|
t.Fatalf("There should only be one socket under subchannel %d, not %d", sc.ID, len(sc.Sockets))
|
||||||
|
}
|
||||||
|
for id = range sc.Sockets {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
skt := channelz.GetSocket(id)
|
||||||
|
if skt == nil || skt.SocketData == nil || skt.SocketData.SocketOptions == nil {
|
||||||
|
t.Fatalf("Unable to get client normal socket options")
|
||||||
|
}
|
||||||
|
}
|
|
@ -39,10 +39,6 @@ import (
|
||||||
testpb "google.golang.org/grpc/test/grpc_testing"
|
testpb "google.golang.org/grpc/test/grpc_testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
channelz.TurnOn()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (te *test) startServers(ts testpb.TestServiceServer, num int) {
|
func (te *test) startServers(ts testpb.TestServiceServer, num int) {
|
||||||
for i := 0; i < num; i++ {
|
for i := 0; i < num; i++ {
|
||||||
te.startServer(ts)
|
te.startServer(ts)
|
||||||
|
|
|
@ -1257,12 +1257,14 @@ func (t *http2Client) ChannelzMetric() *channelz.SocketInternalMetric {
|
||||||
LastMessageSentTimestamp: t.lastMsgSent,
|
LastMessageSentTimestamp: t.lastMsgSent,
|
||||||
LastMessageReceivedTimestamp: t.lastMsgRecv,
|
LastMessageReceivedTimestamp: t.lastMsgRecv,
|
||||||
LocalFlowControlWindow: int64(t.fc.getSize()),
|
LocalFlowControlWindow: int64(t.fc.getSize()),
|
||||||
//socket options
|
SocketOptions: channelz.GetSocketOption(t.conn),
|
||||||
LocalAddr: t.localAddr,
|
LocalAddr: t.localAddr,
|
||||||
RemoteAddr: t.remoteAddr,
|
RemoteAddr: t.remoteAddr,
|
||||||
// Security
|
|
||||||
// RemoteName :
|
// RemoteName :
|
||||||
}
|
}
|
||||||
|
if au, ok := t.authInfo.(credentials.ChannelzSecurityInfo); ok {
|
||||||
|
s.Security = au.GetSecurityValue()
|
||||||
|
}
|
||||||
t.czmu.RUnlock()
|
t.czmu.RUnlock()
|
||||||
s.RemoteFlowControlWindow = t.getOutFlowWindow()
|
s.RemoteFlowControlWindow = t.getOutFlowWindow()
|
||||||
return &s
|
return &s
|
||||||
|
|
|
@ -1091,12 +1091,14 @@ func (t *http2Server) ChannelzMetric() *channelz.SocketInternalMetric {
|
||||||
LastMessageSentTimestamp: t.lastMsgSent,
|
LastMessageSentTimestamp: t.lastMsgSent,
|
||||||
LastMessageReceivedTimestamp: t.lastMsgRecv,
|
LastMessageReceivedTimestamp: t.lastMsgRecv,
|
||||||
LocalFlowControlWindow: int64(t.fc.getSize()),
|
LocalFlowControlWindow: int64(t.fc.getSize()),
|
||||||
//socket options
|
SocketOptions: channelz.GetSocketOption(t.conn),
|
||||||
LocalAddr: t.localAddr,
|
LocalAddr: t.localAddr,
|
||||||
RemoteAddr: t.remoteAddr,
|
RemoteAddr: t.remoteAddr,
|
||||||
// Security
|
|
||||||
// RemoteName :
|
// RemoteName :
|
||||||
}
|
}
|
||||||
|
if au, ok := t.authInfo.(credentials.ChannelzSecurityInfo); ok {
|
||||||
|
s.Security = au.GetSecurityValue()
|
||||||
|
}
|
||||||
t.czmu.RUnlock()
|
t.czmu.RUnlock()
|
||||||
s.RemoteFlowControlWindow = t.getOutFlowWindow()
|
s.RemoteFlowControlWindow = t.getOutFlowWindow()
|
||||||
return &s
|
return &s
|
||||||
|
|
Loading…
Reference in New Issue