1aa97dd1cSKonstantin Ananyev/*
27e18fa1bSKonstantin Ananyev * Copyright (c) 2016-2017  Intel Corporation.
3aa97dd1cSKonstantin Ananyev * Licensed under the Apache License, Version 2.0 (the "License");
4aa97dd1cSKonstantin Ananyev * you may not use this file except in compliance with the License.
5aa97dd1cSKonstantin Ananyev * You may obtain a copy of the License at:
6aa97dd1cSKonstantin Ananyev *
7aa97dd1cSKonstantin Ananyev *     http://www.apache.org/licenses/LICENSE-2.0
8aa97dd1cSKonstantin Ananyev *
9aa97dd1cSKonstantin Ananyev * Unless required by applicable law or agreed to in writing, software
10aa97dd1cSKonstantin Ananyev * distributed under the License is distributed on an "AS IS" BASIS,
11aa97dd1cSKonstantin Ananyev * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12aa97dd1cSKonstantin Ananyev * See the License for the specific language governing permissions and
13aa97dd1cSKonstantin Ananyev * limitations under the License.
14aa97dd1cSKonstantin Ananyev */
15aa97dd1cSKonstantin Ananyev
16aa97dd1cSKonstantin Ananyev#include <string.h>
17aa97dd1cSKonstantin Ananyev#include <rte_malloc.h>
18aa97dd1cSKonstantin Ananyev#include <rte_errno.h>
197e18fa1bSKonstantin Ananyev#include <rte_cycles.h>
20aa97dd1cSKonstantin Ananyev#include <rte_ethdev.h>
21aa97dd1cSKonstantin Ananyev#include <rte_ip.h>
22aa97dd1cSKonstantin Ananyev
23aa97dd1cSKonstantin Ananyev#include "stream.h"
24aa97dd1cSKonstantin Ananyev#include "misc.h"
259fa82a63SReshma Pattan#include <halfsiphash.h>
26aa97dd1cSKonstantin Ananyev
27aa97dd1cSKonstantin Ananyev#define	LPORT_START	0x8000
28aa97dd1cSKonstantin Ananyev#define	LPORT_END	MAX_PORT_NUM
29aa97dd1cSKonstantin Ananyev
30aa97dd1cSKonstantin Ananyev#define	LPORT_START_BLK	PORT_BLK(LPORT_START)
31aa97dd1cSKonstantin Ananyev#define	LPORT_END_BLK	PORT_BLK(LPORT_END)
32aa97dd1cSKonstantin Ananyev
33aa97dd1cSKonstantin Ananyevconst struct in6_addr tle_ipv6_any = IN6ADDR_ANY_INIT;
34aa97dd1cSKonstantin Ananyevconst struct in6_addr tle_ipv6_none = {
35aa97dd1cSKonstantin Ananyev	{
36aa97dd1cSKonstantin Ananyev		.__u6_addr32 = {
37aa97dd1cSKonstantin Ananyev			UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX
38aa97dd1cSKonstantin Ananyev		},
39aa97dd1cSKonstantin Ananyev	},
40aa97dd1cSKonstantin Ananyev};
41aa97dd1cSKonstantin Ananyev
42aa97dd1cSKonstantin Ananyevstruct stream_ops tle_stream_ops[TLE_PROTO_NUM] = {};
43aa97dd1cSKonstantin Ananyev
44aa97dd1cSKonstantin Ananyevstatic int
45aa97dd1cSKonstantin Ananyevcheck_dev_prm(const struct tle_dev_param *dev_prm)
46aa97dd1cSKonstantin Ananyev{
47aa97dd1cSKonstantin Ananyev	/* no valid IPv4/IPv6 addresses provided. */
48aa97dd1cSKonstantin Ananyev	if (dev_prm->local_addr4.s_addr == INADDR_ANY &&
49aa97dd1cSKonstantin Ananyev			memcmp(&dev_prm->local_addr6, &tle_ipv6_any,
50aa97dd1cSKonstantin Ananyev			sizeof(tle_ipv6_any)) == 0)
51aa97dd1cSKonstantin Ananyev		return -EINVAL;
52aa97dd1cSKonstantin Ananyev
53aa97dd1cSKonstantin Ananyev	if (dev_prm->bl4.nb_port > UINT16_MAX ||
54aa97dd1cSKonstantin Ananyev			(dev_prm->bl4.nb_port != 0 &&
55aa97dd1cSKonstantin Ananyev			dev_prm->bl4.port == NULL))
56aa97dd1cSKonstantin Ananyev		return -EINVAL;
57aa97dd1cSKonstantin Ananyev
58aa97dd1cSKonstantin Ananyev	if (dev_prm->bl6.nb_port > UINT16_MAX ||
59aa97dd1cSKonstantin Ananyev			(dev_prm->bl6.nb_port != 0 &&
60aa97dd1cSKonstantin Ananyev			dev_prm->bl6.port == NULL))
61aa97dd1cSKonstantin Ananyev		return -EINVAL;
62aa97dd1cSKonstantin Ananyev
63aa97dd1cSKonstantin Ananyev	return 0;
64aa97dd1cSKonstantin Ananyev}
65aa97dd1cSKonstantin Ananyev
66aa97dd1cSKonstantin Ananyevstatic int
67aa97dd1cSKonstantin Ananyevcheck_ctx_prm(const struct tle_ctx_param *prm)
68aa97dd1cSKonstantin Ananyev{
69aa97dd1cSKonstantin Ananyev	if (prm->proto >= TLE_PROTO_NUM)
70aa97dd1cSKonstantin Ananyev		return -EINVAL;
719fa82a63SReshma Pattan	if (prm->hash_alg >= TLE_HASH_NUM)
729fa82a63SReshma Pattan		return -EINVAL;
73aa97dd1cSKonstantin Ananyev	return 0;
74aa97dd1cSKonstantin Ananyev}
75aa97dd1cSKonstantin Ananyev
76aa97dd1cSKonstantin Ananyevstruct tle_ctx *
77aa97dd1cSKonstantin Ananyevtle_ctx_create(const struct tle_ctx_param *ctx_prm)
78aa97dd1cSKonstantin Ananyev{
79aa97dd1cSKonstantin Ananyev	struct tle_ctx *ctx;
80aa97dd1cSKonstantin Ananyev	size_t sz;
817e18fa1bSKonstantin Ananyev	uint64_t ms;
82aa97dd1cSKonstantin Ananyev	uint32_t i;
83aa97dd1cSKonstantin Ananyev	int32_t rc;
84aa97dd1cSKonstantin Ananyev
85aa97dd1cSKonstantin Ananyev	if (ctx_prm == NULL || check_ctx_prm(ctx_prm) != 0) {
86aa97dd1cSKonstantin Ananyev		rte_errno = EINVAL;
87aa97dd1cSKonstantin Ananyev		return NULL;
88aa97dd1cSKonstantin Ananyev	}
89aa97dd1cSKonstantin Ananyev
90aa97dd1cSKonstantin Ananyev	sz = sizeof(*ctx);
91aa97dd1cSKonstantin Ananyev	ctx = rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
92aa97dd1cSKonstantin Ananyev		ctx_prm->socket_id);
93aa97dd1cSKonstantin Ananyev	if (ctx == NULL) {
94aa97dd1cSKonstantin Ananyev		UDP_LOG(ERR, "allocation of %zu bytes for new ctx "
95aa97dd1cSKonstantin Ananyev			"on socket %d failed\n",
96aa97dd1cSKonstantin Ananyev			sz, ctx_prm->socket_id);
97aa97dd1cSKonstantin Ananyev		return NULL;
98aa97dd1cSKonstantin Ananyev	}
99aa97dd1cSKonstantin Ananyev
1007e18fa1bSKonstantin Ananyev	/* caclulate closest shift to convert from cycles to ms (approximate) */
1017e18fa1bSKonstantin Ananyev	ms = (rte_get_tsc_hz() + MS_PER_S - 1) / MS_PER_S;
1027e18fa1bSKonstantin Ananyev	ctx->cycles_ms_shift = sizeof(ms) * CHAR_BIT - __builtin_clzll(ms) - 1;
1037e18fa1bSKonstantin Ananyev
104aa97dd1cSKonstantin Ananyev	ctx->prm = *ctx_prm;
105aa97dd1cSKonstantin Ananyev
106aa97dd1cSKonstantin Ananyev	rc = tle_stream_ops[ctx_prm->proto].init_streams(ctx);
107aa97dd1cSKonstantin Ananyev	if (rc != 0) {
108aa97dd1cSKonstantin Ananyev		UDP_LOG(ERR, "init_streams(ctx=%p, proto=%u) failed "
109aa97dd1cSKonstantin Ananyev			"with error code: %d;\n",
110aa97dd1cSKonstantin Ananyev			ctx, ctx_prm->proto, rc);
111aa97dd1cSKonstantin Ananyev		tle_ctx_destroy(ctx);
112aa97dd1cSKonstantin Ananyev		rte_errno = -rc;
113aa97dd1cSKonstantin Ananyev		return NULL;
114aa97dd1cSKonstantin Ananyev	}
115aa97dd1cSKonstantin Ananyev
116aa97dd1cSKonstantin Ananyev	for (i = 0; i != RTE_DIM(ctx->use); i++)
117aa97dd1cSKonstantin Ananyev		tle_pbm_init(ctx->use + i, LPORT_START_BLK);
118aa97dd1cSKonstantin Ananyev
1199fa82a63SReshma Pattan	/* Initialization of siphash state is done here to speed up the
1209fa82a63SReshma Pattan	 * fastpath processing.
1219fa82a63SReshma Pattan	 */
1229fa82a63SReshma Pattan	if (ctx->prm.hash_alg == TLE_SIPHASH)
1239fa82a63SReshma Pattan		siphash_initialization(&ctx->prm.secret_key,
1249fa82a63SReshma Pattan					&ctx->prm.secret_key);
125aa97dd1cSKonstantin Ananyev	return ctx;
126aa97dd1cSKonstantin Ananyev}
127aa97dd1cSKonstantin Ananyev
128aa97dd1cSKonstantin Ananyevvoid
129aa97dd1cSKonstantin Ananyevtle_ctx_destroy(struct tle_ctx *ctx)
130aa97dd1cSKonstantin Ananyev{
131aa97dd1cSKonstantin Ananyev	uint32_t i;
132aa97dd1cSKonstantin Ananyev
133aa97dd1cSKonstantin Ananyev	if (ctx == NULL) {
134aa97dd1cSKonstantin Ananyev		rte_errno = EINVAL;
135aa97dd1cSKonstantin Ananyev		return;
136aa97dd1cSKonstantin Ananyev	}
137aa97dd1cSKonstantin Ananyev
138aa97dd1cSKonstantin Ananyev	for (i = 0; i != RTE_DIM(ctx->dev); i++)
139aa97dd1cSKonstantin Ananyev		tle_del_dev(ctx->dev + i);
140aa97dd1cSKonstantin Ananyev
141aa97dd1cSKonstantin Ananyev	tle_stream_ops[ctx->prm.proto].fini_streams(ctx);
142aa97dd1cSKonstantin Ananyev	rte_free(ctx);
143aa97dd1cSKonstantin Ananyev}
144aa97dd1cSKonstantin Ananyev
145aa97dd1cSKonstantin Ananyevvoid
146aa97dd1cSKonstantin Ananyevtle_ctx_invalidate(struct tle_ctx *ctx)
147aa97dd1cSKonstantin Ananyev{
148aa97dd1cSKonstantin Ananyev	RTE_SET_USED(ctx);
149aa97dd1cSKonstantin Ananyev}
150aa97dd1cSKonstantin Ananyev
151aa97dd1cSKonstantin Ananyevstatic void
152aa97dd1cSKonstantin Ananyevfill_pbm(struct tle_pbm *pbm, const struct tle_bl_port *blp)
153aa97dd1cSKonstantin Ananyev{
154aa97dd1cSKonstantin Ananyev	uint32_t i;
155aa97dd1cSKonstantin Ananyev
156aa97dd1cSKonstantin Ananyev	for (i = 0; i != blp->nb_port; i++)
157aa97dd1cSKonstantin Ananyev		tle_pbm_set(pbm, blp->port[i]);
158aa97dd1cSKonstantin Ananyev}
159aa97dd1cSKonstantin Ananyev
160aa97dd1cSKonstantin Ananyevstatic int
161aa97dd1cSKonstantin Ananyevinit_dev_proto(struct tle_dev *dev, uint32_t idx, int32_t socket_id,
162aa97dd1cSKonstantin Ananyev	const struct tle_bl_port *blp)
163aa97dd1cSKonstantin Ananyev{
164aa97dd1cSKonstantin Ananyev	size_t sz;
165aa97dd1cSKonstantin Ananyev
166aa97dd1cSKonstantin Ananyev	sz = sizeof(*dev->dp[idx]);
167aa97dd1cSKonstantin Ananyev	dev->dp[idx] = rte_zmalloc_socket(NULL, sz, RTE_CACHE_LINE_SIZE,
168aa97dd1cSKonstantin Ananyev		socket_id);
169aa97dd1cSKonstantin Ananyev
170aa97dd1cSKonstantin Ananyev	if (dev->dp[idx] == NULL) {
171aa97dd1cSKonstantin Ananyev		UDP_LOG(ERR, "allocation of %zu bytes on "
172aa97dd1cSKonstantin Ananyev			"socket %d for %u-th device failed\n",
173aa97dd1cSKonstantin Ananyev			sz, socket_id, idx);
174aa97dd1cSKonstantin Ananyev		return ENOMEM;
175aa97dd1cSKonstantin Ananyev	}
176aa97dd1cSKonstantin Ananyev
177aa97dd1cSKonstantin Ananyev	tle_pbm_init(&dev->dp[idx]->use, LPORT_START_BLK);
178aa97dd1cSKonstantin Ananyev	fill_pbm(&dev->dp[idx]->use, blp);
179aa97dd1cSKonstantin Ananyev	return 0;
180aa97dd1cSKonstantin Ananyev}
181aa97dd1cSKonstantin Ananyev
182aa97dd1cSKonstantin Ananyevstatic struct tle_dev *
183aa97dd1cSKonstantin Ananyevfind_free_dev(struct tle_ctx *ctx)
184aa97dd1cSKonstantin Ananyev{
185aa97dd1cSKonstantin Ananyev	uint32_t i;
186aa97dd1cSKonstantin Ananyev
187aa97dd1cSKonstantin Ananyev	if (ctx->nb_dev < RTE_DIM(ctx->dev)) {
188aa97dd1cSKonstantin Ananyev		for (i = 0; i != RTE_DIM(ctx->dev); i++) {
189aa97dd1cSKonstantin Ananyev			if (ctx->dev[i].ctx != ctx)
190aa97dd1cSKonstantin Ananyev				return ctx->dev + i;
191aa97dd1cSKonstantin Ananyev		}
192aa97dd1cSKonstantin Ananyev	}
193aa97dd1cSKonstantin Ananyev
194aa97dd1cSKonstantin Ananyev	rte_errno = ENODEV;
195aa97dd1cSKonstantin Ananyev	return NULL;
196aa97dd1cSKonstantin Ananyev}
197aa97dd1cSKonstantin Ananyev
198aa97dd1cSKonstantin Ananyevstruct tle_dev *
199aa97dd1cSKonstantin Ananyevtle_add_dev(struct tle_ctx *ctx, const struct tle_dev_param *dev_prm)
200aa97dd1cSKonstantin Ananyev{
201aa97dd1cSKonstantin Ananyev	int32_t rc;
2027e18fa1bSKonstantin Ananyev	uint32_t df;
203aa97dd1cSKonstantin Ananyev	struct tle_dev *dev;
204aa97dd1cSKonstantin Ananyev
205aa97dd1cSKonstantin Ananyev	if (ctx == NULL || dev_prm == NULL || check_dev_prm(dev_prm) != 0) {
206aa97dd1cSKonstantin Ananyev		rte_errno = EINVAL;
207aa97dd1cSKonstantin Ananyev		return NULL;
208aa97dd1cSKonstantin Ananyev	}
209aa97dd1cSKonstantin Ananyev
210aa97dd1cSKonstantin Ananyev	dev = find_free_dev(ctx);
211aa97dd1cSKonstantin Ananyev	if (dev == NULL)
212aa97dd1cSKonstantin Ananyev		return NULL;
213aa97dd1cSKonstantin Ananyev	rc = 0;
214aa97dd1cSKonstantin Ananyev
215aa97dd1cSKonstantin Ananyev	/* device can handle IPv4 traffic */
216aa97dd1cSKonstantin Ananyev	if (dev_prm->local_addr4.s_addr != INADDR_ANY) {
217aa97dd1cSKonstantin Ananyev		rc = init_dev_proto(dev, TLE_V4, ctx->prm.socket_id,
218aa97dd1cSKonstantin Ananyev			&dev_prm->bl4);
219aa97dd1cSKonstantin Ananyev		if (rc == 0)
220aa97dd1cSKonstantin Ananyev			fill_pbm(&ctx->use[TLE_V4], &dev_prm->bl4);
221aa97dd1cSKonstantin Ananyev	}
222aa97dd1cSKonstantin Ananyev
223aa97dd1cSKonstantin Ananyev	/* device can handle IPv6 traffic */
224aa97dd1cSKonstantin Ananyev	if (rc == 0 && memcmp(&dev_prm->local_addr6, &tle_ipv6_any,
225aa97dd1cSKonstantin Ananyev			sizeof(tle_ipv6_any)) != 0) {
226aa97dd1cSKonstantin Ananyev		rc = init_dev_proto(dev, TLE_V6, ctx->prm.socket_id,
227aa97dd1cSKonstantin Ananyev			&dev_prm->bl6);
228aa97dd1cSKonstantin Ananyev		if (rc == 0)
229aa97dd1cSKonstantin Ananyev			fill_pbm(&ctx->use[TLE_V6], &dev_prm->bl6);
230aa97dd1cSKonstantin Ananyev	}
231aa97dd1cSKonstantin Ananyev
232aa97dd1cSKonstantin Ananyev	if (rc != 0) {
233aa97dd1cSKonstantin Ananyev		/* cleanup and return an error. */
234aa97dd1cSKonstantin Ananyev		rte_free(dev->dp[TLE_V4]);
235aa97dd1cSKonstantin Ananyev		rte_free(dev->dp[TLE_V6]);
236aa97dd1cSKonstantin Ananyev		rte_errno = rc;
237aa97dd1cSKonstantin Ananyev		return NULL;
238aa97dd1cSKonstantin Ananyev	}
239aa97dd1cSKonstantin Ananyev
240aa97dd1cSKonstantin Ananyev	/* setup TX data. */
2417e18fa1bSKonstantin Ananyev	df = ((ctx->prm.flags & TLE_CTX_FLAG_ST) == 0) ? 0 :
2427e18fa1bSKonstantin Ananyev		RING_F_SP_ENQ | RING_F_SC_DEQ;
2437e18fa1bSKonstantin Ananyev	tle_dring_reset(&dev->tx.dr, df);
244aa97dd1cSKonstantin Ananyev
245aa97dd1cSKonstantin Ananyev	if ((dev_prm->tx_offload & DEV_TX_OFFLOAD_UDP_CKSUM) != 0 &&
246aa97dd1cSKonstantin Ananyev			ctx->prm.proto == TLE_PROTO_UDP) {
247aa97dd1cSKonstantin Ananyev		dev->tx.ol_flags[TLE_V4] |= PKT_TX_IPV4 | PKT_TX_UDP_CKSUM;
248aa97dd1cSKonstantin Ananyev		dev->tx.ol_flags[TLE_V6] |= PKT_TX_IPV6 | PKT_TX_UDP_CKSUM;
249aa97dd1cSKonstantin Ananyev	} else if ((dev_prm->tx_offload & DEV_TX_OFFLOAD_TCP_CKSUM) != 0 &&
250aa97dd1cSKonstantin Ananyev			ctx->prm.proto == TLE_PROTO_TCP) {
251aa97dd1cSKonstantin Ananyev		dev->tx.ol_flags[TLE_V4] |= PKT_TX_IPV4 | PKT_TX_TCP_CKSUM;
252aa97dd1cSKonstantin Ananyev		dev->tx.ol_flags[TLE_V6] |= PKT_TX_IPV6 | PKT_TX_TCP_CKSUM;
253aa97dd1cSKonstantin Ananyev	}
254aa97dd1cSKonstantin Ananyev
255aa97dd1cSKonstantin Ananyev	if ((dev_prm->tx_offload & DEV_TX_OFFLOAD_IPV4_CKSUM) != 0)
256aa97dd1cSKonstantin Ananyev		dev->tx.ol_flags[TLE_V4] |= PKT_TX_IPV4 | PKT_TX_IP_CKSUM;
257aa97dd1cSKonstantin Ananyev
258aa97dd1cSKonstantin Ananyev	dev->prm = *dev_prm;
259aa97dd1cSKonstantin Ananyev	dev->ctx = ctx;
260aa97dd1cSKonstantin Ananyev	ctx->nb_dev++;
261aa97dd1cSKonstantin Ananyev
262aa97dd1cSKonstantin Ananyev	return dev;
263aa97dd1cSKonstantin Ananyev}
264aa97dd1cSKonstantin Ananyev
265aa97dd1cSKonstantin Ananyevstatic void
266aa97dd1cSKonstantin Ananyevempty_dring(struct tle_dring *dr, uint32_t proto)
267aa97dd1cSKonstantin Ananyev{
268aa97dd1cSKonstantin Ananyev	uint32_t i, k, n;
269aa97dd1cSKonstantin Ananyev	struct tle_stream *s;
270aa97dd1cSKonstantin Ananyev	struct rte_mbuf *pkt[MAX_PKT_BURST];
271aa97dd1cSKonstantin Ananyev	struct tle_drb *drb[MAX_PKT_BURST];
272aa97dd1cSKonstantin Ananyev
273aa97dd1cSKonstantin Ananyev	do {
274aa97dd1cSKonstantin Ananyev		k = RTE_DIM(drb);
275aa97dd1cSKonstantin Ananyev		n = tle_dring_sc_dequeue(dr, (const void **)(uintptr_t)pkt,
276aa97dd1cSKonstantin Ananyev			RTE_DIM(pkt), drb, &k);
277aa97dd1cSKonstantin Ananyev
278aa97dd1cSKonstantin Ananyev		/* free mbufs */
279aa97dd1cSKonstantin Ananyev		for (i = 0; i != n; i++)
280aa97dd1cSKonstantin Ananyev			rte_pktmbuf_free(pkt[i]);
281aa97dd1cSKonstantin Ananyev		/* free drbs */
282aa97dd1cSKonstantin Ananyev		for (i = 0; i != k; i++) {
283aa97dd1cSKonstantin Ananyev			s = drb[i]->udata;
284aa97dd1cSKonstantin Ananyev			tle_stream_ops[proto].free_drbs(s, drb + i, 1);
285aa97dd1cSKonstantin Ananyev		}
286aa97dd1cSKonstantin Ananyev	} while (n != 0);
287aa97dd1cSKonstantin Ananyev}
288aa97dd1cSKonstantin Ananyev
289aa97dd1cSKonstantin Ananyevint
290aa97dd1cSKonstantin Ananyevtle_del_dev(struct tle_dev *dev)
291aa97dd1cSKonstantin Ananyev{
292aa97dd1cSKonstantin Ananyev	uint32_t p;
293aa97dd1cSKonstantin Ananyev	struct tle_ctx *ctx;
294aa97dd1cSKonstantin Ananyev
295aa97dd1cSKonstantin Ananyev	if (dev == NULL || dev->ctx == NULL)
296aa97dd1cSKonstantin Ananyev		return -EINVAL;
297aa97dd1cSKonstantin Ananyev
298aa97dd1cSKonstantin Ananyev	ctx = dev->ctx;
299aa97dd1cSKonstantin Ananyev	p = dev - ctx->dev;
300aa97dd1cSKonstantin Ananyev
301aa97dd1cSKonstantin Ananyev	if (p >= RTE_DIM(ctx->dev) ||
302aa97dd1cSKonstantin Ananyev			(dev->dp[TLE_V4] == NULL &&
303aa97dd1cSKonstantin Ananyev			dev->dp[TLE_V6] == NULL))
304aa97dd1cSKonstantin Ananyev		return -EINVAL;
305aa97dd1cSKonstantin Ananyev
306aa97dd1cSKonstantin Ananyev	/* emtpy TX queues. */
307aa97dd1cSKonstantin Ananyev	empty_dring(&dev->tx.dr, ctx->prm.proto);
308aa97dd1cSKonstantin Ananyev
309aa97dd1cSKonstantin Ananyev	rte_free(dev->dp[TLE_V4]);
310aa97dd1cSKonstantin Ananyev	rte_free(dev->dp[TLE_V6]);
311aa97dd1cSKonstantin Ananyev	memset(dev, 0, sizeof(*dev));
312aa97dd1cSKonstantin Ananyev	ctx->nb_dev--;
313aa97dd1cSKonstantin Ananyev	return 0;
314aa97dd1cSKonstantin Ananyev}
315aa97dd1cSKonstantin Ananyev
316aa97dd1cSKonstantin Ananyevstatic struct tle_dev *
317aa97dd1cSKonstantin Ananyevfind_ipv4_dev(struct tle_ctx *ctx, const struct in_addr *addr)
318aa97dd1cSKonstantin Ananyev{
319aa97dd1cSKonstantin Ananyev	uint32_t i;
320aa97dd1cSKonstantin Ananyev
321aa97dd1cSKonstantin Ananyev	for (i = 0; i != RTE_DIM(ctx->dev); i++) {
322aa97dd1cSKonstantin Ananyev		if (ctx->dev[i].prm.local_addr4.s_addr == addr->s_addr &&
323aa97dd1cSKonstantin Ananyev				ctx->dev[i].dp[TLE_V4] != NULL)
324aa97dd1cSKonstantin Ananyev			return ctx->dev + i;
325aa97dd1cSKonstantin Ananyev	}
326aa97dd1cSKonstantin Ananyev
327aa97dd1cSKonstantin Ananyev	return NULL;
328aa97dd1cSKonstantin Ananyev}
329aa97dd1cSKonstantin Ananyev
330aa97dd1cSKonstantin Ananyevstatic struct tle_dev *
331aa97dd1cSKonstantin Ananyevfind_ipv6_dev(struct tle_ctx *ctx, const struct in6_addr *addr)
332aa97dd1cSKonstantin Ananyev{
333aa97dd1cSKonstantin Ananyev	uint32_t i;
334aa97dd1cSKonstantin Ananyev
335aa97dd1cSKonstantin Ananyev	for (i = 0; i != RTE_DIM(ctx->dev); i++) {
336aa97dd1cSKonstantin Ananyev		if (memcmp(&ctx->dev[i].prm.local_addr6, addr,
337aa97dd1cSKonstantin Ananyev				sizeof(*addr)) == 0 &&
338aa97dd1cSKonstantin Ananyev				ctx->dev[i].dp[TLE_V6] != NULL)
339aa97dd1cSKonstantin Ananyev			return ctx->dev + i;
340aa97dd1cSKonstantin Ananyev	}
341aa97dd1cSKonstantin Ananyev
342aa97dd1cSKonstantin Ananyev	return NULL;
343aa97dd1cSKonstantin Ananyev}
344aa97dd1cSKonstantin Ananyev
345aa97dd1cSKonstantin Ananyevstatic int
346aa97dd1cSKonstantin Ananyevstream_fill_dev(struct tle_ctx *ctx, struct tle_stream *s,
347aa97dd1cSKonstantin Ananyev	const struct sockaddr *addr)
348aa97dd1cSKonstantin Ananyev{
349aa97dd1cSKonstantin Ananyev	struct tle_dev *dev;
350aa97dd1cSKonstantin Ananyev	struct tle_pbm *pbm;
351aa97dd1cSKonstantin Ananyev	const struct sockaddr_in *lin4;
352aa97dd1cSKonstantin Ananyev	const struct sockaddr_in6 *lin6;
353aa97dd1cSKonstantin Ananyev	uint32_t i, p, sp, t;
354aa97dd1cSKonstantin Ananyev
355aa97dd1cSKonstantin Ananyev	if (addr->sa_family == AF_INET) {
356aa97dd1cSKonstantin Ananyev		lin4 = (const struct sockaddr_in *)addr;
357aa97dd1cSKonstantin Ananyev		t = TLE_V4;
358aa97dd1cSKonstantin Ananyev		p = lin4->sin_port;
359aa97dd1cSKonstantin Ananyev	} else if (addr->sa_family == AF_INET6) {
360aa97dd1cSKonstantin Ananyev		lin6 = (const struct sockaddr_in6 *)addr;
361aa97dd1cSKonstantin Ananyev		t = TLE_V6;
362aa97dd1cSKonstantin Ananyev		p = lin6->sin6_port;
363aa97dd1cSKonstantin Ananyev	} else
364aa97dd1cSKonstantin Ananyev		return EINVAL;
365aa97dd1cSKonstantin Ananyev
366aa97dd1cSKonstantin Ananyev	p = ntohs(p);
367aa97dd1cSKonstantin Ananyev
368aa97dd1c