1 /*
2  * Copyright (c) 2015 Intel Corporation
3  * Copyright (c) 2023 Arm Limited (or its affiliates). All rights reserved.
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_zperf, CONFIG_NET_ZPERF_LOG_LEVEL);
10 
11 #include <zephyr/kernel.h>
12 
13 #include <zephyr/net/net_pkt.h>
14 #include <zephyr/net/udp.h>
15 
16 #include "zperf_session.h"
17 
18 #define SESSION_MAX CONFIG_NET_ZPERF_MAX_SESSIONS
19 
20 static struct session sessions[SESSION_PROTO_END][SESSION_MAX];
21 
get_free_session(const struct sockaddr * addr,enum session_proto proto)22 struct session *get_free_session(const struct sockaddr *addr,
23 				 enum session_proto proto)
24 {
25 	struct session *ptr;
26 	uint64_t oldest = 0ULL;
27 	int oldest_completed_index = -1, oldest_free_index = -1;
28 	int i = 0;
29 
30 	const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
31 	const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
32 
33 	/* Check whether we already have an active session */
34 	while (i < SESSION_MAX) {
35 		ptr = &sessions[proto][i];
36 
37 		if (ptr->state == STATE_NULL ||
38 		    ptr->state == STATE_COMPLETED) {
39 
40 			if (oldest == 0ULL || ptr->last_time < oldest) {
41 				oldest = ptr->last_time;
42 
43 				if (ptr->state == STATE_COMPLETED) {
44 					if (oldest_completed_index < 0) {
45 						oldest_completed_index = i;
46 					}
47 				} else {
48 					/* Free session */
49 					if (oldest_free_index < 0) {
50 						oldest_free_index = i;
51 					}
52 				}
53 			}
54 		}
55 
56 		i++;
57 	}
58 
59 	ptr = NULL;
60 
61 	if (oldest_free_index >= 0) {
62 		ptr = &sessions[proto][oldest_free_index];
63 	} else if (oldest_completed_index >= 0) {
64 		ptr = &sessions[proto][oldest_completed_index];
65 	}
66 
67 	if (ptr != NULL) {
68 		if (IS_ENABLED(CONFIG_NET_IPV4) &&
69 		    addr->sa_family == AF_INET) {
70 			ptr->port = addr4->sin_port;
71 			ptr->ip.family = AF_INET;
72 			net_ipaddr_copy(&ptr->ip.in_addr, &addr4->sin_addr);
73 		} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
74 			   addr->sa_family == AF_INET6) {
75 			ptr->port = addr6->sin6_port;
76 			ptr->ip.family = AF_INET6;
77 			net_ipaddr_copy(&ptr->ip.in6_addr, &addr6->sin6_addr);
78 		}
79 
80 		ptr->state = STATE_STARTING;
81 	}
82 
83 	return ptr;
84 }
85 
86 /* Get session from a given packet */
get_session(const struct sockaddr * addr,enum session_proto proto)87 struct session *get_session(const struct sockaddr *addr,
88 			    enum session_proto proto)
89 {
90 	struct session *active = NULL;
91 	struct session *free = NULL;
92 	int i = 0;
93 	const struct sockaddr_in *addr4 = (const struct sockaddr_in *)addr;
94 	const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)addr;
95 
96 	if (proto != SESSION_TCP && proto != SESSION_UDP) {
97 		NET_ERR("Error! unsupported proto.\n");
98 		return NULL;
99 	}
100 
101 	/* Check whether we already have an active session */
102 	while (!active && i < SESSION_MAX) {
103 		struct session *ptr = &sessions[proto][i];
104 
105 		if (IS_ENABLED(CONFIG_NET_IPV4) &&
106 		    addr->sa_family == AF_INET &&
107 		    ptr->ip.family == AF_INET &&
108 		    ptr->port == addr4->sin_port &&
109 		    net_ipv4_addr_cmp(&ptr->ip.in_addr, &addr4->sin_addr)) {
110 			/* We found an active session */
111 			active = ptr;
112 			break;
113 		}
114 
115 		if (IS_ENABLED(CONFIG_NET_IPV6) &&
116 		    addr->sa_family == AF_INET6 &&
117 		    ptr->ip.family == AF_INET6 &&
118 		    ptr->port == addr6->sin6_port &&
119 		    net_ipv6_addr_cmp(&ptr->ip.in6_addr, &addr6->sin6_addr)) {
120 			/* We found an active session */
121 			active = ptr;
122 			break;
123 		}
124 
125 		if (!free && (ptr->state == STATE_NULL ||
126 			      ptr->state == STATE_COMPLETED)) {
127 			/* We found a free slot - just in case */
128 			free = ptr;
129 		}
130 
131 		i++;
132 	}
133 
134 	/* If no active session then create a new one */
135 	if (!active && free) {
136 		active = free;
137 
138 		if (IS_ENABLED(CONFIG_NET_IPV4) && addr->sa_family == AF_INET) {
139 			active->port = addr4->sin_port;
140 			active->ip.family = AF_INET;
141 			net_ipaddr_copy(&active->ip.in_addr, &addr4->sin_addr);
142 		} else if (IS_ENABLED(CONFIG_NET_IPV6) &&
143 			   addr->sa_family == AF_INET6) {
144 			active->port = addr6->sin6_port;
145 			active->ip.family = AF_INET6;
146 			net_ipaddr_copy(&active->ip.in6_addr, &addr6->sin6_addr);
147 		}
148 	}
149 
150 	return active;
151 }
152 
zperf_reset_session_stats(struct session * session)153 void zperf_reset_session_stats(struct session *session)
154 {
155 	if (!session) {
156 		return;
157 	}
158 
159 	session->counter = 0U;
160 	session->start_time = 0U;
161 	session->next_id = 1U;
162 	session->length = 0U;
163 	session->outorder = 0U;
164 	session->error = 0U;
165 	session->jitter = 0;
166 	session->last_transit_time = 0;
167 }
168 
zperf_session_foreach(enum session_proto proto,session_cb_t cb,void * user_data)169 void zperf_session_foreach(enum session_proto proto, session_cb_t cb,
170 			   void *user_data)
171 {
172 	ARRAY_FOR_EACH(sessions[proto], i) {
173 		cb(&sessions[proto][i], proto, user_data);
174 	}
175 }
176 
zperf_session_reset(enum session_proto proto)177 void zperf_session_reset(enum session_proto proto)
178 {
179 	int i, j;
180 
181 	if (proto >= SESSION_PROTO_END) {
182 		return;
183 	}
184 
185 	i = (int)proto;
186 
187 	for (j = 0; j < SESSION_MAX; j++) {
188 		sessions[i][j].state = STATE_NULL;
189 		sessions[i][j].id = j;
190 		zperf_reset_session_stats(&(sessions[i][j]));
191 	}
192 }
193 
zperf_session_init(void)194 void zperf_session_init(void)
195 {
196 	int i;
197 
198 	for (i = 0; i < SESSION_PROTO_END; i++) {
199 		zperf_session_reset(i);
200 	}
201 }
202