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  }