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

     1  package nfqdatapath
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"math/rand"
     8  	"net"
     9  	"os"
    10  	"strconv"
    11  	"syscall"
    12  	"time"
    13  
    14  	"github.com/ghedo/go.pkt/packet/raw"
    15  	"github.com/ghedo/go.pkt/packet/tcp"
    16  	"github.com/ghedo/go.pkt/routing"
    17  	"go.aporeto.io/enforcerd/trireme-lib/collector"
    18  	enforcerconstants "go.aporeto.io/enforcerd/trireme-lib/controller/internal/enforcer/constants"
    19  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/claimsheader"
    20  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/connection"
    21  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packet"
    22  	tpacket "go.aporeto.io/enforcerd/trireme-lib/controller/pkg/packet"
    23  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pingconfig"
    24  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/pucontext"
    25  	"go.aporeto.io/enforcerd/trireme-lib/controller/pkg/tokens"
    26  	"go.aporeto.io/enforcerd/trireme-lib/policy"
    27  	"go.aporeto.io/gaia"
    28  	"go.uber.org/zap"
    29  )
    30  
    31  var (
    32  	// For unit tests.
    33  	srcip          = getSrcIP
    34  	dial           = dialIP
    35  	bind           = bindRandomPort
    36  	close          = closeRandomPort
    37  	randUint32     = rand.Uint32
    38  	since          = time.Since
    39  	isAppListening = isAppListeningInPort
    40  
    41  	removeDelay = 10 * time.Second
    42  	synAckDelay = 3 * time.Second
    43  
    44  	_ io.Writer = &pingConn{}
    45  )
    46  
    47  func (d *Datapath) initiatePingHandshake(_ context.Context, context *pucontext.PUContext, pingConfig *policy.PingConfig) error {
    48  
    49  	zap.L().Debug("Initiating ping (syn)")
    50  
    51  	srcIP, err := srcip(pingConfig.IP)
    52  	if err != nil {
    53  		return fmt.Errorf("unable to get source ip: %v", err)
    54  	}
    55  
    56  	conn, err := dial(srcIP, pingConfig.IP)
    57  	if err != nil {
    58  		return fmt.Errorf("unable to dial on app syn: %v", err)
    59  	}
    60  	defer conn.Close() // nolint: errcheck
    61  
    62  	for i := 0; i < pingConfig.Iterations; i++ {
    63  		if err := d.sendSynPacket(context, pingConfig, conn, srcIP, i); err != nil {
    64  			return err
    65  		}
    66  	}
    67  
    68  	return nil
    69  }
    70  
    71  // sendSynPacket sends tcp syn packet to the socket. It also dispatches a report.
    72  func (d *Datapath) sendSynPacket(context *pucontext.PUContext, pingConfig *policy.PingConfig, conn PingConn, srcIP net.IP, iterationID int) error {
    73  
    74  	tcpConn := connection.NewTCPConnection(context, nil)
    75  	tcpConn.PingConfig = pingconfig.New()
    76  
    77  	srcPort, err := bind(tcpConn)
    78  	if err != nil {
    79  		return fmt.Errorf("unable to bind free source port: %v", err)
    80  	}
    81  
    82  	claimsHeader := claimsheader.NewClaimsHeader(
    83  		claimsheader.OptionPing(true),
    84  	)
    85  
    86  	pingPayload := &policy.PingPayload{
    87  		PingID:        pingConfig.ID,
    88  		IterationID:   iterationID,
    89  		NamespaceHash: context.ManagementNamespaceHash(),
    90  	}
    91  
    92  	var tcpData []byte
    93  	tcpConn.Secrets, tcpConn.Auth.LocalDatapathPrivateKey, tcpData = context.GetSynToken(pingPayload, tcpConn.Auth.Nonce, claimsHeader)
    94  
    95  	seqNum := randUint32()
    96  	p, err := constructTCPPacket(conn, srcIP, pingConfig.IP, srcPort, pingConfig.Port, seqNum, 0, tcp.Syn, tcpData)
    97  	if err != nil {
    98  		return fmt.Errorf("unable to construct syn packet: %v", err)
    99  	}
   100  
   101  	// We always get a default policy.
   102  	_, pkt, _ := context.ApplicationACLPolicyFromAddr(pingConfig.IP, pingConfig.Port, packet.IPProtocolTCP)
   103  
   104  	pingErr := "timeout"
   105  	if e := pingConfig.Error(); e != "" {
   106  		pingErr = e
   107  	}
   108  
   109  	// RequestTimeout report cached in the connection. This will be sent on
   110  	// expiration timeout for this connection.
   111  	tcpConn.PingConfig.SetPingReport(&collector.PingReport{
   112  		PingID:          pingConfig.ID,
   113  		IterationID:     iterationID,
   114  		AgentVersion:    d.agentVersion.String(),
   115  		PayloadSize:     len(tcpData),
   116  		PayloadSizeType: gaia.PingProbePayloadSizeTypeTransmitted,
   117  		Type:            gaia.PingProbeTypeRequest,
   118  		Error:           pingErr,
   119  		PUID:            context.ManagementID(),
   120  		Namespace:       context.ManagementNamespace(),
   121  		Protocol:        tpacket.IPProtocolTCP,
   122  		ServiceType:     "L3",
   123  		FourTuple: flowTuple(
   124  			tpacket.PacketTypeApplication,
   125  			srcIP,
   126  			pingConfig.IP,
   127  			srcPort,
   128  			pingConfig.Port,
   129  		),
   130  		SeqNum:              seqNum,
   131  		TargetTCPNetworks:   pingConfig.TargetTCPNetworks,
   132  		ExcludedNetworks:    pingConfig.ExcludedNetworks,
   133  		RemoteNamespaceType: gaia.PingProbeRemoteNamespaceTypeHash,
   134  		Claims:              context.Identity().GetSlice(),
   135  		ClaimsType:          gaia.PingProbeClaimsTypeTransmitted,
   136  		ACLPolicyID:         pkt.PolicyID,
   137  		ACLPolicyAction:     pkt.Action,
   138  	})
   139  	tcpConn.TCPtuple = &connection.TCPTuple{
   140  		SourceAddress:      srcIP,
   141  		DestinationAddress: pingConfig.IP,
   142  		SourcePort:         srcPort,
   143  		DestinationPort:    pingConfig.Port,
   144  	}
   145  	tcpConn.PingConfig.StartTime = time.Now()
   146  	tcpConn.PingConfig.SetPingID(pingConfig.ID)
   147  	tcpConn.PingConfig.SetIterationID(iterationID)
   148  	tcpConn.PingConfig.SetSeqNum(seqNum)
   149  	tcpConn.SetState(connection.TCPSynSend)
   150  	key := flowTuple(tpacket.PacketTypeApplication, srcIP, pingConfig.IP, srcPort, pingConfig.Port)
   151  
   152  	d.cachePut(d.tcpClient, key, tcpConn)
   153  
   154  	if _, err := conn.Write(p); err != nil {
   155  		return fmt.Errorf("unable to send syn packet: %v", err)
   156  	}
   157  
   158  	return nil
   159  }
   160  
   161  // processPingNetSynPacket should only be called when the packet is recognized as a ping syn packet.
   162  func (d *Datapath) processPingNetSynPacket(
   163  	context *pucontext.PUContext,
   164  	tcpConn *connection.TCPConnection,
   165  	tcpPacket *tpacket.Packet,
   166  	payloadSize int,
   167  	pkt *policy.FlowPolicy,
   168  	claims *tokens.ConnectionClaims,
   169  ) error {
   170  	zap.L().Debug("Processing ping network syn packet", zap.String("conn", tcpPacket.L4FlowHash()))
   171  
   172  	if tcpConn.GetState() == connection.TCPSynReceived || tcpConn.GetState() == connection.TCPSynAckSend {
   173  		zap.L().Debug("Dropping duplicate ping syn packets")
   174  		return errDropPingNetSyn
   175  	}
   176  
   177  	defer func() {
   178  		tcpConn.SetState(connection.TCPSynReceived)
   179  		tcpConn.PingConfig.SetSocketClosed(true)
   180  		tcpConn.PingConfig.SetPingID(claims.P.PingID)
   181  		tcpConn.PingConfig.SetIterationID(claims.P.IterationID)
   182  
   183  		txtID, ok := claims.T.Get(enforcerconstants.TransmitterLabel)
   184  		if !ok {
   185  			zap.L().Warn("missing transmitter label")
   186  		}
   187  
   188  		d.cachePut(d.tcpServer, tcpPacket.L4FlowHash(), tcpConn)
   189  		d.sendRequestRecvReport(txtID, claims.P, tcpPacket, context, pkt, payloadSize, tcpConn.SourceController)
   190  	}()
   191  
   192  	if tcpConn.PingConfig == nil {
   193  		tcpConn.PingConfig = pingconfig.New()
   194  	}
   195  
   196  	listening, err := isAppListening(tcpPacket.DestPort())
   197  	if listening && !pkt.Action.Rejected() {
   198  		zap.L().Debug("Appplication listening", zap.String("conn", tcpPacket.L4FlowHash()), zap.Error(err))
   199  
   200  		time.AfterFunc(synAckDelay, func() {
   201  
   202  			if tcpConn.PingConfig.ApplicationListening() {
   203  				return
   204  			}
   205  
   206  			if err := d.sendSynAckPacket(context, tcpConn, tcpPacket, claims); err != nil {
   207  				zap.L().Error("unable to send synack paket", zap.Error(err))
   208  			}
   209  		})
   210  
   211  		return nil
   212  	}
   213  
   214  	if err := d.sendSynAckPacket(context, tcpConn, tcpPacket, claims); err != nil {
   215  		return err
   216  	}
   217  
   218  	return errDropPingNetSyn
   219  }
   220  
   221  func (d *Datapath) sendSynAckPacket(
   222  	context *pucontext.PUContext,
   223  	tcpConn *connection.TCPConnection,
   224  	tcpPacket *tpacket.Packet,
   225  	claims *tokens.ConnectionClaims,
   226  ) error {
   227  
   228  	claimsHeader := claimsheader.NewClaimsHeader(
   229  		claimsheader.OptionPing(true),
   230  	)
   231  
   232  	pingPayload := &policy.PingPayload{
   233  		PingID:        claims.P.PingID,
   234  		IterationID:   claims.P.IterationID,
   235  		NamespaceHash: context.ManagementNamespaceHash(),
   236  	}
   237  
   238  	claimsNew := &tokens.ConnectionClaims{
   239  		CT:       context.CompressedTags(),
   240  		LCL:      tcpConn.Auth.Nonce[:],
   241  		RMT:      tcpConn.Auth.RemoteNonce,
   242  		DEKV1:    tcpConn.Auth.LocalDatapathPublicKeyV1,
   243  		SDEKV1:   tcpConn.Auth.LocalDatapathPublicKeySignV1,
   244  		DEKV2:    tcpConn.Auth.LocalDatapathPublicKeyV2,
   245  		SDEKV2:   tcpConn.Auth.LocalDatapathPublicKeySignV2,
   246  		ID:       context.ManagementID(),
   247  		RemoteID: tcpConn.Auth.RemoteContextID,
   248  		P:        pingPayload,
   249  	}
   250  
   251  	tcpData, err := d.tokenAccessor.CreateSynAckPacketToken(tcpConn.Auth.Proto314, claimsNew, tcpConn.EncodedBuf[:], tcpConn.Auth.Nonce[:], claimsHeader, tcpConn.Secrets, tcpConn.Auth.SecretKey) //nolint
   252  	if err != nil {
   253  		return fmt.Errorf("unable to create ping synack token: %w", err)
   254  	}
   255  
   256  	conn, err := dial(tcpPacket.DestinationAddress(), tcpPacket.SourceAddress())
   257  	if err != nil {
   258  		return fmt.Errorf("unable to construct synack packet %w", err)
   259  	}
   260  	defer conn.Close() // nolint: errcheck
   261  
   262  	p, err := constructTCPPacket(
   263  		conn,
   264  		tcpPacket.DestinationAddress(),
   265  		tcpPacket.SourceAddress(),
   266  		tcpPacket.DestPort(),
   267  		tcpPacket.SourcePort(),
   268  		randUint32(),
   269  		tcpPacket.TCPSeqNum()+1,
   270  		tcp.Syn|tcp.Ack,
   271  		tcpData,
   272  	)
   273  	if err != nil {
   274  		return fmt.Errorf("unable to construct synack packet: %w", err)
   275  	}
   276  
   277  	if _, err := conn.Write(p); err != nil {
   278  		return fmt.Errorf("unable to send synack packet: %w", err)
   279  	}
   280  
   281  	tcpConn.SetState(connection.TCPSynAckSend)
   282  
   283  	return nil
   284  }
   285  
   286  // processPingNetSynAckPacket should only be called when the packet is recognized as a ping synack packet.
   287  func (d *Datapath) processPingNetSynAckPacket(
   288  	context *pucontext.PUContext,
   289  	tcpConn *connection.TCPConnection,
   290  	tcpPacket *tpacket.Packet,
   291  	payloadSize int,
   292  	pkt *policy.FlowPolicy,
   293  	claims *tokens.ConnectionClaims,
   294  	ext bool,
   295  ) error {
   296  	zap.L().Debug("Processing ping network synack packet",
   297  		zap.Bool("externalNetwork", ext),
   298  		zap.String("conn", tcpPacket.SourcePortHash(packet.PacketTypeNetwork)),
   299  	)
   300  
   301  	if tcpConn.PingConfig == nil {
   302  		return errDropPingNetSynAck
   303  	}
   304  
   305  	receiveTime := since(tcpConn.PingConfig.StartTime).String()
   306  
   307  	defer func() {
   308  		tcpConn.SetState(connection.TCPSynAckReceived)
   309  
   310  		if !tcpConn.PingConfig.SocketClosed() {
   311  			defer func() {
   312  				if err := close(tcpConn); err != nil {
   313  					zap.L().Warn("unable to close socket", zap.Reflect("fd", tcpConn.PingConfig.SocketFd()), zap.Error(err))
   314  				}
   315  			}()
   316  		}
   317  
   318  		time.AfterFunc(removeDelay, func() {
   319  			d.cacheRemove(d.tcpClient, tcpPacket.SourcePortHash(packet.PacketTypeNetwork))
   320  		})
   321  
   322  		if err := respondWithRstPacket(tcpPacket, nil); err != nil {
   323  			zap.L().Warn("unable to send rst packet", zap.Error(err))
   324  		}
   325  	}()
   326  
   327  	// Drop duplicate synack packets.
   328  	if tcpConn.GetState() == connection.TCPSynAckReceived {
   329  		return errDropPingNetSynAck
   330  	}
   331  
   332  	// Synack from externalnetwork.
   333  	if ext {
   334  		d.sendExtResponseRecvReport(
   335  			context,
   336  			receiveTime,
   337  			pkt,
   338  			payloadSize,
   339  			tcpConn,
   340  		)
   341  		return errDropPingNetSynAck
   342  	}
   343  
   344  	txtID, ok := claims.T.Get(enforcerconstants.TransmitterLabel)
   345  	if !ok {
   346  		return fmt.Errorf("missing transmitter label")
   347  	}
   348  
   349  	d.sendResponseRecvReport(
   350  		txtID,
   351  		claims.P,
   352  		context,
   353  		receiveTime,
   354  		pkt,
   355  		payloadSize,
   356  		tcpConn,
   357  		tcpConn.DestinationController,
   358  	)
   359  
   360  	return errDropPingNetSynAck
   361  }
   362  
   363  // respondWithRstPacket sends a rst packet in response to tcpPacket.
   364  func respondWithRstPacket(tcpPacket *tpacket.Packet, payload []byte) error {
   365  
   366  	conn, err := dial(tcpPacket.DestinationAddress(), tcpPacket.SourceAddress())
   367  	if err != nil {
   368  		return fmt.Errorf("unable to dial: %w", err)
   369  	}
   370  	defer conn.Close() // nolint: errcheck
   371  
   372  	p, err := constructTCPPacket(
   373  		conn,
   374  		tcpPacket.DestinationAddress(),
   375  		tcpPacket.SourceAddress(),
   376  		tcpPacket.DestPort(),
   377  		tcpPacket.SourcePort(),
   378  		tcpPacket.TCPAckNum(),
   379  		tcpPacket.TCPSeqNum()+1,
   380  		tcp.Rst,
   381  		payload,
   382  	)
   383  	if err != nil {
   384  		return fmt.Errorf("unable to construct rst packet: %w", err)
   385  	}
   386  
   387  	if _, err := conn.Write(p); err != nil {
   388  		return fmt.Errorf("unable to send rst packet: %w", err)
   389  	}
   390  
   391  	return nil
   392  }
   393  
   394  // sendRequestRecvReport sends a report on syn recv state.
   395  func (d *Datapath) sendRequestRecvReport(
   396  	srcPUID string,
   397  	pingPayload *policy.PingPayload,
   398  	tcpPacket *tpacket.Packet,
   399  	context *pucontext.PUContext,
   400  	pkt *policy.FlowPolicy,
   401  	payloadSize int,
   402  	controller string,
   403  ) {
   404  
   405  	err := ""
   406  	if pkt.Action.Rejected() {
   407  		err = collector.PolicyDrop
   408  	}
   409  
   410  	d.sendPingReport(
   411  		pingPayload.PingID,
   412  		pingPayload.IterationID,
   413  		d.agentVersion.String(),
   414  		tcpPacket.L4FlowHash(),
   415  		"",
   416  		srcPUID,
   417  		context.ManagementID(),
   418  		context.ManagementNamespace(),
   419  		pingPayload.NamespaceHash,
   420  		gaia.PingProbeTypeRequest,
   421  		payloadSize,
   422  		pkt.PolicyID,
   423  		pkt.Action,
   424  		false,
   425  		collector.EndPointTypePU,
   426  		tcpPacket.TCPSeqNum(),
   427  		controller,
   428  		true,
   429  		false,
   430  		context.Identity().GetSlice(),
   431  		"",
   432  		policy.ActionType(0),
   433  		true,
   434  		err,
   435  	)
   436  }
   437  
   438  // sendResponseRecvReport sends a report on synack recv state.
   439  func (d *Datapath) sendResponseRecvReport(
   440  	srcPUID string,
   441  	pingPayload *policy.PingPayload,
   442  	context *pucontext.PUContext,
   443  	rtt string,
   444  	pkt *policy.FlowPolicy,
   445  	payloadSize int,
   446  	tcpConn *connection.TCPConnection,
   447  	controller string,
   448  ) {
   449  
   450  	pingErr := ""
   451  	if !tcpConn.PingConfig.PingReport().TargetTCPNetworks {
   452  		pingErr = policy.ErrTargetTCPNetworks
   453  	}
   454  
   455  	if tcpConn.PingConfig.PingReport().ExcludedNetworks {
   456  		pingErr = policy.ErrExcludedNetworks
   457  	}
   458  
   459  	if pkt.Action.Rejected() {
   460  		pingErr = collector.PolicyDrop
   461  	}
   462  
   463  	d.sendPingReport(
   464  		pingPayload.PingID,
   465  		pingPayload.IterationID,
   466  		d.agentVersion.String(),
   467  		flowTuple(
   468  			tpacket.PacketTypeNetwork,
   469  			tcpConn.TCPtuple.SourceAddress,
   470  			tcpConn.TCPtuple.DestinationAddress,
   471  			tcpConn.TCPtuple.SourcePort,
   472  			tcpConn.TCPtuple.DestinationPort,
   473  		),
   474  		rtt,
   475  		srcPUID,
   476  		context.ManagementID(),
   477  		context.ManagementNamespace(),
   478  		pingPayload.NamespaceHash,
   479  		gaia.PingProbeTypeResponse,
   480  		payloadSize,
   481  		pkt.PolicyID,
   482  		pkt.Action,
   483  		pingPayload.ApplicationListening,
   484  		collector.EndPointTypePU,
   485  		tcpConn.PingConfig.SeqNum(),
   486  		controller,
   487  		tcpConn.PingConfig.PingReport().TargetTCPNetworks,
   488  		tcpConn.PingConfig.PingReport().ExcludedNetworks,
   489  		tcpConn.PingConfig.PingReport().Claims,
   490  		tcpConn.PingConfig.PingReport().ACLPolicyID,
   491  		tcpConn.PingConfig.PingReport().ACLPolicyAction,
   492  		false,
   493  		pingErr,
   494  	)
   495  }
   496  
   497  // sendExtResponseRecvReport sends a report on synack from ext net.
   498  func (d *Datapath) sendExtResponseRecvReport(
   499  	context *pucontext.PUContext,
   500  	rtt string,
   501  	pkt *policy.FlowPolicy,
   502  	payloadSize int,
   503  	tcpConn *connection.TCPConnection,
   504  ) {
   505  	d.sendPingReport(
   506  		tcpConn.PingConfig.PingID(),
   507  		tcpConn.PingConfig.IterationID(),
   508  		d.agentVersion.String(),
   509  		flowTuple(
   510  			tpacket.PacketTypeNetwork,
   511  			tcpConn.TCPtuple.SourceAddress,
   512  			tcpConn.TCPtuple.DestinationAddress,
   513  			tcpConn.TCPtuple.SourcePort,
   514  			tcpConn.TCPtuple.DestinationPort,
   515  		),
   516  		rtt,
   517  		"",
   518  		context.ManagementID(),
   519  		context.ManagementNamespace(),
   520  		"",
   521  		gaia.PingProbeTypeResponse,
   522  		payloadSize,
   523  		pkt.PolicyID,
   524  		pkt.Action,
   525  		true,
   526  		collector.EndPointTypeExternalIP,
   527  		tcpConn.PingConfig.SeqNum(),
   528  		"",
   529  		tcpConn.PingConfig.PingReport().TargetTCPNetworks,
   530  		tcpConn.PingConfig.PingReport().ExcludedNetworks,
   531  		tcpConn.PingConfig.PingReport().Claims,
   532  		tcpConn.PingConfig.PingReport().ACLPolicyID,
   533  		tcpConn.PingConfig.PingReport().ACLPolicyAction,
   534  		false,
   535  		"",
   536  	)
   537  }
   538  
   539  func (d *Datapath) sendPingReport(
   540  	pingID string,
   541  	iterationID int,
   542  	agentVersion,
   543  	fourTuple,
   544  	rtt,
   545  	remoteID,
   546  	puid,
   547  	ns string,
   548  	nsHash string,
   549  	ptype gaia.PingProbeTypeValue,
   550  	payloadSize int,
   551  	policyID string,
   552  	policyAction policy.ActionType,
   553  	appListening bool,
   554  	txType collector.EndPointType,
   555  	seqNum uint32,
   556  	controller string,
   557  	tn,
   558  	en bool,
   559  	claims []string,
   560  	aclPolicyID string,
   561  	aclPolicyAction policy.ActionType,
   562  	isServer bool,
   563  	err string,
   564  ) {
   565  
   566  	report := &collector.PingReport{
   567  		PingID:               pingID,
   568  		IterationID:          iterationID,
   569  		AgentVersion:         agentVersion,
   570  		FourTuple:            fourTuple,
   571  		RTT:                  rtt,
   572  		PayloadSize:          payloadSize,
   573  		PayloadSizeType:      gaia.PingProbePayloadSizeTypeReceived,
   574  		Type:                 ptype,
   575  		PUID:                 puid,
   576  		RemotePUID:           remoteID,
   577  		Namespace:            ns,
   578  		RemoteNamespace:      nsHash,
   579  		RemoteNamespaceType:  gaia.PingProbeRemoteNamespaceTypeHash,
   580  		Protocol:             tpacket.IPProtocolTCP,
   581  		ServiceType:          "L3",
   582  		PolicyID:             policyID,
   583  		PolicyAction:         policyAction,
   584  		ApplicationListening: appListening,
   585  		RemoteEndpointType:   txType,
   586  		SeqNum:               seqNum,
   587  		RemoteController:     controller,
   588  		TargetTCPNetworks:    tn,
   589  		ExcludedNetworks:     en,
   590  		Claims:               claims,
   591  		ClaimsType:           gaia.PingProbeClaimsTypeTransmitted,
   592  		ACLPolicyID:          aclPolicyID,
   593  		ACLPolicyAction:      aclPolicyAction,
   594  		IsServer:             isServer,
   595  		Error:                err,
   596  	}
   597  
   598  	d.collector.CollectPingEvent(report)
   599  }
   600  
   601  // constructTCPPacket constructs a valid tcp packet that can be sent on wire.
   602  func constructTCPPacket(conn PingConn, srcIP, dstIP net.IP, srcPort, dstPort uint16, seqNum, ackNum uint32, flag tcp.Flags, tcpData []byte) ([]byte, error) {
   603  
   604  	// tcp.
   605  	tcpPacket := tcp.Make()
   606  	tcpPacket.SrcPort = srcPort
   607  	tcpPacket.DstPort = dstPort
   608  	tcpPacket.Flags = flag
   609  	tcpPacket.Seq = seqNum
   610  	tcpPacket.Ack = ackNum
   611  	tcpPacket.WindowSize = 0xAAAA
   612  	tcpPacket.Options = []tcp.Option{
   613  		{
   614  			Type: tcp.MSS,
   615  			Len:  4,
   616  			Data: []byte{0x05, 0x8C},
   617  		},
   618  	}
   619  	tcpPacket.DataOff = uint8(6) // 5 (header size) + 1 * (4 byte options)
   620  
   621  	if len(tcpData) != 0 {
   622  		tcpPacket.Options = append(
   623  			tcpPacket.Options,
   624  			tcp.Option{
   625  				Type: 34, // tfo
   626  				Len:  enforcerconstants.TCPAuthenticationOptionBaseLen,
   627  				Data: make([]byte, 2),
   628  			},
   629  		)
   630  		tcpPacket.DataOff += uint8(1) // 6 + 1 * (4 byte options)
   631  	}
   632  
   633  	// payload.
   634  	payload := raw.Make()
   635  	payload.Data = tcpData
   636  
   637  	// construct the wire packet
   638  	buf, err := conn.ConstructWirePacket(srcIP, dstIP, tcpPacket, payload)
   639  	if err != nil {
   640  		return nil, fmt.Errorf("unable to encode packet to wire format: %v", err)
   641  	}
   642  
   643  	return buf, nil
   644  }
   645  
   646  // getSrcIP returns the interface ip that can reach the destination.
   647  func getSrcIP(dstIP net.IP) (net.IP, error) {
   648  
   649  	route, err := routing.RouteTo(dstIP)
   650  	if err != nil || route == nil {
   651  		return nil, fmt.Errorf("no route found for destination %s: %v", dstIP.String(), err)
   652  	}
   653  
   654  	ip, err := route.GetIfaceIPv4Addr()
   655  	if err != nil {
   656  		return nil, fmt.Errorf("unable to get interface ip address: %v", err)
   657  	}
   658  
   659  	return ip, nil
   660  }
   661  
   662  // flowTuple returns the tuple based on the stage in format <sip:dip:spt:dpt> or <dip:sip:dpt:spt>
   663  func flowTuple(stage uint64, srcIP, dstIP net.IP, srcPort, dstPort uint16) string {
   664  
   665  	if stage == tpacket.PacketTypeNetwork {
   666  		return fmt.Sprintf("%s:%s:%s:%s", dstIP.String(), srcIP.String(), strconv.Itoa(int(dstPort)), strconv.Itoa(int(srcPort)))
   667  	}
   668  
   669  	return fmt.Sprintf("%s:%s:%s:%s", srcIP.String(), dstIP.String(), strconv.Itoa(int(srcPort)), strconv.Itoa(int(dstPort)))
   670  }
   671  
   672  // isAppListeningInPort returns true if the port is in use by IPv4:TCP app.
   673  // It immediately closes the listener socket.
   674  // Also returns the actual error for further scrutiny.
   675  func isAppListeningInPort(port uint16) (bool, error) {
   676  
   677  	addr := fmt.Sprintf(":%d", port)
   678  	listener, err := net.Listen("tcp4", addr)
   679  	if listener != nil {
   680  		listener.Close() // nolint:errcheck
   681  	}
   682  
   683  	return isAddressInUse(err), err
   684  }
   685  
   686  // isAddressInUse returns true for and unix.EADDRINUSE or windows.WSAEADDRINUSE errors.
   687  func isAddressInUse(err error) bool {
   688  
   689  	opErr, ok := err.(*net.OpError)
   690  	if !ok {
   691  		return false
   692  	}
   693  
   694  	syscallErr, ok := opErr.Unwrap().(*os.SyscallError)
   695  	if !ok {
   696  		return false
   697  	}
   698  
   699  	errNo, ok := syscallErr.Unwrap().(syscall.Errno)
   700  	if !ok {
   701  		return false
   702  	}
   703  
   704  	return isAddrInUseErrno(errNo)
   705  }