github.com/aporeto-inc/trireme-lib@v10.358.0+incompatible/controller/internal/enforcer/nfqdatapath/nflog/nflog_common.go (about)

     1  package nflog
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"net"
     7  	"strconv"
     8  	"strings"
     9  
    10  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    11  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/counters"
    12  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packet"
    13  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext"
    14  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    15  	"go.aporeto.io/enforcerd/trireme-lib/utils/cache"
    16  	"go.uber.org/zap"
    17  )
    18  
    19  // NFLogger provides an interface for NFLog
    20  type NFLogger interface {
    21  	Run(ctx context.Context)
    22  }
    23  
    24  // GetPUContextFunc provides PU information given the id
    25  type GetPUContextFunc func(hash string) (*pucontext.PUContext, error)
    26  
    27  func recordCounters(protocol uint8, dstport uint16, srcport uint16, pu *pucontext.PUContext, puIsSource bool) {
    28  	switch protocol {
    29  	case packet.IPProtocolTCP:
    30  		pu.Counters().IncrementCounter(counters.ErrDroppedTCPPackets)
    31  	case packet.IPProtocolUDP:
    32  		pu.Counters().IncrementCounter(counters.ErrDroppedUDPPackets)
    33  		if puIsSource {
    34  			switch dstport {
    35  			case 53:
    36  				pu.Counters().IncrementCounter(counters.ErrDroppedDNSPackets)
    37  			case 67, 68:
    38  				pu.Counters().IncrementCounter(counters.ErrDroppedDHCPPackets)
    39  			case 123:
    40  				pu.Counters().IncrementCounter(counters.ErrDroppedNTPPackets)
    41  			}
    42  		} else {
    43  			switch srcport {
    44  			case 53:
    45  				pu.Counters().IncrementCounter(counters.ErrDroppedDNSPackets)
    46  			case 67, 68:
    47  				pu.Counters().IncrementCounter(counters.ErrDroppedDHCPPackets)
    48  			case 123:
    49  				pu.Counters().IncrementCounter(counters.ErrDroppedNTPPackets)
    50  			}
    51  		}
    52  
    53  	case packet.IPProtocolICMP:
    54  		pu.Counters().IncrementCounter(counters.ErrDroppedICMPPackets)
    55  
    56  	}
    57  }
    58  
    59  func recordDroppedPacket(payload []byte, protocol uint8, srcIP, dstIP net.IP, srcPort, dstPort uint16, pu *pucontext.PUContext, puIsSource bool) (*collector.PacketReport, error) {
    60  
    61  	report := &collector.PacketReport{}
    62  
    63  	report.PUID = pu.ManagementID()
    64  	report.Namespace = pu.ManagementNamespace()
    65  	ipPacket, err := packet.New(packet.PacketTypeNetwork, payload, "", false)
    66  	if err == nil {
    67  		report.Length = int(ipPacket.GetIPLength())
    68  		report.PacketID, _ = strconv.Atoi(ipPacket.ID())
    69  
    70  	} else {
    71  		zap.L().Debug("payload not valid", zap.Error(err))
    72  		return nil, err
    73  	}
    74  	recordCounters(protocol, dstPort, srcPort, pu, puIsSource)
    75  	if protocol == packet.IPProtocolTCP || protocol == packet.IPProtocolUDP {
    76  		report.SourcePort = int(srcPort)
    77  		report.DestinationPort = int(dstPort)
    78  	}
    79  	if protocol == packet.IPProtocolTCP {
    80  		report.TCPFlags = int(ipPacket.GetTCPFlags())
    81  	}
    82  	report.Protocol = int(protocol)
    83  	report.DestinationIP = dstIP.String()
    84  	report.SourceIP = srcIP.String()
    85  	report.TriremePacket = false
    86  	report.DropReason = collector.PacketDrop
    87  
    88  	if payload == nil {
    89  		report.Payload = []byte{}
    90  		return report, nil
    91  	}
    92  	if len(payload) <= 64 {
    93  		report.Payload = make([]byte, len(payload))
    94  		copy(report.Payload, payload)
    95  
    96  	} else {
    97  		report.Payload = make([]byte, 64)
    98  		copy(report.Payload, payload[0:64])
    99  	}
   100  
   101  	return report, nil
   102  }
   103  
   104  func recordFromNFLogData(payload []byte, prefix string, protocol uint8, srcIP, dstIP net.IP, srcPort, dstPort uint16, getPUContext GetPUContextFunc, puIsSource bool) (*collector.FlowRecord, *collector.PacketReport, error) {
   105  
   106  	var packetReport *collector.PacketReport
   107  	var err error
   108  
   109  	var hashID string
   110  	var policyID string
   111  	var extNetworkID string
   112  	var ruleName string
   113  	var encodedAction string
   114  
   115  	parts := strings.Split(prefix, ":")
   116  	switch len(parts) {
   117  	case 4:
   118  		// hashID:policyID:extNetworkID:action
   119  		hashID, policyID, extNetworkID, encodedAction = parts[0], parts[1], parts[2], parts[3]
   120  	case 5:
   121  		// hashID:policyID:extNetworkID:ruleName:action
   122  		hashID, policyID, extNetworkID, ruleName, encodedAction = parts[0], parts[1], parts[2], parts[3], parts[4]
   123  	default:
   124  		return nil, nil, fmt.Errorf("nflog: prefix doesn't contain sufficient information: %s", prefix)
   125  	}
   126  
   127  	pu, err := getPUContext(hashID)
   128  	if err != nil {
   129  		return nil, nil, err
   130  	}
   131  
   132  	// If we have a rule name, then look up the long version of logging prefix
   133  	if len(ruleName) > 0 {
   134  		realPrefix, ok := pu.LookupLogPrefix(policyID + ":" + extNetworkID + ":" + ruleName)
   135  		if !ok {
   136  			return nil, nil, fmt.Errorf("nflog: prefix not found in pucontext mapping: %s", prefix)
   137  		}
   138  		parts = strings.SplitN(realPrefix, ":", 3)
   139  		if len(parts) != 3 {
   140  			return nil, nil, fmt.Errorf("nflog: realPrefix doesn't contain sufficient information: %s", realPrefix)
   141  		}
   142  		policyID, extNetworkID, ruleName = parts[0], parts[1], parts[2]
   143  	}
   144  
   145  	if encodedAction == "10" {
   146  		packetReport, err = recordDroppedPacket(payload, protocol, srcIP, dstIP, srcPort, dstPort, pu, puIsSource)
   147  		return nil, packetReport, err
   148  	}
   149  
   150  	action, observedActionType, err := policy.EncodedStringToAction(encodedAction)
   151  	if err != nil {
   152  		return nil, packetReport, fmt.Errorf("nflog: unable to decode action for context id: %s (%s)", pu.ID(), encodedAction)
   153  	}
   154  
   155  	dropReason := ""
   156  	if action.Rejected() {
   157  		dropReason = collector.PolicyDrop
   158  	}
   159  
   160  	// point fix for now.
   161  	var destination collector.EndPoint
   162  	if protocol == packet.IPProtocolUDP || protocol == packet.IPProtocolTCP {
   163  		destination = collector.EndPoint{
   164  			IP:   dstIP.String(),
   165  			Port: dstPort,
   166  		}
   167  	} else {
   168  		destination = collector.EndPoint{
   169  			IP: dstIP.String(),
   170  		}
   171  	}
   172  
   173  	record := &collector.FlowRecord{
   174  		ContextID: pu.ID(),
   175  		Source: collector.EndPoint{
   176  			IP: srcIP.String(),
   177  		},
   178  		Destination: destination,
   179  		DropReason:  dropReason,
   180  		PolicyID:    policyID,
   181  		Tags:        pu.Annotations().GetSlice(),
   182  		Action:      action | policy.Log, // Add the logging flag back
   183  		L4Protocol:  protocol,
   184  		Namespace:   pu.ManagementNamespace(),
   185  		Count:       1,
   186  		RuleName:    ruleName,
   187  	}
   188  
   189  	if action.Observed() {
   190  		record.ObservedAction = action
   191  		record.ObservedPolicyID = policyID
   192  		record.ObservedActionType = observedActionType
   193  	}
   194  
   195  	if puIsSource {
   196  		record.Source.Type = collector.EndPointTypePU
   197  		record.Source.ID = pu.ManagementID()
   198  		record.Destination.Type = collector.EndPointTypeExternalIP
   199  		record.Destination.ID = extNetworkID
   200  	} else {
   201  		record.Source.Type = collector.EndPointTypeExternalIP
   202  		record.Source.ID = extNetworkID
   203  		record.Destination.Type = collector.EndPointTypePU
   204  		record.Destination.ID = pu.ManagementID()
   205  	}
   206  
   207  	return record, packetReport, nil
   208  }
   209  
   210  func handleFlowReport(flowReportCache cache.DataStore, eventCollector collector.EventCollector, record *collector.FlowRecord, puIsSource bool) {
   211  
   212  	if record == nil {
   213  		return
   214  	}
   215  
   216  	uniqueKey := fmt.Sprintf("%d:%s:%d:%s:%d",
   217  		record.L4Protocol, record.Source.IP, record.Source.Port, record.Destination.IP, record.Destination.Port)
   218  
   219  	// If the flow record is ObserveContinue
   220  	if record.ObservedActionType.ObserveContinue() {
   221  
   222  		// If another observed continue policy is reported, then we ignore it.
   223  		if _, err := flowReportCache.Get(uniqueKey); err == nil {
   224  			return
   225  		}
   226  
   227  		// Add the observed policy report to the cache
   228  		err := flowReportCache.Add(uniqueKey, record)
   229  		if err != nil {
   230  			eventCollector.CollectFlowEvent(record)
   231  			zap.L().Error("handleFlowReport: unable to add flow record to cache", zap.Error(err))
   232  		}
   233  		return
   234  	}
   235  
   236  	// See if there was an ObserveContinue policy
   237  	value, err := flowReportCache.Get(uniqueKey)
   238  	if err == nil {
   239  		report := value.(*collector.FlowRecord)
   240  		record.ObservedAction = report.ObservedAction
   241  		record.ObservedPolicyID = report.ObservedPolicyID
   242  		record.ObservedActionType = report.ObservedActionType
   243  		if puIsSource {
   244  			record.Destination.ID = report.Destination.ID
   245  			record.Destination.Type = report.Destination.Type
   246  		} else {
   247  			record.Source.ID = report.Source.ID
   248  			record.Source.Type = report.Source.Type
   249  		}
   250  		err = flowReportCache.Remove(uniqueKey)
   251  		if err != nil {
   252  			zap.L().Error("handleFlowReport: failed to remove flow from cache", zap.Error(err))
   253  		}
   254  	}
   255  	eventCollector.CollectFlowEvent(record)
   256  }