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

     1  // +build linux
     2  
     3  package nfqdatapath
     4  
     5  // Go libraries
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strconv"
    10  	"time"
    11  
    12  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/connection"
    13  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/counters"
    14  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packet"
    15  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    16  	"go.aporeto.io/enforcerd/trireme-lib/utils/constants"
    17  	markconstants "go.aporeto.io/enforcerd/trireme-lib/utils/constants"
    18  	nfqueue "go.aporeto.io/netlink-go/nfqueue"
    19  	"go.uber.org/zap"
    20  )
    21  
    22  const (
    23  	// nfq actions.
    24  	allow  = 1
    25  	drop   = 0
    26  	repeat = 4
    27  )
    28  
    29  const (
    30  	// max
    31  	maxTriesNfq = 5
    32  )
    33  
    34  func (d *Datapath) errorCallback(err error, _ interface{}) {
    35  	zap.L().Error("Error while processing packets on queue", zap.Error(err))
    36  }
    37  
    38  func (d *Datapath) callback(packet *nfqueue.NFPacket, _ interface{}) {
    39  	packet.Mark = packet.Mark - int(packet.QueueHandle.QueueNum)*constants.QueueBalanceFactor
    40  
    41  	if packet.Mark == int(constants.DefaultInputMark) {
    42  		d.processNetworkPacketsFromNFQ(packet)
    43  		return
    44  	}
    45  
    46  	packet.Mark = packet.Mark / d.filterQueue.GetNumQueues()
    47  	d.processApplicationPacketsFromNFQ(packet)
    48  }
    49  
    50  func (d *Datapath) startInterceptor(ctx context.Context) {
    51  
    52  	var err error
    53  LOOP:
    54  	for i := 0; i < d.filterQueue.GetNumQueues(); i++ {
    55  		// Initialize all the queues
    56  		for tries := 0; tries < maxTriesNfq; tries++ {
    57  			if _, err = nfqueue.CreateAndStartNfQueue(ctx, uint16(i), 4096, nfqueue.NfDefaultPacketSize, d.callback, d.errorCallback, nil); err == nil {
    58  				continue LOOP
    59  			}
    60  
    61  			time.Sleep(1 * time.Second)
    62  		}
    63  
    64  		zap.L().Fatal("Unable to initialize netfilter queue", zap.Error(err))
    65  	}
    66  }
    67  
    68  // processNetworkPacketsFromNFQ processes packets arriving from the network in an NF queue
    69  func (d *Datapath) processNetworkPacketsFromNFQ(p *nfqueue.NFPacket) {
    70  	var processError error
    71  	var tcpConn *connection.TCPConnection
    72  	var udpConn *connection.UDPConnection
    73  	var processAfterVerdict func()
    74  
    75  	netPacket := &packet.Packet{}
    76  	err := netPacket.NewPacket(packet.PacketTypeNetwork, p.Buffer, strconv.Itoa(p.Mark), true)
    77  	if err != nil {
    78  		counters.CounterError(counters.ErrCorruptPacket, err) //nolint
    79  		zap.L().Debug("Dropping corrupted packet on network path", zap.Error(err))
    80  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), drop, 0, 0, uint32(p.ID), []byte{0})
    81  		return
    82  	} else if netPacket.IPProto() == packet.IPProtocolTCP {
    83  		tcpConn, processAfterVerdict, processError = d.processNetworkTCPPackets(netPacket)
    84  	} else if netPacket.IPProto() == packet.IPProtocolUDP {
    85  		udpConn, processError = d.ProcessNetworkUDPPacket(netPacket)
    86  	} else if netPacket.IPProto() == packet.IPProtocolICMP {
    87  		icmpType, icmpCode := netPacket.GetICMPTypeCode()
    88  		context, err := d.contextFromIP(false, netPacket.Mark, 0, packet.IPProtocolICMP)
    89  
    90  		if err == nil {
    91  			action := d.processNetworkICMPPacket(context, netPacket, icmpType, icmpCode)
    92  			if action == icmpAccept {
    93  				p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, 0, uint32(len(netPacket.GetBuffer(0))), uint32(p.ID), netPacket.GetBuffer(0))
    94  				return
    95  			}
    96  		}
    97  
    98  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), drop, 0, uint32(len(netPacket.GetBuffer(0))), uint32(p.ID), netPacket.GetBuffer(0))
    99  		zap.L().Debug("dropping Network ICMP Packet",
   100  			zap.Error(err),
   101  			zap.String("SourceIP", netPacket.SourceAddress().String()),
   102  			zap.String("DestinationIP", netPacket.DestinationAddress().String()),
   103  			zap.Int8("icmp type", icmpType),
   104  			zap.Int8("icmp code", icmpCode))
   105  
   106  		return
   107  	} else {
   108  		processError = counters.CounterError(counters.ErrInvalidProtocol, fmt.Errorf("Invalid Protocol %d", int(netPacket.IPProto())))
   109  	}
   110  
   111  	// TODO: Use error types and handle it in switch case here
   112  
   113  	if processError != nil {
   114  		if processError != errDropPingNetSynAck && processError != errHandshakePacket && processError != errDropQueuedPacket {
   115  			zap.L().Debug("Dropping packet on network path",
   116  				zap.Error(processError),
   117  				zap.String("SourceIP", netPacket.SourceAddress().String()),
   118  				zap.String("DestinationIP", netPacket.DestinationAddress().String()),
   119  				zap.Int("SourcePort", int(netPacket.SourcePort())),
   120  				zap.Int("DestinationPort", int(netPacket.DestPort())),
   121  				zap.Int("Protocol", int(netPacket.IPProto())),
   122  				zap.String("Flags", packet.TCPFlagsToStr(netPacket.GetTCPFlags())),
   123  			)
   124  		}
   125  
   126  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), drop, uint32(p.Mark), 0, uint32(p.ID), []byte{0})
   127  
   128  		if processError != errDropPingNetSynAck {
   129  			if netPacket.IPProto() == packet.IPProtocolTCP {
   130  				d.collectTCPPacket(&debugpacketmessage{
   131  					Mark:    p.Mark,
   132  					p:       netPacket,
   133  					tcpConn: tcpConn,
   134  					udpConn: nil,
   135  					err:     processError,
   136  					network: true,
   137  				})
   138  			} else if netPacket.IPProto() == packet.IPProtocolUDP {
   139  				d.collectUDPPacket(&debugpacketmessage{
   140  					Mark:    p.Mark,
   141  					p:       netPacket,
   142  					tcpConn: nil,
   143  					udpConn: udpConn,
   144  					err:     processError,
   145  					network: true,
   146  				})
   147  			}
   148  		}
   149  
   150  		return
   151  	}
   152  
   153  	if netPacket.IPProto() == packet.IPProtocolTCP {
   154  		if netPacket.SetConnmark {
   155  			p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), repeat, markconstants.PacketMarkToSetConnmark, uint32(len(netPacket.GetBuffer(0))), uint32(p.ID), netPacket.GetBuffer(0))
   156  		} else {
   157  			if d.serviceMeshType == policy.Istio {
   158  				p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, markconstants.IstioPacketMark, uint32(len(netPacket.GetBuffer(0))), uint32(p.ID), netPacket.GetBuffer(0))
   159  			} else {
   160  				p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, 0, uint32(len(netPacket.GetBuffer(0))), uint32(p.ID), netPacket.GetBuffer(0))
   161  			}
   162  		}
   163  	} else {
   164  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, 0, uint32(len(netPacket.GetBuffer(0))), uint32(p.ID), netPacket.GetBuffer(0))
   165  	}
   166  
   167  	if processAfterVerdict != nil {
   168  		processAfterVerdict()
   169  	}
   170  
   171  	if netPacket.IPProto() == packet.IPProtocolTCP {
   172  		d.collectTCPPacket(&debugpacketmessage{
   173  			Mark:    p.Mark,
   174  			p:       netPacket,
   175  			tcpConn: tcpConn,
   176  			udpConn: nil,
   177  			err:     nil,
   178  			network: true,
   179  		})
   180  	} else if netPacket.IPProto() == packet.IPProtocolUDP {
   181  		d.collectUDPPacket(&debugpacketmessage{
   182  			Mark:    p.Mark,
   183  			p:       netPacket,
   184  			tcpConn: nil,
   185  			udpConn: udpConn,
   186  			err:     nil,
   187  			network: true,
   188  		})
   189  	}
   190  
   191  }
   192  
   193  // processApplicationPackets processes packets arriving from an application and are destined to the network
   194  func (d *Datapath) processApplicationPacketsFromNFQ(p *nfqueue.NFPacket) {
   195  
   196  	var processError error
   197  	var tcpConn *connection.TCPConnection
   198  	var udpConn *connection.UDPConnection
   199  
   200  	appPacket := &packet.Packet{}
   201  	err := appPacket.NewPacket(packet.PacketTypeApplication, p.Buffer, strconv.Itoa(p.Mark), true)
   202  
   203  	if err != nil {
   204  		zap.L().Debug("Dropping corrupted packet on application path", zap.Error(err))
   205  		counters.CounterError(counters.ErrCorruptPacket, err) //nolint
   206  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), drop, 0, 0, uint32(p.ID), []byte{0})
   207  		return
   208  	} else if appPacket.IPProto() == packet.IPProtocolTCP {
   209  		tcpConn, processError = d.processApplicationTCPPackets(appPacket)
   210  	} else if appPacket.IPProto() == packet.IPProtocolUDP {
   211  		udpConn, processError = d.ProcessApplicationUDPPacket(appPacket)
   212  	} else if appPacket.IPProto() == packet.IPProtocolICMP {
   213  		icmpType, icmpCode := appPacket.GetICMPTypeCode()
   214  		context, err := d.contextFromIP(true, appPacket.Mark, 0, packet.IPProtocolICMP)
   215  
   216  		if err == nil {
   217  			action := d.processApplicationICMPPacket(context, appPacket, icmpType, icmpCode)
   218  			if action == icmpAccept {
   219  				p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, 0, uint32(len(appPacket.GetBuffer(0))), uint32(p.ID), appPacket.GetBuffer(0))
   220  				return
   221  			}
   222  		}
   223  
   224  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), drop, 0, uint32(len(appPacket.GetBuffer(0))), uint32(p.ID), appPacket.GetBuffer(0))
   225  		zap.L().Debug("dropping Application ICMP Packet",
   226  			zap.Error(err),
   227  			zap.String("SourceIP", appPacket.SourceAddress().String()),
   228  			zap.String("DestinationIP", appPacket.DestinationAddress().String()),
   229  			zap.Int8("icmp type", icmpType),
   230  			zap.Int8("icmp code", icmpCode))
   231  
   232  		return
   233  	} else {
   234  		processError = counters.CounterError(counters.ErrInvalidProtocol, fmt.Errorf("Invalid Protocol %d", int(appPacket.IPProto())))
   235  	}
   236  
   237  	if processError != nil {
   238  		if processError != errHandshakePacket && processError != errDropQueuedPacket {
   239  
   240  			zap.L().Debug("Dropping packet on app path",
   241  				zap.Error(processError),
   242  				zap.String("SourceIP", appPacket.SourceAddress().String()),
   243  				zap.String("DestinationIP", appPacket.DestinationAddress().String()),
   244  				zap.Int("SourcePort", int(appPacket.SourcePort())),
   245  				zap.Int("DestinationPort", int(appPacket.DestPort())),
   246  				zap.Int("Protocol", int(appPacket.IPProto())),
   247  				zap.String("Flags", packet.TCPFlagsToStr(appPacket.GetTCPFlags())),
   248  			)
   249  		}
   250  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), drop, uint32(p.Mark), 0, uint32(p.ID), []byte{0})
   251  
   252  		if appPacket.IPProto() == packet.IPProtocolTCP {
   253  			d.collectTCPPacket(&debugpacketmessage{
   254  				Mark:    p.Mark,
   255  				p:       appPacket,
   256  				tcpConn: tcpConn,
   257  				udpConn: nil,
   258  				err:     processError,
   259  				network: false,
   260  			})
   261  
   262  		} else if appPacket.IPProto() == packet.IPProtocolUDP {
   263  			d.collectUDPPacket(&debugpacketmessage{
   264  				Mark:    p.Mark,
   265  				p:       appPacket,
   266  				tcpConn: nil,
   267  				udpConn: udpConn,
   268  				err:     processError,
   269  				network: false,
   270  			})
   271  		}
   272  		return
   273  	}
   274  
   275  	if appPacket.IPProto() == packet.IPProtocolTCP {
   276  		if appPacket.SetConnmark {
   277  			p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), repeat, markconstants.PacketMarkToSetConnmark, uint32(len(appPacket.GetBuffer(0))), uint32(p.ID), appPacket.GetBuffer(0))
   278  		} else {
   279  			if d.serviceMeshType == policy.Istio {
   280  				p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, markconstants.IstioPacketMark, uint32(len(appPacket.GetBuffer(0))), uint32(p.ID), appPacket.GetBuffer(0))
   281  			} else {
   282  				p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, 0, uint32(len(appPacket.GetBuffer(0))), uint32(p.ID), appPacket.GetBuffer(0))
   283  			}
   284  		}
   285  	} else {
   286  		p.QueueHandle.SetVerdict2(uint32(p.QueueHandle.QueueNum), allow, 0, uint32(len(appPacket.GetBuffer(0))), uint32(p.ID), appPacket.GetBuffer(0))
   287  	}
   288  
   289  	if appPacket.IPProto() == packet.IPProtocolTCP {
   290  		var id string
   291  		if tcpConn != nil {
   292  			id = tcpConn.Context.ID()
   293  		} else if d.puFromIP != nil {
   294  			id = d.puFromIP.ID()
   295  		}
   296  
   297  		if _, err = d.packetTracingCache.Get(id); err == nil {
   298  			d.collectTCPPacket(&debugpacketmessage{
   299  				Mark:    p.Mark,
   300  				p:       appPacket,
   301  				tcpConn: tcpConn,
   302  				udpConn: nil,
   303  				err:     nil,
   304  				network: false,
   305  			})
   306  		}
   307  
   308  	} else if appPacket.IPProto() == packet.IPProtocolUDP {
   309  		d.collectUDPPacket(&debugpacketmessage{
   310  			Mark:    p.Mark,
   311  			p:       appPacket,
   312  			tcpConn: nil,
   313  			udpConn: udpConn,
   314  			err:     nil,
   315  			network: false,
   316  		})
   317  	}
   318  }
   319  
   320  func (d *Datapath) cleanupPlatform() {}