2018-05-18 11:25:37 +00:00
|
|
|
/* SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
* Copyright(c) 2016-2018 Intel Corporation.
|
|
|
|
* Copyright(c) 2017-2018 Linaro Limited.
|
2017-07-04 10:24:00 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef _L3FWD_COMMON_H_
|
|
|
|
#define _L3FWD_COMMON_H_
|
|
|
|
|
|
|
|
#ifdef DO_RFC_1812_CHECKS
|
|
|
|
|
|
|
|
#define IPV4_MIN_VER_IHL 0x45
|
|
|
|
#define IPV4_MAX_VER_IHL 0x4f
|
|
|
|
#define IPV4_MAX_VER_IHL_DIFF (IPV4_MAX_VER_IHL - IPV4_MIN_VER_IHL)
|
|
|
|
|
|
|
|
/* Minimum value of IPV4 total length (20B) in network byte order. */
|
2019-05-21 16:13:10 +00:00
|
|
|
#define IPV4_MIN_LEN_BE (sizeof(struct rte_ipv4_hdr) << 8)
|
2017-07-04 10:24:00 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* From http://www.rfc-editor.org/rfc/rfc1812.txt section 5.2.2:
|
|
|
|
* - The IP version number must be 4.
|
|
|
|
* - The IP header length field must be large enough to hold the
|
|
|
|
* minimum length legal IP datagram (20 bytes = 5 words).
|
|
|
|
* - The IP total length field must be large enough to hold the IP
|
|
|
|
* datagram header, whose length is specified in the IP header length
|
|
|
|
* field.
|
|
|
|
* If we encounter invalid IPV4 packet, then set destination port for it
|
|
|
|
* to BAD_PORT value.
|
|
|
|
*/
|
|
|
|
static __rte_always_inline void
|
2019-05-21 16:13:10 +00:00
|
|
|
rfc1812_process(struct rte_ipv4_hdr *ipv4_hdr, uint16_t *dp, uint32_t ptype)
|
2017-07-04 10:24:00 +00:00
|
|
|
{
|
|
|
|
uint8_t ihl;
|
|
|
|
|
|
|
|
if (RTE_ETH_IS_IPV4_HDR(ptype)) {
|
|
|
|
ihl = ipv4_hdr->version_ihl - IPV4_MIN_VER_IHL;
|
|
|
|
|
|
|
|
ipv4_hdr->time_to_live--;
|
|
|
|
ipv4_hdr->hdr_checksum++;
|
|
|
|
|
|
|
|
if (ihl > IPV4_MAX_VER_IHL_DIFF ||
|
|
|
|
((uint8_t)ipv4_hdr->total_length == 0 &&
|
|
|
|
ipv4_hdr->total_length < IPV4_MIN_LEN_BE))
|
|
|
|
dp[0] = BAD_PORT;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#else
|
|
|
|
#define rfc1812_process(mb, dp, ptype) do { } while (0)
|
|
|
|
#endif /* DO_RFC_1812_CHECKS */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We group consecutive packets with the same destionation port into one burst.
|
|
|
|
* To avoid extra latency this is done together with some other packet
|
|
|
|
* processing, but after we made a final decision about packet's destination.
|
|
|
|
* To do this we maintain:
|
|
|
|
* pnum - array of number of consecutive packets with the same dest port for
|
|
|
|
* each packet in the input burst.
|
|
|
|
* lp - pointer to the last updated element in the pnum.
|
|
|
|
* dlp - dest port value lp corresponds to.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define GRPSZ (1 << FWDSTEP)
|
|
|
|
#define GRPMSK (GRPSZ - 1)
|
|
|
|
|
|
|
|
#define GROUP_PORT_STEP(dlp, dcp, lp, pn, idx) do { \
|
|
|
|
if (likely((dlp) == (dcp)[(idx)])) { \
|
|
|
|
(lp)[0]++; \
|
|
|
|
} else { \
|
|
|
|
(dlp) = (dcp)[idx]; \
|
|
|
|
(lp) = (pn) + (idx); \
|
|
|
|
(lp)[0] = 1; \
|
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
static const struct {
|
|
|
|
uint64_t pnum; /* prebuild 4 values for pnum[]. */
|
|
|
|
int32_t idx; /* index for new last updated elemnet. */
|
|
|
|
uint16_t lpv; /* add value to the last updated element. */
|
|
|
|
} gptbl[GRPSZ] = {
|
|
|
|
{
|
|
|
|
/* 0: a != b, b != c, c != d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000100010001),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 1: a == b, b != c, c != d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000100010002),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 2: a != b, b == c, c != d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000100020001),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 3: a == b, b == c, c != d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000100020003),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 2,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 4: a != b, b != c, c == d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000200010001),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 5: a == b, b != c, c == d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000200010002),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 6: a != b, b == c, c == d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000200030001),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 7: a == b, b == c, c == d, d != e */
|
|
|
|
.pnum = UINT64_C(0x0001000200030004),
|
|
|
|
.idx = 4,
|
|
|
|
.lpv = 3,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 8: a != b, b != c, c != d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000100010001),
|
|
|
|
.idx = 3,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 9: a == b, b != c, c != d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000100010002),
|
|
|
|
.idx = 3,
|
|
|
|
.lpv = 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 0xa: a != b, b == c, c != d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000100020001),
|
|
|
|
.idx = 3,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 0xb: a == b, b == c, c != d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000100020003),
|
|
|
|
.idx = 3,
|
|
|
|
.lpv = 2,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 0xc: a != b, b != c, c == d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000300010001),
|
|
|
|
.idx = 2,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 0xd: a == b, b != c, c == d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000300010002),
|
|
|
|
.idx = 2,
|
|
|
|
.lpv = 1,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 0xe: a != b, b == c, c == d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000300040001),
|
|
|
|
.idx = 1,
|
|
|
|
.lpv = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
/* 0xf: a == b, b == c, c == d, d == e */
|
|
|
|
.pnum = UINT64_C(0x0002000300040005),
|
|
|
|
.idx = 0,
|
|
|
|
.lpv = 4,
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
static __rte_always_inline void
|
2017-09-29 07:17:24 +00:00
|
|
|
send_packetsx4(struct lcore_conf *qconf, uint16_t port, struct rte_mbuf *m[],
|
2017-07-04 10:24:00 +00:00
|
|
|
uint32_t num)
|
|
|
|
{
|
|
|
|
uint32_t len, j, n;
|
|
|
|
|
|
|
|
len = qconf->tx_mbufs[port].len;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If TX buffer for that queue is empty, and we have enough packets,
|
|
|
|
* then send them straightway.
|
|
|
|
*/
|
|
|
|
if (num >= MAX_TX_BURST && len == 0) {
|
|
|
|
n = rte_eth_tx_burst(port, qconf->tx_queue_id[port], m, num);
|
|
|
|
if (unlikely(n < num)) {
|
|
|
|
do {
|
|
|
|
rte_pktmbuf_free(m[n]);
|
|
|
|
} while (++n < num);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Put packets into TX buffer for that queue.
|
|
|
|
*/
|
|
|
|
|
|
|
|
n = len + num;
|
|
|
|
n = (n > MAX_PKT_BURST) ? MAX_PKT_BURST - len : num;
|
|
|
|
|
|
|
|
j = 0;
|
|
|
|
switch (n % FWDSTEP) {
|
|
|
|
while (j < n) {
|
|
|
|
case 0:
|
|
|
|
qconf->tx_mbufs[port].m_table[len + j] = m[j];
|
|
|
|
j++;
|
|
|
|
/* fallthrough */
|
|
|
|
case 3:
|
|
|
|
qconf->tx_mbufs[port].m_table[len + j] = m[j];
|
|
|
|
j++;
|
|
|
|
/* fallthrough */
|
|
|
|
case 2:
|
|
|
|
qconf->tx_mbufs[port].m_table[len + j] = m[j];
|
|
|
|
j++;
|
|
|
|
/* fallthrough */
|
|
|
|
case 1:
|
|
|
|
qconf->tx_mbufs[port].m_table[len + j] = m[j];
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
len += n;
|
|
|
|
|
|
|
|
/* enough pkts to be sent */
|
|
|
|
if (unlikely(len == MAX_PKT_BURST)) {
|
|
|
|
|
|
|
|
send_burst(qconf, MAX_PKT_BURST, port);
|
|
|
|
|
|
|
|
/* copy rest of the packets into the TX buffer. */
|
|
|
|
len = num - n;
|
|
|
|
j = 0;
|
|
|
|
switch (len % FWDSTEP) {
|
|
|
|
while (j < len) {
|
|
|
|
case 0:
|
|
|
|
qconf->tx_mbufs[port].m_table[j] = m[n + j];
|
|
|
|
j++;
|
|
|
|
/* fallthrough */
|
|
|
|
case 3:
|
|
|
|
qconf->tx_mbufs[port].m_table[j] = m[n + j];
|
|
|
|
j++;
|
|
|
|
/* fallthrough */
|
|
|
|
case 2:
|
|
|
|
qconf->tx_mbufs[port].m_table[j] = m[n + j];
|
|
|
|
j++;
|
|
|
|
/* fallthrough */
|
|
|
|
case 1:
|
|
|
|
qconf->tx_mbufs[port].m_table[j] = m[n + j];
|
|
|
|
j++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
qconf->tx_mbufs[port].len = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* _L3FWD_COMMON_H_ */
|