aboutgitcodebugslistschat
path: root/flow.h
blob: 29ef9f12ab82a3fabceaab5135d321518b1edff3 (plain) (tree)
1
2
3
4
5
6
7
8
9








                                                     

                                                        
   














                                                                             

                                                                             


                                                                        


                                                                               

                                                                               

























                                                                               






                                                                             


                                                                               














                                                                            

                       













                                                                         








                                                                  



                                                                               


                       


                                                        




                                                                         



                                                                




                                                             

                                                      
                                             
                                   
                                                   

                    






                                                             
                             


                                                            
                                   

  





                                                                    





                                                        
                                






                                                          










                                                                     

           
                     
                                                                         
 













                                                                          
                   
/* SPDX-License-Identifier: GPL-2.0-or-later
 * Copyright Red Hat
 * Author: David Gibson <david@gibson.dropbear.id.au>
 *
 * Tracking for logical "flows" of packets.
 */
#ifndef FLOW_H
#define FLOW_H

#define FLOW_TIMER_INTERVAL		1000	/* ms */

/**
 * enum flow_state - States of a flow table entry
 *
 * An individual flow table entry moves through these states, usually in this
 * order.
 *  General rules:
 *    - Code outside flow.c should never write common fields of union flow.
 *    - The state field may always be read.
 *
 *    FREE - Part of the general pool of free flow table entries
 *        Operations:
 *            - flow_alloc() finds an entry and moves it to NEW
 *
 *    NEW - Freshly allocated, uninitialised entry
 *        Operations:
 *            - flow_alloc_cancel() returns the entry to FREE
 *            - flow_initiate() sets the entry's INISIDE details and moves to
 *              INI
 *            - FLOW_SET_TYPE() sets the entry's type and moves to TYPED
 *        Caveats:
 *            - No fields other than state may be accessed
 *            - At most one entry may be NEW, INI, TGT or TYPED at a time, so
 *              it's unsafe to use flow_alloc() again until this entry moves to
 *              ACTIVE or FREE
 *            - You may not return to the main epoll loop while any flow is NEW
 *
 *    INI - An entry with INISIDE common information completed
 *        Operations:
 *            - Common fields related to INISIDE may be read
 *            - flow_alloc_cancel() returns the entry to FREE
 *            - flow_target() sets the entry's TGTSIDE details and moves to TGT
 *        Caveats:
 *            - Other common fields may not be read
 *            - Type specific fields may not be read or written
 *            - At most one entry may be NEW, INI, TGT or TYPED at a time, so
 *              it's unsafe to use flow_alloc() again until this entry moves to
 *              ACTIVE or FREE
 *            - You may not return to the main epoll loop while any flow is INI
 *
 *    TGT - An entry with only INISIDE and TGTSIDE common information completed
 *        Operations:
 *            - Common fields related to INISIDE & TGTSIDE may be read
 *            - flow_alloc_cancel() returns the entry to FREE
 *            - FLOW_SET_TYPE() sets the entry's type and moves to TYPED
 *        Caveats:
 *            - Other common fields may not be read
 *            - Type specific fields may not be read or written
 *            - At most one entry may be NEW, INI, TGT or TYPED at a time, so
 *              it's unsafe to use flow_alloc() again until this entry moves to
 *              ACTIVE or FREE
 *            - You may not return to the main epoll loop while any flow is TGT
 *
 *    TYPED - Generic info initialised, type specific initialisation underway
 *        Operations:
 *            - All common fields may be read
 *            - Type specific fields may be read and written
 *            - flow_alloc_cancel() returns the entry to FREE
 *            - FLOW_ACTIVATE() moves the entry to ACTIVE
 *        Caveats:
 *            - At most one entry may be NEW, INI, TGT or TYPED at a time, so
 *              it's unsafe to use flow_alloc() again until this entry moves to
 *              ACTIVE or FREE
 *            - You may not return to the main epoll loop while any flow is
 *              TYPED
 *
 *    ACTIVE - An active, fully-initialised flow entry
 *        Operations:
 *            - All common fields may be read
 *            - Type specific fields may be read and written
 *            - Flow returns to FREE when it expires, signalled by returning
 *              'true' from flow type specific deferred or timer handler
 *        Caveats:
 *            - flow_alloc_cancel() may not be called on it
 */
enum flow_state {
	FLOW_STATE_FREE,
	FLOW_STATE_NEW,
	FLOW_STATE_INI,
	FLOW_STATE_TGT,
	FLOW_STATE_TYPED,
	FLOW_STATE_ACTIVE,

	FLOW_NUM_STATES,
};
#define FLOW_STATE_BITS		8
static_assert(FLOW_NUM_STATES <= (1 << FLOW_STATE_BITS),
	      "Too many flow states for FLOW_STATE_BITS");

extern const char *flow_state_str[];
#define FLOW_STATE(f)							\
        ((f)->state < FLOW_NUM_STATES ? flow_state_str[(f)->state] : "?")

/**
 * enum flow_type - Different types of packet flows we track
 */
enum flow_type {
	/* Represents an invalid or unused flow */
	FLOW_TYPE_NONE = 0,
	/* A TCP connection between a socket and tap interface */
	FLOW_TCP,
	/* A TCP connection between a host socket and ns socket */
	FLOW_TCP_SPLICE,
	/* ICMP echo requests from guest to host and matching replies back */
	FLOW_PING4,
	/* ICMPv6 echo requests from guest to host and matching replies back */
	FLOW_PING6,

	FLOW_NUM_TYPES,
};
#define FLOW_TYPE_BITS		8
static_assert(FLOW_NUM_TYPES <= (1 << FLOW_TYPE_BITS),
	      "Too many flow types for FLOW_TYPE_BITS");

extern const char *flow_type_str[];
#define FLOW_TYPE(f)							\
        ((f)->type < FLOW_NUM_TYPES ? flow_type_str[(f)->type] : "?")

extern const uint8_t flow_proto[];
#define FLOW_PROTO(f)				\
	((f)->type < FLOW_NUM_TYPES ? flow_proto[(f)->type] : 0)

#define SIDES			2

#define INISIDE			0	/* Initiating side */
#define TGTSIDE			1	/* Target side */

/**
 * struct flow_common - Common fields for packet flows
 * @state:	State of the flow table entry
 * @type:	Type of packet flow
 * @pif[]:	Interface for each side of the flow
 */
struct flow_common {
#ifdef __GNUC__
	enum flow_state	state:FLOW_STATE_BITS;
	enum flow_type	type:FLOW_TYPE_BITS;
#else
	uint8_t		state;
	static_assert(sizeof(uint8_t) * 8 >= FLOW_STATE_BITS,
		      "Not enough bits for state field");
	uint8_t		type;
	static_assert(sizeof(uint8_t) * 8 >= FLOW_TYPE_BITS,
		      "Not enough bits for type field");
#endif
	uint8_t		pif[SIDES];
};

#define FLOW_INDEX_BITS		17	/* 128k - 1 */
#define FLOW_MAX		MAX_FROM_BITS(FLOW_INDEX_BITS)

#define FLOW_TABLE_PRESSURE		30	/* % of FLOW_MAX */
#define FLOW_FILE_PRESSURE		30	/* % of c->nofile */

/**
 * struct flow_sidx - ID for one side of a specific flow
 * @side:	Side referenced (0 or 1)
 * @flow:	Index of flow referenced
 */
typedef struct flow_sidx {
	unsigned	side :1;
	unsigned	flow :FLOW_INDEX_BITS;
} flow_sidx_t;
static_assert(sizeof(flow_sidx_t) <= sizeof(uint32_t),
	      "flow_sidx_t must fit within 32 bits");

#define FLOW_SIDX_NONE ((flow_sidx_t){ .flow = FLOW_MAX })

/**
 * flow_sidx_eq() - Test if two sidx values are equal
 * @a, @b:	sidx values
 *
 * Return: true iff @a and @b refer to the same side of the same flow
 */
static inline bool flow_sidx_eq(flow_sidx_t a, flow_sidx_t b)
{
	return (a.flow == b.flow) && (a.side == b.side);
}

union flow;

void flow_init(void);
void flow_defer_handler(const struct ctx *c, const struct timespec *now);

void flow_log_(const struct flow_common *f, int pri, const char *fmt, ...)
	__attribute__((format(printf, 3, 4)));

#define flow_log(f_, pri, ...)	flow_log_(&(f_)->f, (pri), __VA_ARGS__)

#define flow_dbg(f, ...)	flow_log((f), LOG_DEBUG, __VA_ARGS__)
#define flow_err(f, ...)	flow_log((f), LOG_ERR, __VA_ARGS__)

#define flow_trace(f, ...)						\
	do {								\
		if (log_trace)						\
			flow_dbg((f), __VA_ARGS__);			\
	} while (0)

#endif /* FLOW_H */