1// +build linux 2// +build 386 amd64 3 4/* 5 * 6 * Copyright 2018 gRPC authors. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 * 20 */ 21 22// SocketOptions is only supported on linux system. The functions defined in 23// this file are to parse the socket option field and the test is specifically 24// to verify the behavior of socket option parsing. 25 26package service 27 28import ( 29 "context" 30 "reflect" 31 "strconv" 32 "testing" 33 34 "github.com/golang/protobuf/ptypes" 35 durpb "github.com/golang/protobuf/ptypes/duration" 36 "golang.org/x/sys/unix" 37 channelzpb "google.golang.org/grpc/channelz/grpc_channelz_v1" 38 "google.golang.org/grpc/internal/channelz" 39) 40 41func init() { 42 // Assign protoToSocketOption to protoToSocketOpt in order to enable socket option 43 // data conversion from proto message to channelz defined struct. 44 protoToSocketOpt = protoToSocketOption 45} 46 47func convertToDuration(d *durpb.Duration) (sec int64, usec int64) { 48 if d != nil { 49 if dur, err := ptypes.Duration(d); err == nil { 50 sec = int64(int64(dur) / 1e9) 51 usec = (int64(dur) - sec*1e9) / 1e3 52 } 53 } 54 return 55} 56 57func protoToLinger(protoLinger *channelzpb.SocketOptionLinger) *unix.Linger { 58 linger := &unix.Linger{} 59 if protoLinger.GetActive() { 60 linger.Onoff = 1 61 } 62 lv, _ := convertToDuration(protoLinger.GetDuration()) 63 linger.Linger = int32(lv) 64 return linger 65} 66 67func protoToSocketOption(skopts []*channelzpb.SocketOption) *channelz.SocketOptionData { 68 skdata := &channelz.SocketOptionData{} 69 for _, opt := range skopts { 70 switch opt.GetName() { 71 case "SO_LINGER": 72 protoLinger := &channelzpb.SocketOptionLinger{} 73 err := ptypes.UnmarshalAny(opt.GetAdditional(), protoLinger) 74 if err == nil { 75 skdata.Linger = protoToLinger(protoLinger) 76 } 77 case "SO_RCVTIMEO": 78 protoTimeout := &channelzpb.SocketOptionTimeout{} 79 err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout) 80 if err == nil { 81 skdata.RecvTimeout = protoToTime(protoTimeout) 82 } 83 case "SO_SNDTIMEO": 84 protoTimeout := &channelzpb.SocketOptionTimeout{} 85 err := ptypes.UnmarshalAny(opt.GetAdditional(), protoTimeout) 86 if err == nil { 87 skdata.SendTimeout = protoToTime(protoTimeout) 88 } 89 case "TCP_INFO": 90 tcpi := &channelzpb.SocketOptionTcpInfo{} 91 err := ptypes.UnmarshalAny(opt.GetAdditional(), tcpi) 92 if err == nil { 93 skdata.TCPInfo = &unix.TCPInfo{ 94 State: uint8(tcpi.TcpiState), 95 Ca_state: uint8(tcpi.TcpiCaState), 96 Retransmits: uint8(tcpi.TcpiRetransmits), 97 Probes: uint8(tcpi.TcpiProbes), 98 Backoff: uint8(tcpi.TcpiBackoff), 99 Options: uint8(tcpi.TcpiOptions), 100 Rto: tcpi.TcpiRto, 101 Ato: tcpi.TcpiAto, 102 Snd_mss: tcpi.TcpiSndMss, 103 Rcv_mss: tcpi.TcpiRcvMss, 104 Unacked: tcpi.TcpiUnacked, 105 Sacked: tcpi.TcpiSacked, 106 Lost: tcpi.TcpiLost, 107 Retrans: tcpi.TcpiRetrans, 108 Fackets: tcpi.TcpiFackets, 109 Last_data_sent: tcpi.TcpiLastDataSent, 110 Last_ack_sent: tcpi.TcpiLastAckSent, 111 Last_data_recv: tcpi.TcpiLastDataRecv, 112 Last_ack_recv: tcpi.TcpiLastAckRecv, 113 Pmtu: tcpi.TcpiPmtu, 114 Rcv_ssthresh: tcpi.TcpiRcvSsthresh, 115 Rtt: tcpi.TcpiRtt, 116 Rttvar: tcpi.TcpiRttvar, 117 Snd_ssthresh: tcpi.TcpiSndSsthresh, 118 Snd_cwnd: tcpi.TcpiSndCwnd, 119 Advmss: tcpi.TcpiAdvmss, 120 Reordering: tcpi.TcpiReordering} 121 } 122 } 123 } 124 return skdata 125} 126 127func (s) TestGetSocketOptions(t *testing.T) { 128 czCleanup := channelz.NewChannelzStorage() 129 defer cleanupWrapper(czCleanup, t) 130 ss := []*dummySocket{ 131 { 132 socketOptions: &channelz.SocketOptionData{ 133 Linger: &unix.Linger{Onoff: 1, Linger: 2}, 134 RecvTimeout: &unix.Timeval{Sec: 10, Usec: 1}, 135 SendTimeout: &unix.Timeval{}, 136 TCPInfo: &unix.TCPInfo{State: 1}, 137 }, 138 }, 139 } 140 svr := newCZServer() 141 ids := make([]int64, len(ss)) 142 svrID := channelz.RegisterServer(&dummyServer{}, "") 143 defer channelz.RemoveEntry(svrID) 144 for i, s := range ss { 145 ids[i] = channelz.RegisterNormalSocket(s, svrID, strconv.Itoa(i)) 146 defer channelz.RemoveEntry(ids[i]) 147 } 148 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 149 defer cancel() 150 for i, s := range ss { 151 resp, _ := svr.GetSocket(ctx, &channelzpb.GetSocketRequest{SocketId: ids[i]}) 152 metrics := resp.GetSocket() 153 if !reflect.DeepEqual(metrics.GetRef(), &channelzpb.SocketRef{SocketId: ids[i], Name: strconv.Itoa(i)}) || !reflect.DeepEqual(socketProtoToStruct(metrics), s) { 154 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)) 155 } 156 } 157} 158