From 7d779d9073a0b59f1e0fc2f7145bfcffa8b7e79d Mon Sep 17 00:00:00 2001 From: lyuxuan Date: Tue, 22 May 2018 13:32:02 -0700 Subject: [PATCH 1/8] channelz: stage 4 - add security and socket option info (#1965) --- channelz/service/func_nonunix.go | 30 ++++ channelz/service/func_unix.go | 105 ++++++++++++++ channelz/service/service.go | 34 +++++ channelz/service/service_test.go | 172 +++++++++++++++++++++-- channelz/types.go | 7 +- channelz/types_nonunix.go | 32 +++++ channelz/types_unix.go | 54 +++++++ channelz/util_nonunix_pre_go19.go | 26 ++++ channelz/util_test.go | 88 ++++++++++++ channelz/util_unix_go19.go | 40 ++++++ credentials/credentials.go | 51 ++++++- credentials/credentials_util_go18.go | 26 ++++ credentials/credentials_util_go19.go | 42 ++++++ credentials/credentials_util_pre_go18.go | 44 ++++++ credentials/credentials_util_pre_go19.go | 31 ++++ server.go | 3 +- test/channelz_linux_go19_test.go | 100 +++++++++++++ transport/http2_client.go | 10 +- transport/http2_server.go | 10 +- 19 files changed, 881 insertions(+), 24 deletions(-) create mode 100644 channelz/service/func_nonunix.go create mode 100644 channelz/service/func_unix.go create mode 100644 channelz/types_nonunix.go create mode 100644 channelz/types_unix.go create mode 100644 channelz/util_nonunix_pre_go19.go create mode 100644 channelz/util_test.go create mode 100644 channelz/util_unix_go19.go create mode 100644 credentials/credentials_util_go19.go create mode 100644 credentials/credentials_util_pre_go18.go create mode 100644 credentials/credentials_util_pre_go19.go create mode 100644 test/channelz_linux_go19_test.go diff --git a/channelz/service/func_nonunix.go b/channelz/service/func_nonunix.go new file mode 100644 index 000000000000..9d32f27d3486 --- /dev/null +++ b/channelz/service/func_nonunix.go @@ -0,0 +1,30 @@ +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +/* + * + * 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 ( + "google.golang.org/grpc/channelz" + channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" +) + +func sockoptToProto(skopts *channelz.SocketOptionData) []*channelzpb.SocketOption { + return nil +} diff --git a/channelz/service/func_unix.go b/channelz/service/func_unix.go new file mode 100644 index 000000000000..99cc150528e5 --- /dev/null +++ b/channelz/service/func_unix.go @@ -0,0 +1,105 @@ +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +/* + * + * 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" + "google.golang.org/grpc/channelz" + channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" +) + +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 +} diff --git a/channelz/service/service.go b/channelz/service/service.go index 9630568890f2..acab738014c6 100644 --- a/channelz/service/service.go +++ b/channelz/service/service.go @@ -21,8 +21,10 @@ package service import ( "net" + "time" "github.com/golang/protobuf/ptypes" + durpb "github.com/golang/protobuf/ptypes/duration" wrpb "github.com/golang/protobuf/ptypes/wrappers" "golang.org/x/net/context" "google.golang.org/grpc" @@ -30,8 +32,13 @@ import ( channelzgrpc "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/credentials" ) +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. func RegisterChannelzServiceToServer(s *grpc.Server) { channelzgrpc.RegisterChannelzServer(s, &serverImpl{}) @@ -128,6 +135,26 @@ func subChannelMetricToProto(cm *channelz.SubChannelMetric) *channelzpb.Subchann 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 { switch a.Network() { case "udp": @@ -175,6 +202,13 @@ func socketMetricToProto(sm *channelz.SocketMetric) *channelzpb.Socket { s.Data.LocalFlowControlWindow = &wrpb.Int64Value{Value: sm.SocketData.LocalFlowControlWindow} 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 { s.Local = addrToProto(sm.SocketData.LocalAddr) } diff --git a/channelz/service/service_test.go b/channelz/service/service_test.go index 4d3973c13df4..e2d32411d504 100644 --- a/channelz/service/service_test.go +++ b/channelz/service/service_test.go @@ -1,3 +1,5 @@ +// +build amd64,linux + /* * * Copyright 2018 gRPC authors. @@ -25,11 +27,15 @@ import ( "testing" "time" + "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes" + pdur "github.com/golang/protobuf/ptypes/duration" "golang.org/x/net/context" + "golang.org/x/sys/unix" "google.golang.org/grpc/channelz" channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials" ) func init() { @@ -92,11 +98,11 @@ type dummySocket struct { lastMessageReceivedTimestamp time.Time localFlowControlWindow int64 remoteFlowControlWindow int64 - //socket options - localAddr net.Addr - remoteAddr net.Addr - // Security - remoteName string + SocketOptions *channelz.SocketOptionData + localAddr net.Addr + remoteAddr net.Addr + Security credentials.ChannelzSecurityValue + remoteName string } func (d *dummySocket) ChannelzMetric() *channelz.SocketInternalMetric { @@ -113,11 +119,11 @@ func (d *dummySocket) ChannelzMetric() *channelz.SocketInternalMetric { LastMessageReceivedTimestamp: d.lastMessageReceivedTimestamp, LocalFlowControlWindow: d.localFlowControlWindow, RemoteFlowControlWindow: d.remoteFlowControlWindow, - //socket options - LocalAddr: d.localAddr, - RemoteAddr: d.remoteAddr, - // Security - RemoteName: d.remoteName, + SocketOptions: d.SocketOptions, + LocalAddr: d.localAddr, + RemoteAddr: d.remoteAddr, + Security: d.Security, + RemoteName: d.remoteName, } } @@ -164,6 +170,107 @@ func serverProtoToStruct(s *channelzpb.Server) *dummyServer { return ds } +func convertToDuration(d *pdur.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 protoToTime(protoTime *channelzpb.SocketOptionTimeout) *unix.Timeval { + timeout := &unix.Timeval{} + timeout.Sec, timeout.Usec = convertToDuration(protoTime.GetDuration()) + return timeout +} + +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 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 protoToAddr(a *channelzpb.Address) net.Addr { switch v := a.Address.(type) { case *channelzpb.Address_TcpipAddress: @@ -214,6 +321,12 @@ func socketProtoToStruct(s *channelzpb.Socket) *dummySocket { 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) } @@ -232,6 +345,20 @@ func convertSocketRefSliceToMap(sktRefs []*channelzpb.SocketRef) map[int64]strin 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) { tcs := []*dummyChannel{ { @@ -460,6 +587,31 @@ func TestGetSocket(t *testing.T) { { localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 10001}, }, + { + 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}, + }, + }, + { + 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() ids := make([]int64, len(ss)) diff --git a/channelz/types.go b/channelz/types.go index 153d75340e41..6fd6bb388af8 100644 --- a/channelz/types.go +++ b/channelz/types.go @@ -23,6 +23,7 @@ import ( "time" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/credentials" "google.golang.org/grpc/grpclog" ) @@ -281,9 +282,9 @@ type SocketInternalMetric struct { RemoteAddr net.Addr // Optional, represents the name of the remote endpoint, if different than // the original target name. - RemoteName string - //TODO: socket options - //TODO: Security + RemoteName string + SocketOptions *SocketOptionData + Security credentials.ChannelzSecurityValue } // Socket is the interface that should be satisfied in order to be tracked by diff --git a/channelz/types_nonunix.go b/channelz/types_nonunix.go new file mode 100644 index 000000000000..57f071ee121b --- /dev/null +++ b/channelz/types_nonunix.go @@ -0,0 +1,32 @@ +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris + +/* + * + * 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 + +// 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) {} diff --git a/channelz/types_unix.go b/channelz/types_unix.go new file mode 100644 index 000000000000..539f39fcab14 --- /dev/null +++ b/channelz/types_unix.go @@ -0,0 +1,54 @@ +// +build darwin dragonfly freebsd linux netbsd openbsd solaris + +/* + * + * 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 +} diff --git a/channelz/util_nonunix_pre_go19.go b/channelz/util_nonunix_pre_go19.go new file mode 100644 index 000000000000..f634fe09a6c4 --- /dev/null +++ b/channelz/util_nonunix_pre_go19.go @@ -0,0 +1,26 @@ +// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris !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 channelz + +// GetSocketOption gets the socket option info of the conn. +func GetSocketOption(c interface{}) *SocketOptionData { + return nil +} diff --git a/channelz/util_test.go b/channelz/util_test.go new file mode 100644 index 000000000000..12b2f2371813 --- /dev/null +++ b/channelz/util_test.go @@ -0,0 +1,88 @@ +// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build go1.10 + +/* + * + * 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_test + +import ( + "net" + "reflect" + "syscall" + "testing" + + "golang.org/x/sys/unix" + "google.golang.org/grpc/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) + } +} diff --git a/channelz/util_unix_go19.go b/channelz/util_unix_go19.go new file mode 100644 index 000000000000..1747a2b90c2f --- /dev/null +++ b/channelz/util_unix_go19.go @@ -0,0 +1,40 @@ +// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +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 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 +} diff --git a/credentials/credentials.go b/credentials/credentials.go index 3351bf0ee5f8..81d66d54f4cf 100644 --- a/credentials/credentials.go +++ b/credentials/credentials.go @@ -31,6 +31,7 @@ import ( "net" "strings" + "github.com/golang/protobuf/proto" "golang.org/x/net/context" ) @@ -118,6 +119,18 @@ func (t TLSInfo) AuthType() string { 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. type tlsCreds struct { // TLS configuration @@ -155,7 +168,7 @@ func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawCon case <-ctx.Done(): 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) { @@ -163,7 +176,7 @@ func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) if err := conn.Handshake(); err != nil { 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 { @@ -218,3 +231,37 @@ func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error } 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() {} diff --git a/credentials/credentials_util_go18.go b/credentials/credentials_util_go18.go index 93f0e1d8de23..b910e61323c4 100644 --- a/credentials/credentials_util_go18.go +++ b/credentials/credentials_util_go18.go @@ -24,6 +24,32 @@ 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_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 // fields of cfg, ignoring the unexported sync.Once, which // contains a mutex and must not be copied. diff --git a/credentials/credentials_util_go19.go b/credentials/credentials_util_go19.go new file mode 100644 index 000000000000..615578a4b8c4 --- /dev/null +++ b/credentials/credentials_util_go19.go @@ -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() +} diff --git a/credentials/credentials_util_pre_go18.go b/credentials/credentials_util_pre_go18.go new file mode 100644 index 000000000000..06417c8e48c8 --- /dev/null +++ b/credentials/credentials_util_pre_go18.go @@ -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", +} diff --git a/credentials/credentials_util_pre_go19.go b/credentials/credentials_util_pre_go19.go new file mode 100644 index 000000000000..9466e1955801 --- /dev/null +++ b/credentials/credentials_util_pre_go19.go @@ -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 +} diff --git a/server.go b/server.go index c76bb535fa59..7866a690923c 100644 --- a/server.go +++ b/server.go @@ -481,7 +481,8 @@ type listenSocket struct { func (l *listenSocket) ChannelzMetric() *channelz.SocketInternalMetric { return &channelz.SocketInternalMetric{ - LocalAddr: l.Listener.Addr(), + SocketOptions: channelz.GetSocketOption(l.Listener), + LocalAddr: l.Listener.Addr(), } } diff --git a/test/channelz_linux_go19_test.go b/test/channelz_linux_go19_test.go new file mode 100644 index 000000000000..66942afcc44b --- /dev/null +++ b/test/channelz_linux_go19_test.go @@ -0,0 +1,100 @@ +// +build go1.10,linux + +/* + * + * 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 test + +import ( + "testing" + "time" + + "google.golang.org/grpc/channelz" + testpb "google.golang.org/grpc/test/grpc_testing" + "google.golang.org/grpc/test/leakcheck" +) + +func init() { + channelz.TurnOn() +} + +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") + } +} diff --git a/transport/http2_client.go b/transport/http2_client.go index edf4d6cb3b7b..5e352c60407e 100644 --- a/transport/http2_client.go +++ b/transport/http2_client.go @@ -1249,12 +1249,14 @@ func (t *http2Client) ChannelzMetric() *channelz.SocketInternalMetric { LastMessageSentTimestamp: t.lastMsgSent, LastMessageReceivedTimestamp: t.lastMsgRecv, LocalFlowControlWindow: int64(t.fc.getSize()), - //socket options - LocalAddr: t.localAddr, - RemoteAddr: t.remoteAddr, - // Security + SocketOptions: channelz.GetSocketOption(t.conn), + LocalAddr: t.localAddr, + RemoteAddr: t.remoteAddr, // RemoteName : } + if au, ok := t.authInfo.(credentials.ChannelzSecurityInfo); ok { + s.Security = au.GetSecurityValue() + } t.czmu.RUnlock() s.RemoteFlowControlWindow = t.getOutFlowWindow() return &s diff --git a/transport/http2_server.go b/transport/http2_server.go index af0095745fb4..a69910bf31f2 100644 --- a/transport/http2_server.go +++ b/transport/http2_server.go @@ -1085,12 +1085,14 @@ func (t *http2Server) ChannelzMetric() *channelz.SocketInternalMetric { LastMessageSentTimestamp: t.lastMsgSent, LastMessageReceivedTimestamp: t.lastMsgRecv, LocalFlowControlWindow: int64(t.fc.getSize()), - //socket options - LocalAddr: t.localAddr, - RemoteAddr: t.remoteAddr, - // Security + SocketOptions: channelz.GetSocketOption(t.conn), + LocalAddr: t.localAddr, + RemoteAddr: t.remoteAddr, // RemoteName : } + if au, ok := t.authInfo.(credentials.ChannelzSecurityInfo); ok { + s.Security = au.GetSecurityValue() + } t.czmu.RUnlock() s.RemoteFlowControlWindow = t.getOutFlowWindow() return &s From 8e142ac9578bbb7c3d0cecbe7a66a3b6383a3bd6 Mon Sep 17 00:00:00 2001 From: Yuxuan Li Date: Tue, 22 May 2018 15:38:23 -0700 Subject: [PATCH 2/8] change build tags --- channelz/service/{func_unix.go => func_linux.go} | 2 -- channelz/service/{func_nonunix.go => func_nonlinux.go} | 2 +- channelz/service/service_test.go | 2 +- channelz/{types_unix.go => types_linux.go} | 2 -- channelz/{types_nonunix.go => types_nonlinux.go} | 2 +- channelz/{util_unix_go19.go => util_linux_go19.go} | 2 +- .../{util_nonunix_pre_go19.go => util_nonlinux_pre_go19.go} | 2 +- channelz/util_test.go | 2 +- 8 files changed, 6 insertions(+), 10 deletions(-) rename channelz/service/{func_unix.go => func_linux.go} (98%) rename channelz/service/{func_nonunix.go => func_nonlinux.go} (92%) rename channelz/{types_unix.go => types_linux.go} (96%) rename channelz/{types_nonunix.go => types_nonlinux.go} (93%) rename channelz/{util_unix_go19.go => util_linux_go19.go} (93%) rename channelz/{util_nonunix_pre_go19.go => util_nonlinux_pre_go19.go} (90%) diff --git a/channelz/service/func_unix.go b/channelz/service/func_linux.go similarity index 98% rename from channelz/service/func_unix.go rename to channelz/service/func_linux.go index 99cc150528e5..1dd713a2b97a 100644 --- a/channelz/service/func_unix.go +++ b/channelz/service/func_linux.go @@ -1,5 +1,3 @@ -// +build darwin dragonfly freebsd linux netbsd openbsd solaris - /* * * Copyright 2018 gRPC authors. diff --git a/channelz/service/func_nonunix.go b/channelz/service/func_nonlinux.go similarity index 92% rename from channelz/service/func_nonunix.go rename to channelz/service/func_nonlinux.go index 9d32f27d3486..4c7151bd6b53 100644 --- a/channelz/service/func_nonunix.go +++ b/channelz/service/func_nonlinux.go @@ -1,4 +1,4 @@ -// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris +// +build !linux /* * diff --git a/channelz/service/service_test.go b/channelz/service/service_test.go index e2d32411d504..f8e3fbe6b833 100644 --- a/channelz/service/service_test.go +++ b/channelz/service/service_test.go @@ -1,4 +1,4 @@ -// +build amd64,linux +// +build linux /* * diff --git a/channelz/types_unix.go b/channelz/types_linux.go similarity index 96% rename from channelz/types_unix.go rename to channelz/types_linux.go index 539f39fcab14..13cbbacbbe51 100644 --- a/channelz/types_unix.go +++ b/channelz/types_linux.go @@ -1,5 +1,3 @@ -// +build darwin dragonfly freebsd linux netbsd openbsd solaris - /* * * Copyright 2018 gRPC authors. diff --git a/channelz/types_nonunix.go b/channelz/types_nonlinux.go similarity index 93% rename from channelz/types_nonunix.go rename to channelz/types_nonlinux.go index 57f071ee121b..d59d5e02c674 100644 --- a/channelz/types_nonunix.go +++ b/channelz/types_nonlinux.go @@ -1,4 +1,4 @@ -// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris +// +build !linux /* * diff --git a/channelz/util_unix_go19.go b/channelz/util_linux_go19.go similarity index 93% rename from channelz/util_unix_go19.go rename to channelz/util_linux_go19.go index 1747a2b90c2f..74efd5b64ea4 100644 --- a/channelz/util_unix_go19.go +++ b/channelz/util_linux_go19.go @@ -1,4 +1,4 @@ -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build linux // +build go1.9 /* diff --git a/channelz/util_nonunix_pre_go19.go b/channelz/util_nonlinux_pre_go19.go similarity index 90% rename from channelz/util_nonunix_pre_go19.go rename to channelz/util_nonlinux_pre_go19.go index f634fe09a6c4..bc5edb35b6da 100644 --- a/channelz/util_nonunix_pre_go19.go +++ b/channelz/util_nonlinux_pre_go19.go @@ -1,4 +1,4 @@ -// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris !go1.9 +// +build !linux !go1.9 /* * diff --git a/channelz/util_test.go b/channelz/util_test.go index 12b2f2371813..60f91af4c072 100644 --- a/channelz/util_test.go +++ b/channelz/util_test.go @@ -1,4 +1,4 @@ -// +build darwin dragonfly freebsd linux netbsd openbsd solaris +// +build linux // +build go1.10 /* From 006b269fb7c2ce08f0697e88774569da18d7a798 Mon Sep 17 00:00:00 2001 From: lyuxuan Date: Thu, 24 May 2018 14:05:52 -0700 Subject: [PATCH 3/8] service_test only runs under amd64 --- channelz/service/service_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/channelz/service/service_test.go b/channelz/service/service_test.go index f8e3fbe6b833..e2d32411d504 100644 --- a/channelz/service/service_test.go +++ b/channelz/service/service_test.go @@ -1,4 +1,4 @@ -// +build linux +// +build amd64,linux /* * From 02e23b1e1a7ad5e1299d03932b65cbf2036c3b74 Mon Sep 17 00:00:00 2001 From: Yuxuan Li Date: Thu, 24 May 2018 15:30:14 -0700 Subject: [PATCH 4/8] run service_test for both 386 and amd64 --- channelz/service/service_test.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/channelz/service/service_test.go b/channelz/service/service_test.go index e2d32411d504..d8ae4ca9c1a7 100644 --- a/channelz/service/service_test.go +++ b/channelz/service/service_test.go @@ -1,4 +1,4 @@ -// +build amd64,linux +// +build linux /* * @@ -190,12 +190,6 @@ func protoToLinger(protoLinger *channelzpb.SocketOptionLinger) *unix.Linger { return linger } -func protoToTime(protoTime *channelzpb.SocketOptionTimeout) *unix.Timeval { - timeout := &unix.Timeval{} - timeout.Sec, timeout.Usec = convertToDuration(protoTime.GetDuration()) - return timeout -} - func protoToSecurity(protoSecurity *channelzpb.Security) credentials.ChannelzSecurityValue { switch v := protoSecurity.Model.(type) { case *channelzpb.Security_Tls_: From 9637fb866478d7b91acc5f29e37e1171c6697e53 Mon Sep 17 00:00:00 2001 From: Yuxuan Li Date: Tue, 29 May 2018 11:22:34 -0700 Subject: [PATCH 5/8] add init --- channelz/service/util_386.go | 33 +++++++++++++++++++++++++++++++++ channelz/service/util_amd64.go | 32 ++++++++++++++++++++++++++++++++ channelz/types_nonlinux.go | 6 ++++++ 3 files changed, 71 insertions(+) create mode 100644 channelz/service/util_386.go create mode 100644 channelz/service/util_amd64.go diff --git a/channelz/service/util_386.go b/channelz/service/util_386.go new file mode 100644 index 000000000000..d9c981271361 --- /dev/null +++ b/channelz/service/util_386.go @@ -0,0 +1,33 @@ +// +build 386,linux + +/* + * + * 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 +} diff --git a/channelz/service/util_amd64.go b/channelz/service/util_amd64.go new file mode 100644 index 000000000000..0ff06d128330 --- /dev/null +++ b/channelz/service/util_amd64.go @@ -0,0 +1,32 @@ +// +build amd64,linux + +/* + * + * 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 +} diff --git a/channelz/types_nonlinux.go b/channelz/types_nonlinux.go index d59d5e02c674..0791cce59afb 100644 --- a/channelz/types_nonlinux.go +++ b/channelz/types_nonlinux.go @@ -20,6 +20,12 @@ package channelz +import "google.golang.org/grpc/grpclog" + +func init() { + grpclog.Infof("Channelz: socket options are not supported on non-linux os.") +} + // 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 From e63f688da5e866c6c319f9b1ebe59df29b9b96ea Mon Sep 17 00:00:00 2001 From: Yuxuan Li Date: Tue, 29 May 2018 16:27:45 -0700 Subject: [PATCH 6/8] fix staticcheck --- channelz/service/{util_386.go => util_386_test.go} | 0 channelz/service/{util_amd64.go => util_amd64_test.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename channelz/service/{util_386.go => util_386_test.go} (100%) rename channelz/service/{util_amd64.go => util_amd64_test.go} (100%) diff --git a/channelz/service/util_386.go b/channelz/service/util_386_test.go similarity index 100% rename from channelz/service/util_386.go rename to channelz/service/util_386_test.go diff --git a/channelz/service/util_amd64.go b/channelz/service/util_amd64_test.go similarity index 100% rename from channelz/service/util_amd64.go rename to channelz/service/util_amd64_test.go From 5d5baf6009ea45895c77c5c09816dc46b7dbf209 Mon Sep 17 00:00:00 2001 From: Yuxuan Li Date: Wed, 30 May 2018 16:35:04 -0700 Subject: [PATCH 7/8] service test on more platforms --- channelz/service/service_linux_test.go | 192 ++++++++++++++++++ channelz/service/service_nonlinux_test.go | 74 +++++++ channelz/service/service_test.go | 143 ------------- ...9_test.go => channelz_linux_go110_test.go} | 0 4 files changed, 266 insertions(+), 143 deletions(-) create mode 100644 channelz/service/service_linux_test.go create mode 100644 channelz/service/service_nonlinux_test.go rename test/{channelz_linux_go19_test.go => channelz_linux_go110_test.go} (100%) diff --git a/channelz/service/service_linux_test.go b/channelz/service/service_linux_test.go new file mode 100644 index 000000000000..921825670402 --- /dev/null +++ b/channelz/service/service_linux_test.go @@ -0,0 +1,192 @@ +// +build linux + +/* + * + * 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 ( + "reflect" + "strconv" + "testing" + + "github.com/golang/protobuf/ptypes" + pdur "github.com/golang/protobuf/ptypes/duration" + "golang.org/x/net/context" + "golang.org/x/sys/unix" + "google.golang.org/grpc/channelz" + channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" +) + +func convertToDuration(d *pdur.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)) + } + } +} diff --git a/channelz/service/service_nonlinux_test.go b/channelz/service/service_nonlinux_test.go new file mode 100644 index 000000000000..206d3e424d24 --- /dev/null +++ b/channelz/service/service_nonlinux_test.go @@ -0,0 +1,74 @@ +// +build !linux + +/* + * + * 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" +) + +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 +} diff --git a/channelz/service/service_test.go b/channelz/service/service_test.go index d8ae4ca9c1a7..816deb269f9d 100644 --- a/channelz/service/service_test.go +++ b/channelz/service/service_test.go @@ -1,5 +1,3 @@ -// +build linux - /* * * Copyright 2018 gRPC authors. @@ -29,9 +27,7 @@ import ( "github.com/golang/protobuf/proto" "github.com/golang/protobuf/ptypes" - pdur "github.com/golang/protobuf/ptypes/duration" "golang.org/x/net/context" - "golang.org/x/sys/unix" "google.golang.org/grpc/channelz" channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" "google.golang.org/grpc/connectivity" @@ -170,26 +166,6 @@ func serverProtoToStruct(s *channelzpb.Server) *dummyServer { return ds } -func convertToDuration(d *pdur.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 protoToSecurity(protoSecurity *channelzpb.Security) credentials.ChannelzSecurityValue { switch v := protoSecurity.Model.(type) { case *channelzpb.Security_Tls_: @@ -205,66 +181,6 @@ func protoToSecurity(protoSecurity *channelzpb.Security) credentials.ChannelzSec return nil } -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 protoToAddr(a *channelzpb.Address) net.Addr { switch v := a.Address.(type) { case *channelzpb.Address_TcpipAddress: @@ -280,57 +196,6 @@ func protoToAddr(a *channelzpb.Address) net.Addr { 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 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 convertSocketRefSliceToMap(sktRefs []*channelzpb.SocketRef) map[int64]string { m := make(map[int64]string) for _, sr := range sktRefs { @@ -581,14 +446,6 @@ func TestGetSocket(t *testing.T) { { localAddr: &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: 10001}, }, - { - 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}, - }, - }, { Security: &credentials.TLSChannelzSecurityValue{ StandardName: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", diff --git a/test/channelz_linux_go19_test.go b/test/channelz_linux_go110_test.go similarity index 100% rename from test/channelz_linux_go19_test.go rename to test/channelz_linux_go110_test.go From 9b21de4886833ce1023dff423220a266be9f5c30 Mon Sep 17 00:00:00 2001 From: Yuxuan Li Date: Fri, 1 Jun 2018 17:05:42 -0700 Subject: [PATCH 8/8] add comments --- channelz/service/service_linux_test.go | 4 ++++ channelz/service/service_nonlinux_test.go | 3 +++ channelz/util_test.go | 4 ++++ test/channelz_linux_go110_test.go | 8 ++++---- test/channelz_test.go | 4 ---- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/channelz/service/service_linux_test.go b/channelz/service/service_linux_test.go index 921825670402..348eae26557a 100644 --- a/channelz/service/service_linux_test.go +++ b/channelz/service/service_linux_test.go @@ -18,6 +18,10 @@ * */ +// 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 ( diff --git a/channelz/service/service_nonlinux_test.go b/channelz/service/service_nonlinux_test.go index 206d3e424d24..cedfd73df6fe 100644 --- a/channelz/service/service_nonlinux_test.go +++ b/channelz/service/service_nonlinux_test.go @@ -18,6 +18,9 @@ * */ +// 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 ( diff --git a/channelz/util_test.go b/channelz/util_test.go index 60f91af4c072..18a7d2c5429a 100644 --- a/channelz/util_test.go +++ b/channelz/util_test.go @@ -19,6 +19,10 @@ * */ +// 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 ( diff --git a/test/channelz_linux_go110_test.go b/test/channelz_linux_go110_test.go index 66942afcc44b..20d54f4f57b1 100644 --- a/test/channelz_linux_go110_test.go +++ b/test/channelz_linux_go110_test.go @@ -18,6 +18,10 @@ * */ +// 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 ( @@ -29,10 +33,6 @@ import ( "google.golang.org/grpc/test/leakcheck" ) -func init() { - channelz.TurnOn() -} - func TestCZSocketMetricsSocketOption(t *testing.T) { envs := []env{tcpClearRREnv, tcpTLSRREnv} for _, e := range envs { diff --git a/test/channelz_test.go b/test/channelz_test.go index ffee50b2efde..1c8a2fabcfe4 100644 --- a/test/channelz_test.go +++ b/test/channelz_test.go @@ -38,10 +38,6 @@ import ( "google.golang.org/grpc/test/leakcheck" ) -func init() { - channelz.TurnOn() -} - func (te *test) startServers(ts testpb.TestServiceServer, num int) { for i := 0; i < num; i++ { te.startServer(ts)