/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright Red Hat * Author: David Gibson * * Tracking for logical "flows" of packets. */ #ifndef FLOW_H #define FLOW_H #define FLOW_TIMER_INTERVAL 1000 /* ms */ /** * 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, }; 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) /** * struct flowside - Common information for one side of a flow * @eaddr: Endpoint address (remote address from passt's PoV) * @faddr: Forwarding address (local address from passt's PoV) * @eport: Endpoint port * @fport: Forwarding port * @pif: pif ID on which this side of the flow exists */ struct flowside { union inany_addr faddr; union inany_addr eaddr; in_port_t fport; in_port_t eport; uint8_t pif; }; static_assert(_Alignof(struct flowside) == _Alignof(uint32_t), "Unexpected alignment for struct flowside"); /** flowside_from_af - Initialize flowside from addresses * @fside: flowside to initialize * @pif: pif if of this flowside * @af: Address family (AF_INET or AF_INET6) * @faddr: Forwarding address (pointer to in_addr or in6_addr) * @fport: Forwarding port * @eaddr: Endpoint address (pointer to in_addr or in6_addr) * @eport: Endpoint port */ static inline void flowside_from_af(struct flowside *fside, uint8_t pif, int af, const void *faddr, in_port_t fport, const void *eaddr, in_port_t eport) { fside->pif = pif; inany_from_af(&fside->faddr, af, faddr); inany_from_af(&fside->eaddr, af, eaddr); fside->fport = fport; fside->eport = eport; } int flowside_from_sock(struct flowside *fside, uint8_t pif, int s, const void *fsa, const void *esa); /** flowside_complete - Check if flowside is fully initialized * @fside: flowside to check */ static inline bool flowside_complete(const struct flowside *fside) { return fside->pif != PIF_NONE && !IN6_IS_ADDR_UNSPECIFIED(&fside->faddr) && !IN6_IS_ADDR_UNSPECIFIED(&fside->eaddr) && fside->fport != 0 && fside->eport != 0; } #define SIDES 2 /** * flowside_eq() - Check if two flowsides are equal * @left, @right: Flowsides to compare * * Return: true if equal, false otherwise */ static inline bool flowside_eq(const struct flowside *left, const struct flowside *right) { return left->pif == right->pif && inany_equals(&left->eaddr, &right->eaddr) && left->eport == right->eport && inany_equals(&left->faddr, &right->faddr) && left->fport == right->fport; } /** * struct flow_common - Common fields for packet flows * @side[]: Information for each side of the flow * @type: Type of packet flow */ struct flow_common { struct flowside side[SIDES]; uint8_t type; }; #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 */ /** flow_complete - Check if common parts of flow are fully initialized * @flow: flow to check */ static inline bool flow_complete(const struct flow_common *f) { return f->type != FLOW_TYPE_NONE && f->type < FLOW_NUM_TYPES && flowside_complete(&f->side[0]) && flowside_complete(&f->side[1]); } void flow_new_dbg(const struct flow_common *f, unsigned side); #define FLOW_NEW_DBG(flow, side) (flow_new_dbg(&(flow)->f, (side))) void flow_fwd_dbg(const struct flow_common *f, unsigned side); #define FLOW_FWD_DBG(flow, side) (flow_fwd_dbg(&(flow)->f, (side))) /** * 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); } uint64_t flow_hash_insert(const struct ctx *c, flow_sidx_t sidx); void flow_hash_remove(const struct ctx *c, flow_sidx_t sidx); flow_sidx_t flow_hash_lookup(const struct ctx *c, uint8_t proto, uint8_t pif, int af, const void *eaddr, const void *faddr, in_port_t eport, in_port_t fport); 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) void flow_init(void); #endif /* FLOW_H */