github.com/fafucoder/cilium@v1.6.11/pkg/monitor/dissect.go (about) 1 // Copyright 2016-2017 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/hex" 19 "fmt" 20 "net" 21 "strconv" 22 23 "github.com/cilium/cilium/pkg/lock" 24 "github.com/cilium/cilium/pkg/logging" 25 "github.com/cilium/cilium/pkg/logging/logfields" 26 27 "github.com/google/gopacket" 28 "github.com/google/gopacket/layers" 29 ) 30 31 type parserCache struct { 32 eth layers.Ethernet 33 ip4 layers.IPv4 34 ip6 layers.IPv6 35 icmp4 layers.ICMPv4 36 icmp6 layers.ICMPv6 37 tcp layers.TCP 38 udp layers.UDP 39 decoded []gopacket.LayerType 40 } 41 42 var ( 43 cache *parserCache 44 dissectLock lock.Mutex 45 parser *gopacket.DecodingLayerParser 46 47 log = logging.DefaultLogger.WithField(logfields.LogSubsys, "monitor") 48 ) 49 50 // getParser must be called with dissectLock held 51 func initParser() { 52 if cache == nil { 53 log.Info("Initializing dissection cache...") 54 55 cache = &parserCache{ 56 decoded: []gopacket.LayerType{}, 57 } 58 59 parser = gopacket.NewDecodingLayerParser( 60 layers.LayerTypeEthernet, 61 &cache.eth, &cache.ip4, &cache.ip6, 62 &cache.icmp4, &cache.icmp6, &cache.tcp, &cache.udp) 63 } 64 } 65 66 func getTCPInfo() string { 67 info := "" 68 addTCPFlag := func(flag, new string) string { 69 if flag == "" { 70 return new 71 } 72 return flag + ", " + new 73 } 74 75 if cache.tcp.SYN { 76 info = addTCPFlag(info, "SYN") 77 } 78 79 if cache.tcp.ACK { 80 info = addTCPFlag(info, "ACK") 81 } 82 83 if cache.tcp.RST { 84 info = addTCPFlag(info, "RST") 85 } 86 87 if cache.tcp.FIN { 88 info = addTCPFlag(info, "FIN") 89 } 90 91 return info 92 } 93 94 // GetConnectionSummary decodes the data into layers and returns a connection 95 // summary in the format: 96 // 97 // - sIP:sPort -> dIP:dPort, e.g. 1.1.1.1:2000 -> 2.2.2.2:80 98 // - sIP -> dIP icmpCode, 1.1.1.1 -> 2.2.2.2 echo-request 99 func GetConnectionSummary(data []byte) string { 100 dissectLock.Lock() 101 defer dissectLock.Unlock() 102 103 initParser() 104 parser.DecodeLayers(data, &cache.decoded) 105 106 var ( 107 srcIP, dstIP net.IP 108 srcPort, dstPort string 109 icmpCode, proto string 110 hasIP, hasEth bool 111 ) 112 113 for _, typ := range cache.decoded { 114 switch typ { 115 case layers.LayerTypeEthernet: 116 hasEth = true 117 case layers.LayerTypeIPv4: 118 hasIP = true 119 srcIP, dstIP = cache.ip4.SrcIP, cache.ip4.DstIP 120 case layers.LayerTypeIPv6: 121 hasIP = true 122 srcIP, dstIP = cache.ip6.SrcIP, cache.ip6.DstIP 123 case layers.LayerTypeTCP: 124 proto = "tcp" 125 srcPort, dstPort = strconv.Itoa(int(cache.tcp.SrcPort)), strconv.Itoa(int(cache.tcp.DstPort)) 126 case layers.LayerTypeUDP: 127 proto = "udp" 128 srcPort, dstPort = strconv.Itoa(int(cache.udp.SrcPort)), strconv.Itoa(int(cache.udp.DstPort)) 129 case layers.LayerTypeIPSecAH: 130 proto = "IPsecAH" 131 case layers.LayerTypeIPSecESP: 132 proto = "IPsecESP" 133 case layers.LayerTypeICMPv4: 134 icmpCode = cache.icmp4.TypeCode.String() 135 case layers.LayerTypeICMPv6: 136 icmpCode = cache.icmp6.TypeCode.String() 137 } 138 } 139 140 switch { 141 case icmpCode != "": 142 return fmt.Sprintf("%s -> %s %s", srcIP, dstIP, icmpCode) 143 case proto != "": 144 var s string 145 146 if proto == "esp" { 147 s = proto 148 } else { 149 s = fmt.Sprintf("%s -> %s %s", 150 net.JoinHostPort(srcIP.String(), srcPort), 151 net.JoinHostPort(dstIP.String(), dstPort), 152 proto) 153 } 154 if proto == "tcp" { 155 s += " " + getTCPInfo() 156 } 157 return s 158 case hasIP: 159 return fmt.Sprintf("%s -> %s", srcIP, dstIP) 160 case hasEth: 161 return fmt.Sprintf("%s -> %s %s", cache.eth.SrcMAC, cache.eth.DstMAC, cache.eth.EthernetType.String()) 162 } 163 164 return "[unknown]" 165 } 166 167 // Dissect parses and prints the provided data if dissect is set to true, 168 // otherwise the data is printed as HEX output 169 func Dissect(dissect bool, data []byte) { 170 if dissect { 171 dissectLock.Lock() 172 defer dissectLock.Unlock() 173 174 initParser() 175 parser.DecodeLayers(data, &cache.decoded) 176 177 for _, typ := range cache.decoded { 178 switch typ { 179 case layers.LayerTypeEthernet: 180 fmt.Println(gopacket.LayerString(&cache.eth)) 181 case layers.LayerTypeIPv4: 182 fmt.Println(gopacket.LayerString(&cache.ip4)) 183 case layers.LayerTypeIPv6: 184 fmt.Println(gopacket.LayerString(&cache.ip6)) 185 case layers.LayerTypeTCP: 186 fmt.Println(gopacket.LayerString(&cache.tcp)) 187 case layers.LayerTypeUDP: 188 fmt.Println(gopacket.LayerString(&cache.udp)) 189 case layers.LayerTypeICMPv4: 190 fmt.Println(gopacket.LayerString(&cache.icmp4)) 191 case layers.LayerTypeICMPv6: 192 fmt.Println(gopacket.LayerString(&cache.icmp6)) 193 default: 194 fmt.Println("Unknown layer") 195 } 196 } 197 if parser.Truncated { 198 fmt.Println(" Packet has been truncated") 199 } 200 201 } else { 202 fmt.Print(hex.Dump(data)) 203 } 204 } 205 206 // Flow contains source and destination 207 type Flow struct { 208 Src string `json:"src"` 209 Dst string `json:"dst"` 210 } 211 212 // DissectSummary bundles decoded layers into json-marshallable message 213 type DissectSummary struct { 214 Ethernet string `json:"ethernet,omitempty"` 215 IPv4 string `json:"ipv4,omitempty"` 216 IPv6 string `json:"ipv6,omitempty"` 217 TCP string `json:"tcp,omitempty"` 218 UDP string `json:"udp,omitempty"` 219 ICMPv4 string `json:"icmpv4,omitempty"` 220 ICMPv6 string `json:"icmpv6,omitempty"` 221 L2 *Flow `json:"l2,omitempty"` 222 L3 *Flow `json:"l3,omitempty"` 223 L4 *Flow `json:"l4,omitempty"` 224 } 225 226 // GetDissectSummary returns DissectSummary created from data 227 func GetDissectSummary(data []byte) *DissectSummary { 228 dissectLock.Lock() 229 defer dissectLock.Unlock() 230 231 initParser() 232 parser.DecodeLayers(data, &cache.decoded) 233 234 ret := &DissectSummary{} 235 236 for _, typ := range cache.decoded { 237 switch typ { 238 case layers.LayerTypeEthernet: 239 ret.Ethernet = gopacket.LayerString(&cache.eth) 240 src, dst := cache.eth.LinkFlow().Endpoints() 241 ret.L2 = &Flow{Src: src.String(), Dst: dst.String()} 242 case layers.LayerTypeIPv4: 243 ret.IPv4 = gopacket.LayerString(&cache.ip4) 244 src, dst := cache.ip4.NetworkFlow().Endpoints() 245 ret.L3 = &Flow{Src: src.String(), Dst: dst.String()} 246 case layers.LayerTypeIPv6: 247 ret.IPv6 = gopacket.LayerString(&cache.ip6) 248 src, dst := cache.ip6.NetworkFlow().Endpoints() 249 ret.L3 = &Flow{Src: src.String(), Dst: dst.String()} 250 case layers.LayerTypeTCP: 251 ret.TCP = gopacket.LayerString(&cache.tcp) 252 src, dst := cache.tcp.TransportFlow().Endpoints() 253 ret.L4 = &Flow{Src: src.String(), Dst: dst.String()} 254 case layers.LayerTypeUDP: 255 ret.UDP = gopacket.LayerString(&cache.udp) 256 src, dst := cache.udp.TransportFlow().Endpoints() 257 ret.L4 = &Flow{Src: src.String(), Dst: dst.String()} 258 case layers.LayerTypeICMPv4: 259 ret.ICMPv4 = gopacket.LayerString(&cache.icmp4) 260 case layers.LayerTypeICMPv6: 261 ret.ICMPv6 = gopacket.LayerString(&cache.icmp6) 262 } 263 } 264 return ret 265 }