github.com/fafucoder/cilium@v1.6.11/pkg/monitor/datapath_debug.go (about) 1 // Copyright 2016-2019 Authors of Cilium 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 monitor 16 17 import ( 18 "encoding/json" 19 "fmt" 20 "net" 21 22 "github.com/cilium/cilium/pkg/byteorder" 23 "github.com/cilium/cilium/pkg/monitor/api" 24 ) 25 26 // must be in sync with <bpf/lib/dbg.h> 27 const ( 28 DbgCaptureUnspec = iota 29 DbgCaptureReserved1 30 DbgCaptureReserved2 31 DbgCaptureReserved3 32 DbgCaptureDelivery 33 DbgCaptureFromLb 34 DbgCaptureAfterV46 35 DbgCaptureAfterV64 36 DbgCaptureProxyPre 37 DbgCaptureProxyPost 38 DbgCaptureSnatPre 39 DbgCaptureSnatPost 40 ) 41 42 // must be in sync with <bpf/lib/dbg.h> 43 const ( 44 DbgUnspec = iota 45 DbgGeneric 46 DbgLocalDelivery 47 DbgEncap 48 DbgLxcFound 49 DbgPolicyDenied 50 DbgCtLookup 51 DbgCtLookupRev 52 DbgCtMatch 53 DbgCtCreated 54 DbgCtCreated2 55 DbgIcmp6Handle 56 DbgIcmp6Request 57 DbgIcmp6Ns 58 DbgIcmp6TimeExceeded 59 DbgCtVerdict 60 DbgDecap 61 DbgPortMap 62 DbgErrorRet 63 DbgToHost 64 DbgToStack 65 DbgPktHash 66 DbgLb6LookupMaster 67 DbgLb6LookupMasterFail 68 DbgLb6LookupSlave 69 DbgLb6LookupSlaveSuccess 70 DbgLb6LookupSlaveV2Fail 71 DbgLb6LookupBackendFail 72 DbgLb6ReverseNatLookup 73 DbgLb6ReverseNat 74 DbgLb4LookupMaster 75 DbgLb4LookupMasterFail 76 DbgLb4LookupSlave 77 DbgLb4LookupSlaveSuccess 78 DbgLb4LookupSlaveV2Fail 79 DbgLb4LookupBackendFail 80 DbgLb4ReverseNatLookup 81 DbgLb4ReverseNat 82 DbgLb4LoopbackSnat 83 DbgLb4LoopbackSnatRev 84 DbgCtLookup4 85 DbgRRSlaveSel 86 DbgRevProxyLookup 87 DbgRevProxyFound 88 DbgRevProxyUpdate 89 DbgL4Policy 90 DbgNetdevInCluster 91 DbgNetdevEncap4 92 DbgCTLookup41 93 DbgCTLookup42 94 DbgCTCreated4 95 DbgCTLookup61 96 DbgCTLookup62 97 DbgCTCreated6 98 DbgSkipProxy 99 DbgL4Create 100 DbgIPIDMapFailed4 101 DbgIPIDMapFailed6 102 DbgIPIDMapSucceed4 103 DbgIPIDMapSucceed6 104 DbgLbStaleCT 105 DbgInheritIdentity 106 ) 107 108 // must be in sync with <bpf/lib/conntrack.h> 109 const ( 110 CtNew uint32 = iota 111 CtEstablished 112 CtReply 113 CtRelated 114 ) 115 116 var ctStateText = map[uint32]string{ 117 CtNew: "New", 118 CtEstablished: "Established", 119 CtReply: "Reply", 120 CtRelated: "Related", 121 } 122 123 const ( 124 ctEgress = 0 125 ctIngress = 1 126 ) 127 128 var ctDirection = map[int]string{ 129 ctEgress: "egress", 130 ctIngress: "ingress", 131 } 132 133 func ctState(state uint32) string { 134 txt, ok := ctStateText[state] 135 if ok { 136 return txt 137 } 138 139 return api.DropReason(uint8(state)) 140 } 141 142 var tupleFlags = map[int16]string{ 143 0: "IN", 144 1: "OUT", 145 2: "RELATED", 146 } 147 148 func ctFlags(flags int16) string { 149 s := "" 150 for k, v := range tupleFlags { 151 if k&flags != 0 { 152 if s != "" { 153 s += ", " 154 } 155 s += v 156 } 157 } 158 return s 159 } 160 161 func ctInfo(arg1 uint32, arg2 uint32) string { 162 return fmt.Sprintf("sport=%d dport=%d nexthdr=%d flags=%s", 163 arg1>>16, arg1&0xFFFF, arg2>>8, ctFlags(int16(arg2&0xFF))) 164 } 165 166 func ctLookup4Info1(n *DebugMsg) string { 167 return fmt.Sprintf("src=%s:%d dst=%s:%d", ip4Str(n.Arg1), 168 n.Arg3&0xFFFF, ip4Str(n.Arg2), n.Arg3>>16) 169 } 170 171 func ctLookup4Info2(n *DebugMsg) string { 172 return fmt.Sprintf("nexthdr=%d flags=%d", 173 n.Arg1>>8, n.Arg1&0xFF) 174 } 175 176 func ctCreate4Info(n *DebugMsg) string { 177 return fmt.Sprintf("proxy-port=%d revnat=%d src-identity=%d lb=%s", 178 n.Arg1>>16, byteorder.NetworkToHost(uint16(n.Arg1&0xFFFF)), n.Arg2, ip4Str(n.Arg3)) 179 } 180 181 func ctLookup6Info1(n *DebugMsg) string { 182 return fmt.Sprintf("src=[::%s]:%d dst=[::%s]:%d", ip6Str(n.Arg1), 183 n.Arg3&0xFFFF, ip6Str(n.Arg2), n.Arg3>>16) 184 } 185 186 func ctCreate6Info(n *DebugMsg) string { 187 return fmt.Sprintf("proxy-port=%d revnat=%d src-identity=%d", 188 n.Arg1>>16, byteorder.NetworkToHost(uint16(n.Arg1&0xFFFF)), n.Arg2) 189 } 190 191 func verdictInfo(arg uint32) string { 192 revnat := byteorder.NetworkToHost(uint16(arg & 0xFFFF)) 193 return fmt.Sprintf("revnat=%d", revnat) 194 } 195 196 func proxyInfo(arg1 uint32, arg2 uint32) string { 197 sport := byteorder.NetworkToHost(uint16(arg1 >> 16)) 198 dport := byteorder.NetworkToHost(uint16(arg1 & 0xFFFF)) 199 return fmt.Sprintf("sport=%d dport=%d saddr=%s", sport, dport, ip4Str(arg2)) 200 } 201 202 func l4CreateInfo(n *DebugMsg) string { 203 src := n.Arg1 204 dst := n.Arg2 205 dport := byteorder.NetworkToHost(uint16(n.Arg3 >> 16)) 206 proto := n.Arg3 & 0xFF 207 return fmt.Sprintf("src=%d dst=%d dport=%d proto=%d", src, dst, dport, proto) 208 } 209 210 func ip4Str(arg1 uint32) string { 211 ip := make(net.IP, 4) 212 byteorder.NetworkToHostPut(ip, arg1) 213 return ip.String() 214 } 215 216 func ip6Str(arg1 uint32) string { 217 ip6 := byteorder.NetworkToHost(arg1).(uint32) 218 return fmt.Sprintf("%x:%x", ip6>>16, ip6&0xFFFF) 219 } 220 221 // DebugMsg is the message format of the debug message found in the BPF ring buffer 222 type DebugMsg struct { 223 Type uint8 224 SubType uint8 225 Source uint16 226 Hash uint32 227 Arg1 uint32 228 Arg2 uint32 229 Arg3 uint32 230 } 231 232 // DumpInfo prints a summary of a subset of the debug messages which are related 233 // to sending, not processing, of packets. 234 func (n *DebugMsg) DumpInfo(data []byte) { 235 } 236 237 // Dump prints the debug message in a human readable format. 238 func (n *DebugMsg) Dump(prefix string) { 239 fmt.Printf("%s MARK %#x FROM %d DEBUG: %s\n", prefix, n.Hash, n.Source, n.subTypeString()) 240 } 241 242 func (n *DebugMsg) subTypeString() string { 243 switch n.SubType { 244 case DbgGeneric: 245 return fmt.Sprintf("No message, arg1=%d (%#x) arg2=%d (%#x)", n.Arg1, n.Arg1, n.Arg2, n.Arg2) 246 case DbgLocalDelivery: 247 return fmt.Sprintf("Attempting local delivery for container id %d from seclabel %d", n.Arg1, n.Arg2) 248 case DbgEncap: 249 return fmt.Sprintf("Encapsulating to node %d (%#x) from seclabel %d", n.Arg1, n.Arg1, n.Arg2) 250 case DbgLxcFound: 251 return fmt.Sprintf("Local container found ifindex %s seclabel %d", ifname(int(n.Arg1)), byteorder.NetworkToHost(uint16(n.Arg2))) 252 case DbgPolicyDenied: 253 return fmt.Sprintf("Policy evaluation would deny packet from %d to %d", n.Arg1, n.Arg2) 254 case DbgCtLookup: 255 return fmt.Sprintf("CT lookup: %s", ctInfo(n.Arg1, n.Arg2)) 256 case DbgCtLookupRev: 257 return fmt.Sprintf("CT reverse lookup: %s", ctInfo(n.Arg1, n.Arg2)) 258 case DbgCtLookup4: 259 return fmt.Sprintf("CT lookup address: %s", ip4Str(n.Arg1)) 260 case DbgCtMatch: 261 return fmt.Sprintf("CT entry found lifetime=%d, %s", n.Arg1, 262 verdictInfo(n.Arg2)) 263 case DbgCtCreated: 264 return fmt.Sprintf("CT created 1/2: %s %s", 265 ctInfo(n.Arg1, n.Arg2), verdictInfo(n.Arg3)) 266 case DbgCtCreated2: 267 return fmt.Sprintf("CT created 2/2: %s revnat=%d", ip4Str(n.Arg1), byteorder.NetworkToHost(uint16(n.Arg2))) 268 case DbgCtVerdict: 269 return fmt.Sprintf("CT verdict: %s, %s", 270 ctState(n.Arg1), verdictInfo(n.Arg2)) 271 case DbgIcmp6Handle: 272 return fmt.Sprintf("Handling ICMPv6 type=%d", n.Arg1) 273 case DbgIcmp6Request: 274 return fmt.Sprintf("ICMPv6 echo request for router offset=%d", n.Arg1) 275 case DbgIcmp6Ns: 276 return fmt.Sprintf("ICMPv6 neighbour soliciation for address %x:%x", n.Arg1, n.Arg2) 277 case DbgIcmp6TimeExceeded: 278 return fmt.Sprintf("Sending ICMPv6 time exceeded") 279 case DbgDecap: 280 return fmt.Sprintf("Tunnel decap: id=%d flowlabel=%x", n.Arg1, n.Arg2) 281 case DbgPortMap: 282 return fmt.Sprintf("Mapping port from=%d to=%d", n.Arg1, n.Arg2) 283 case DbgErrorRet: 284 return fmt.Sprintf("BPF function %d returned error %d", n.Arg1, n.Arg2) 285 case DbgToHost: 286 return fmt.Sprintf("Going to host, policy-skip=%d", n.Arg1) 287 case DbgToStack: 288 return fmt.Sprintf("Going to the stack, policy-skip=%d", n.Arg1) 289 case DbgPktHash: 290 return fmt.Sprintf("Packet hash=%d (%#x), selected_service=%d", n.Arg1, n.Arg1, n.Arg2) 291 case DbgRRSlaveSel: 292 return fmt.Sprintf("RR slave selection hash=%d (%#x), selected_service=%d", n.Arg1, n.Arg1, n.Arg2) 293 case DbgLb6LookupMaster: 294 return fmt.Sprintf("Master service lookup, addr.p4=%x key.dport=%d", n.Arg1, byteorder.NetworkToHost(uint16(n.Arg2))) 295 case DbgLb6LookupMasterFail: 296 return fmt.Sprintf("Master service lookup failed, addr.p2=%x addr.p3=%x", n.Arg1, n.Arg2) 297 case DbgLb6LookupSlave, DbgLb4LookupSlave: 298 return fmt.Sprintf("Slave service lookup: slave=%d, dport=%d", n.Arg1, byteorder.NetworkToHost(uint16(n.Arg2))) 299 case DbgLb6LookupSlaveV2Fail, DbgLb4LookupSlaveV2Fail: 300 return fmt.Sprintf("Slave service lookup failed: slave=%d, dport=%d", n.Arg1, byteorder.NetworkToHost(uint16(n.Arg2))) 301 case DbgLb6LookupBackendFail, DbgLb4LookupBackendFail: 302 return fmt.Sprintf("Backend service lookup failed: backend_id=%d", n.Arg1) 303 case DbgLb6LookupSlaveSuccess: 304 return fmt.Sprintf("Slave service lookup result: target.p4=%x port=%d", n.Arg1, byteorder.NetworkToHost(uint16(n.Arg2))) 305 case DbgLb6ReverseNatLookup, DbgLb4ReverseNatLookup: 306 return fmt.Sprintf("Reverse NAT lookup, index=%d", byteorder.NetworkToHost(uint16(n.Arg1))) 307 case DbgLb6ReverseNat: 308 return fmt.Sprintf("Performing reverse NAT, address.p4=%x port=%d", n.Arg1, byteorder.NetworkToHost(uint16(n.Arg2))) 309 case DbgLb4LookupMaster: 310 return fmt.Sprintf("Master service lookup, addr=%s key.dport=%d", ip4Str(n.Arg1), byteorder.NetworkToHost(uint16(n.Arg2))) 311 case DbgLb4LookupMasterFail: 312 return fmt.Sprintf("Master service lookup failed") 313 case DbgLb4LookupSlaveSuccess: 314 return fmt.Sprintf("Slave service lookup result: target=%s port=%d", ip4Str(n.Arg1), byteorder.NetworkToHost(uint16(n.Arg2))) 315 case DbgLb4ReverseNat: 316 return fmt.Sprintf("Performing reverse NAT, address=%s port=%d", ip4Str(n.Arg1), byteorder.NetworkToHost(uint16(n.Arg2))) 317 case DbgLb4LoopbackSnat: 318 return fmt.Sprintf("Loopback SNAT from=%s to=%s", ip4Str(n.Arg1), ip4Str(n.Arg2)) 319 case DbgLb4LoopbackSnatRev: 320 return fmt.Sprintf("Loopback reverse SNAT from=%s to=%s", ip4Str(n.Arg1), ip4Str(n.Arg2)) 321 case DbgRevProxyLookup: 322 return fmt.Sprintf("Reverse proxy lookup %s nexthdr=%d", 323 proxyInfo(n.Arg1, n.Arg2), n.Arg3) 324 case DbgRevProxyFound: 325 return fmt.Sprintf("Reverse proxy entry found, orig-daddr=%s orig-dport=%d", ip4Str(n.Arg1), n.Arg2) 326 case DbgRevProxyUpdate: 327 return fmt.Sprintf("Reverse proxy updated %s nexthdr=%d", 328 proxyInfo(n.Arg1, n.Arg2), n.Arg3) 329 case DbgL4Policy: 330 return fmt.Sprintf("Resolved L4 policy to: %d / %s", 331 byteorder.NetworkToHost(uint16(n.Arg1)), ctDirection[int(n.Arg2)]) 332 case DbgNetdevInCluster: 333 return fmt.Sprintf("Destination is inside cluster prefix, source identity: %d", n.Arg1) 334 case DbgNetdevEncap4: 335 return fmt.Sprintf("Attempting encapsulation, lookup key: %s, identity: %d", ip4Str(n.Arg1), n.Arg2) 336 case DbgCTLookup41: 337 return fmt.Sprintf("Conntrack lookup 1/2: %s", ctLookup4Info1(n)) 338 case DbgCTLookup42: 339 return fmt.Sprintf("Conntrack lookup 2/2: %s", ctLookup4Info2(n)) 340 case DbgCTCreated4: 341 return fmt.Sprintf("Conntrack create: %s", ctCreate4Info(n)) 342 case DbgCTLookup61: 343 return fmt.Sprintf("Conntrack lookup 1/2: %s", ctLookup6Info1(n)) 344 case DbgCTLookup62: 345 return fmt.Sprintf("Conntrack lookup 2/2: %s", ctLookup4Info2(n)) 346 case DbgCTCreated6: 347 return fmt.Sprintf("Conntrack create: %s", ctCreate6Info(n)) 348 case DbgSkipProxy: 349 return fmt.Sprintf("Skipping proxy, tc_index is set=%x", n.Arg1) 350 case DbgL4Create: 351 return fmt.Sprintf("Matched L4 policy; creating conntrack %s", l4CreateInfo(n)) 352 case DbgIPIDMapFailed4: 353 return fmt.Sprintf("Failed to map daddr=%s to identity", ip4Str(n.Arg1)) 354 case DbgIPIDMapFailed6: 355 return fmt.Sprintf("Failed to map daddr.p4=[::%s] to identity", ip6Str(n.Arg1)) 356 case DbgIPIDMapSucceed4: 357 return fmt.Sprintf("Successfully mapped daddr=%s to identity=%d", ip4Str(n.Arg1), n.Arg2) 358 case DbgIPIDMapSucceed6: 359 return fmt.Sprintf("Successfully mapped daddr.p4=[::%s] to identity=%d", ip6Str(n.Arg1), n.Arg2) 360 case DbgLbStaleCT: 361 return fmt.Sprintf("Stale CT entry found stale_ct.rev_nat_id=%d, svc.rev_nat_id=%d", n.Arg2, n.Arg1) 362 case DbgInheritIdentity: 363 return fmt.Sprintf("Inheriting identity=%d from stack", n.Arg1) 364 default: 365 return fmt.Sprintf("Unknown message type=%d arg1=%d arg2=%d", n.SubType, n.Arg1, n.Arg2) 366 } 367 } 368 369 func (n *DebugMsg) getJSON(cpuPrefix string) string { 370 return fmt.Sprintf(`{"cpu":%q,"type":"debug","message":%q}`, 371 cpuPrefix, n.subTypeString()) 372 } 373 374 // DumpJSON prints notification in json format 375 func (n *DebugMsg) DumpJSON(cpuPrefix string) { 376 fmt.Println(n.getJSON(cpuPrefix)) 377 } 378 379 const ( 380 // DebugCaptureLen is the amount of packet data in a packet capture message 381 DebugCaptureLen = 24 382 ) 383 384 // DebugCapture is the metadata sent along with a captured packet frame 385 type DebugCapture struct { 386 Type uint8 387 SubType uint8 388 // Source, if populated, is the ID of the source endpoint. 389 Source uint16 390 Hash uint32 391 Len uint32 392 OrigLen uint32 393 Arg1 uint32 394 Arg2 uint32 395 // data 396 } 397 398 // DumpInfo prints a summary of the capture messages. 399 func (n *DebugCapture) DumpInfo(data []byte) { 400 prefix := n.infoPrefix() 401 402 if len(prefix) > 0 { 403 fmt.Printf("%s: %s\n", prefix, GetConnectionSummary(data[DebugCaptureLen:])) 404 } 405 } 406 407 func (n *DebugCapture) infoPrefix() string { 408 switch n.SubType { 409 case DbgCaptureDelivery: 410 return fmt.Sprintf("-> %s", ifname(int(n.Arg1))) 411 412 case DbgCaptureFromLb: 413 return fmt.Sprintf("<- load-balancer %s", ifname(int(n.Arg1))) 414 415 case DbgCaptureAfterV46: 416 return fmt.Sprintf("== v4->v6 %d", n.Arg1) 417 418 case DbgCaptureAfterV64: 419 return fmt.Sprintf("== v6->v4 %d", n.Arg1) 420 421 case DbgCaptureProxyPost: 422 return fmt.Sprintf("-> proxy port %d", byteorder.NetworkToHost(uint16(n.Arg1))) 423 default: 424 return "" 425 } 426 } 427 428 // DumpVerbose prints the captured packet in human readable format 429 func (n *DebugCapture) DumpVerbose(dissect bool, data []byte, prefix string) { 430 fmt.Printf("%s MARK %#x FROM %d DEBUG: %d bytes, ", prefix, n.Hash, n.Source, n.Len) 431 fmt.Println(n.subTypeString()) 432 433 if n.Len > 0 && len(data) > DebugCaptureLen { 434 Dissect(dissect, data[DebugCaptureLen:]) 435 } 436 } 437 438 func (n *DebugCapture) subTypeString() string { 439 switch n.SubType { 440 case DbgCaptureDelivery: 441 return fmt.Sprintf("Delivery to ifindex %d", n.Arg1) 442 case DbgCaptureFromLb: 443 return fmt.Sprintf("Incoming packet to load balancer on ifindex %d", n.Arg1) 444 case DbgCaptureAfterV46: 445 return fmt.Sprintf("Packet after nat46 ifindex %d", n.Arg1) 446 case DbgCaptureAfterV64: 447 return fmt.Sprintf("Packet after nat64 ifindex %d", n.Arg1) 448 case DbgCaptureProxyPre: 449 return fmt.Sprintf("Packet to proxy port %d (Pre)", byteorder.NetworkToHost(uint16(n.Arg1))) 450 case DbgCaptureProxyPost: 451 return fmt.Sprintf("Packet to proxy port %d (Post)", byteorder.NetworkToHost(uint16(n.Arg1))) 452 case DbgCaptureSnatPre: 453 return fmt.Sprintf("Packet going into snat engine on ifindex %d", n.Arg1) 454 case DbgCaptureSnatPost: 455 return fmt.Sprintf("Packet coming from snat engine on ifindex %d", n.Arg1) 456 default: 457 return fmt.Sprintf("Unknown message type=%d arg1=%d", n.SubType, n.Arg1) 458 } 459 } 460 461 func (n *DebugCapture) getJSON(data []byte, cpuPrefix string) (string, error) { 462 463 v := DebugCaptureToVerbose(n) 464 v.CPUPrefix = cpuPrefix 465 v.Summary = GetConnectionSummary(data[DebugCaptureLen:]) 466 467 ret, err := json.Marshal(v) 468 return string(ret), err 469 } 470 471 // DumpJSON prints notification in json format 472 func (n *DebugCapture) DumpJSON(data []byte, cpuPrefix string) { 473 resp, err := n.getJSON(data, cpuPrefix) 474 if err != nil { 475 fmt.Println(fmt.Sprintf(`{"type":"debug_capture_error","message":%q}`, err.Error())) 476 return 477 } 478 fmt.Println(resp) 479 } 480 481 // DebugCaptureVerbose represents a json notification printed by monitor 482 type DebugCaptureVerbose struct { 483 CPUPrefix string `json:"cpu,omitempty"` 484 Type string `json:"type,omitempty"` 485 Mark string `json:"mark,omitempty"` 486 Message string `json:"message,omitempty"` 487 Prefix string `json:"prefix,omitempty"` 488 489 Source uint16 `json:"source"` 490 Bytes uint32 `json:"bytes"` 491 492 Summary string `json:"summary,omitempty"` 493 } 494 495 // DebugCaptureToVerbose creates verbose notification from base TraceNotify 496 func DebugCaptureToVerbose(n *DebugCapture) DebugCaptureVerbose { 497 return DebugCaptureVerbose{ 498 Type: "capture", 499 Mark: fmt.Sprintf("%#x", n.Hash), 500 Source: n.Source, 501 Bytes: n.Len, 502 Message: n.subTypeString(), 503 Prefix: n.infoPrefix(), 504 } 505 }