#ifndef _TCP_LRO_H_
#define _TCP_LRO_H_
#include <sys/time.h>
#include <sys/param.h>
#include <sys/mbuf.h>
#include <netinet/in.h>
#ifndef TCP_LRO_ENTRIES
#define TCP_LRO_ENTRIES 8
#endif
#define TSTMP_LRO 0x1000
#define TSTMP_HDWR 0x2000
#define HAS_TSTMP 0x4000
#define TCP_LRO_CPU_DECLARATION_THRESH 50
struct inpcb;
#define LRO_RAW_ADDRESS_MAX \
howmany(12 + 2 * sizeof(struct in6_addr), sizeof(u_long))
union lro_address {
u_long raw[LRO_RAW_ADDRESS_MAX];
struct {
uint8_t lro_type;
#define LRO_TYPE_NONE 0
#define LRO_TYPE_IPV4_TCP 1
#define LRO_TYPE_IPV6_TCP 2
#define LRO_TYPE_IPV4_UDP 3
#define LRO_TYPE_IPV6_UDP 4
uint8_t lro_flags;
#define LRO_FLAG_DECRYPTED 1
uint16_t vlan_id;
uint16_t s_port;
uint16_t d_port;
uint32_t vxlan_vni;
union {
struct in_addr v4;
struct in6_addr v6;
} s_addr;
union {
struct in_addr v4;
struct in6_addr v6;
} d_addr;
};
};
_Static_assert(sizeof(union lro_address) == sizeof(u_long) * LRO_RAW_ADDRESS_MAX,
"The raw field in the lro_address union does not cover the whole structure.");
static inline bool
lro_address_compare(const union lro_address *pa, const union lro_address *pb)
{
if (pa->lro_type == LRO_TYPE_NONE && pb->lro_type == LRO_TYPE_NONE) {
return (true);
} else for (unsigned i = 0; i < LRO_RAW_ADDRESS_MAX; i++) {
if (pa->raw[i] != pb->raw[i])
return (false);
}
return (true);
}
struct lro_parser {
union lro_address data;
union {
uint8_t *l3;
struct ip *ip4;
struct ip6_hdr *ip6;
};
union {
uint8_t *l4;
struct tcphdr *tcp;
struct udphdr *udp;
};
uint16_t total_hdr_len;
};
struct lro_entry {
LIST_ENTRY(lro_entry) next;
LIST_ENTRY(lro_entry) hash_next;
struct mbuf *m_head;
struct mbuf *m_tail;
struct mbuf *m_last_mbuf;
struct lro_parser outer;
struct lro_parser inner;
uint32_t next_seq;
uint32_t ack_seq;
uint32_t tsval;
uint32_t tsecr;
uint16_t compressed;
uint16_t uncompressed;
uint16_t window;
uint16_t flags : 12,
timestamp : 1,
needs_merge : 1,
reserved : 2;
struct bintime alloc_time;
};
LIST_HEAD(lro_head, lro_entry);
struct lro_mbuf_sort {
uint64_t seq;
struct mbuf *mb;
};
struct lro_ctrl {
struct ifnet *ifp;
struct lro_mbuf_sort *lro_mbuf_data;
struct bintime lro_last_queue_time;
uint64_t lro_queued;
uint64_t lro_flushed;
uint64_t lro_bad_csum;
unsigned lro_cnt;
unsigned lro_mbuf_count;
unsigned lro_mbuf_max;
unsigned short lro_ackcnt_lim;
unsigned short lro_cpu;
unsigned lro_length_lim;
u_long lro_hashsz;
uint32_t lro_last_cpu;
uint32_t lro_cnt_of_same_cpu;
struct lro_head *lro_hash;
struct lro_head lro_active;
struct lro_head lro_free;
uint8_t lro_cpu_is_set;
};
struct tcp_ackent {
uint64_t timestamp;
uint32_t seq;
uint32_t ack;
uint32_t ts_value;
uint32_t ts_echo;
uint16_t win;
uint16_t flags;
uint8_t codepoint;
uint8_t ack_val_set;
uint8_t pad[2];
};
#define M_ACKCMP M_PROTO4
#define M_LRO_EHDRSTRP M_PROTO6
#define TCP_LRO_LENGTH_MAX (65535 - 255)
#define TCP_LRO_ACKCNT_MAX 65535
#define TCP_LRO_TS_OPTION ntohl((TCPOPT_NOP << 24) | (TCPOPT_NOP << 16) |\
(TCPOPT_TIMESTAMP << 8) | TCPOLEN_TIMESTAMP)
static inline struct tcphdr *
tcp_lro_get_th(struct mbuf *m)
{
return ((struct tcphdr *)((char *)m->m_data +
m->m_pkthdr.lro_tcp_h_off));
}
extern long tcplro_stacks_wanting_mbufq;
int tcp_lro_init(struct lro_ctrl *);
int tcp_lro_init_args(struct lro_ctrl *, struct ifnet *, unsigned, unsigned);
void tcp_lro_free(struct lro_ctrl *);
void tcp_lro_flush_inactive(struct lro_ctrl *, const struct timeval *);
void tcp_lro_flush_all(struct lro_ctrl *);
extern int (*tcp_lro_flush_tcphpts)(struct lro_ctrl *, struct lro_entry *);
int tcp_lro_rx(struct lro_ctrl *, struct mbuf *, uint32_t);
void tcp_lro_queue_mbuf(struct lro_ctrl *, struct mbuf *);
void tcp_lro_reg_mbufq(void);
void tcp_lro_dereg_mbufq(void);
#define TCP_LRO_NO_ENTRIES -2
#define TCP_LRO_CANNOT -1
#define TCP_LRO_NOT_SUPPORTED 1
#endif