github.com/google/cloudprober@v0.11.3/common/message/message.go (about) 1 // Copyright 2017-2018 The Cloudprober Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package message implements wrappers for sending and receiving messages with 16 // sequence numbers and timestamps. 17 package message 18 19 import ( 20 "fmt" 21 "sync" 22 "time" 23 24 "github.com/golang/protobuf/proto" 25 msgpb "github.com/google/cloudprober/common/message/proto" 26 ) 27 28 // FlowState maintains the state of flow on both the src and dst sides. 29 type FlowState struct { 30 mu sync.Mutex 31 src string 32 srcPort string 33 dst string 34 35 // largest sequence number received till now, except for resets. 36 seq uint64 37 // timestamps associated with the largest seq number message. 38 msgTS time.Time 39 rxTS time.Time 40 } 41 42 // FlowStateMap is a container to hold all flows with a mutex to safely add new 43 // flows. 44 type FlowStateMap struct { 45 mu sync.Mutex 46 flowState map[string]*FlowState 47 } 48 49 // Results captures the result of sequence number analysis after 50 // processing the latest message. 51 type Results struct { 52 FS *FlowState 53 54 Success bool 55 LostCount int 56 Delayed bool 57 Dup bool 58 59 // Delta of rxTS and src timestamp in message. 60 Latency time.Duration 61 62 // Inter-packet delay for one-way-packets = delta_recv_ts - delta_send_ts. 63 // delta_send_ts = sender_ts_for_seq - sender_ts_for_seq+1 (send_ts is in msg) 64 // delta_recv_ts = rcvr_ts_for_seq - rcvr_ts_for_seq+1 (ts at msg recv). 65 InterPktDelay time.Duration 66 } 67 68 const ( 69 bytesInUint64 = 8 70 // If msgSeq - prevSeq is lesser than this number, assume src restart. 71 delayedThreshold = 300 72 ) 73 74 var constants msgpb.Constants 75 76 // Uint64ToNetworkBytes converts a 64bit unsigned integer to an 8-byte slice. 77 // in network byte order. 78 func Uint64ToNetworkBytes(val uint64) []byte { 79 bytes := make([]byte, bytesInUint64) 80 for i := bytesInUint64 - 1; i >= 0; i-- { 81 if val <= 0 { 82 break 83 } 84 bytes[i] = byte(val & 0xff) 85 val >>= 8 86 } 87 return bytes 88 } 89 90 // NetworkBytesToUint64 converts up to first 8 bytes of input slice to a uint64. 91 func NetworkBytesToUint64(bytes []byte) uint64 { 92 endIdx := len(bytes) 93 if endIdx > bytesInUint64 { 94 endIdx = bytesInUint64 95 } 96 97 val := uint64(0) 98 shift := uint(0) 99 for i := endIdx - 1; i >= 0; i-- { 100 val += uint64(bytes[i]) << shift 101 shift += 8 102 } 103 return val 104 } 105 106 // Message is a wrapper struct for the message protobuf that provides 107 // functions to access the most commonly accessed fields. 108 type Message struct { 109 m *msgpb.Msg 110 } 111 112 // NewMessage parses a byte array into a message. 113 func NewMessage(msgBytes []byte) (*Message, error) { 114 m := &Message{ 115 m: &msgpb.Msg{}, 116 } 117 118 msg := m.m 119 if err := proto.Unmarshal(msgBytes, msg); err != nil { 120 return nil, err 121 } 122 123 if msg.GetSrc() == nil { 124 return nil, fmt.Errorf("invalid message: no src node found") 125 } 126 if msg.GetDst() == nil { 127 return nil, fmt.Errorf("invalid message: no dst node found") 128 } 129 130 if msg.GetMagic() != constants.GetMagic() { 131 return nil, fmt.Errorf("invalid message from %s: got magic %x want %x", msg.GetSrc().GetName(), msg.GetMagic(), constants.GetMagic()) 132 } 133 134 return m, nil 135 } 136 137 // Src returns the src node name. 138 func (m *Message) Src() string { 139 return m.m.GetSrc().GetName() 140 } 141 142 // SrcPort returns the src port. 143 func (m *Message) SrcPort() string { 144 return m.m.GetSrc().GetPort() 145 } 146 147 // Dst returns the dst node name. 148 func (m *Message) Dst() string { 149 return m.m.GetDst().GetName() 150 } 151 152 // Seq returns the sequence number. 153 func (m *Message) Seq() uint64 { 154 return NetworkBytesToUint64(m.m.GetSeq()) 155 } 156 157 // SrcTS returns the timestamp for the source. 158 func (m *Message) SrcTS() time.Time { 159 tsVal := NetworkBytesToUint64(m.m.GetSrc().GetTimestampUsec()) 160 return time.Unix(0, int64(tsVal)*1000) 161 } 162 163 // Payload returns the payload in the UDP message. 164 func (m *Message) Payload() []byte { 165 return m.m.GetPayload() 166 } 167 168 // NewFlowStateMap returns a new FlowStateMap variable. 169 func NewFlowStateMap() *FlowStateMap { 170 return &FlowStateMap{ 171 flowState: make(map[string]*FlowState), 172 } 173 } 174 175 // FlowState returns the flow state for the node, creating a new one 176 // if necessary. 177 func (fm *FlowStateMap) FlowState(src, srcPort, dst string) *FlowState { 178 fm.mu.Lock() 179 defer fm.mu.Unlock() 180 idx := src + ":" + srcPort + "-" + dst 181 fs, ok := fm.flowState[idx] 182 if !ok { 183 now := time.Now() 184 fs = &FlowState{ 185 src: src, 186 srcPort: srcPort, 187 dst: dst, 188 msgTS: now, 189 rxTS: now, 190 } 191 fm.flowState[idx] = fs 192 } 193 return fs 194 } 195 196 // SetSeq sets internal state such that the next message will contain nextSeq. 197 func (fs *FlowState) SetSeq(nextSeq uint64) { 198 fs.seq = nextSeq - 1 199 } 200 201 // NextSeq returns the next sequence number that will be used. 202 func (fs *FlowState) NextSeq() uint64 { 203 return fs.seq + 1 204 } 205 206 // CreateMessage creates a message for the flow and returns byte array 207 // representation of the message and sequence number used on success. 208 // TODO: add Message.CreateMessage() fn and use it in FlowState.CreateMessage. 209 func (fs *FlowState) CreateMessage(ts time.Time, payload []byte, maxLen int) ([]byte, uint64, error) { 210 fs.mu.Lock() 211 defer fs.mu.Unlock() 212 213 dstType := msgpb.DataNode_SERVER 214 msg := &msgpb.Msg{ 215 Magic: proto.Uint64(constants.GetMagic()), 216 Seq: Uint64ToNetworkBytes(fs.seq + 1), 217 Src: &msgpb.DataNode{ 218 Name: proto.String(fs.src), 219 Port: proto.String(fs.srcPort), 220 TimestampUsec: Uint64ToNetworkBytes(uint64(ts.UnixNano()) / 1000), 221 }, 222 Dst: &msgpb.DataNode{ 223 Name: proto.String(fs.dst), 224 TimestampUsec: Uint64ToNetworkBytes(uint64(0)), 225 Type: &dstType, 226 }, 227 Payload: payload, 228 } 229 bytes, err := proto.Marshal(msg) 230 if err != nil { 231 return nil, 0, err 232 } 233 234 if len(bytes) > maxLen { 235 return nil, 0, fmt.Errorf("marshalled message too long %d > allowed max %d", len(bytes), maxLen) 236 } 237 fs.seq++ 238 return bytes, fs.seq, nil 239 } 240 241 // WithdrawMessage tries to update internal state that message with "seq" 242 // was not sent. If no new packet was sent in parallel, then sequence number 243 // is decremented, and the function returns true. Otherwise, it is a no-op 244 // and the function returns false. 245 func (fs *FlowState) WithdrawMessage(seq uint64) bool { 246 fs.mu.Lock() 247 defer fs.mu.Unlock() 248 if fs.seq == seq { 249 fs.seq-- 250 return true 251 } 252 return false 253 } 254 255 // ProcessOneWay processes a one-way message on the receiving end. 256 // It updates FlowState for the sender seq|msgTS|rxTS and returns a 257 // Results object with metrics derived from the message. 258 func (m *Message) ProcessOneWay(fsm *FlowStateMap, rxTS time.Time) *Results { 259 srcTS := m.SrcTS() 260 res := &Results{ 261 Latency: rxTS.Sub(srcTS), 262 } 263 264 fs := fsm.FlowState(m.Src(), m.SrcPort(), m.Dst()) 265 fs.mu.Lock() 266 defer fs.mu.Unlock() 267 res.FS = fs 268 269 msgSeq := m.Seq() 270 seqDelta := int64(msgSeq - fs.seq) 271 // Reset flow state and declare success if any of the conditions are met. 272 // a) msg.seq == 1 => sender likely restarted. 273 // b) fs.seq == 0 => listener restarted and flow state begins at 0 274 // c) msg.seq too far behind fs.seq => unlikely to be a delayed msg. 275 if msgSeq == 1 || fs.seq == 0 || seqDelta <= -delayedThreshold { 276 fs.seq = msgSeq 277 fs.msgTS = srcTS 278 fs.rxTS = rxTS 279 res.Success = true 280 return res 281 } 282 283 if seqDelta > 0 { 284 res.LostCount = int(seqDelta - 1) 285 if res.LostCount == 0 { 286 res.Success = true 287 msgTSDelta := srcTS.Sub(fs.msgTS) 288 if msgTSDelta <= 0 { 289 msgTSDelta = 0 290 } 291 res.InterPktDelay = rxTS.Sub(fs.rxTS) - msgTSDelta 292 } 293 fs.seq = msgSeq 294 fs.msgTS = srcTS 295 fs.rxTS = rxTS 296 } else if seqDelta == 0 { 297 // Repeat message !!!. Prober restart? or left over message? 298 res.Dup = true 299 } else { 300 res.Delayed = true 301 } 302 return res 303 }