net/i40e: fix Rx packet statistics
[dpdk.git] / drivers / net / softnic / conn.c
1 /* SPDX-License-Identifier: BSD-3-Clause
2  * Copyright(c) 2010-2018 Intel Corporation
3  */
4
5 #include <string.h>
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <sys/types.h>
10
11 #include <sys/socket.h>
12
13 #include <sys/epoll.h>
14 #include <netinet/in.h>
15 #include <arpa/inet.h>
16 #include <errno.h>
17
18 #include "conn.h"
19
20 #define MSG_CMD_TOO_LONG "Command too long."
21
22 struct softnic_conn {
23         char *welcome;
24         char *prompt;
25         char *buf;
26         char *msg_in;
27         char *msg_out;
28         size_t buf_size;
29         size_t msg_in_len_max;
30         size_t msg_out_len_max;
31         size_t msg_in_len;
32         int fd_server;
33         int fd_client_group;
34         softnic_conn_msg_handle_t msg_handle;
35         void *msg_handle_arg;
36 };
37
38 struct softnic_conn *
39 softnic_conn_init(struct softnic_conn_params *p)
40 {
41         struct sockaddr_in server_address;
42         struct softnic_conn *conn;
43         int fd_server, fd_client_group, status;
44
45         memset(&server_address, 0, sizeof(server_address));
46
47         /* Check input arguments */
48         if (p == NULL ||
49                 p->welcome == NULL ||
50                 p->prompt == NULL ||
51                 p->addr == NULL ||
52                 p->buf_size == 0 ||
53                 p->msg_in_len_max == 0 ||
54                 p->msg_out_len_max == 0 ||
55                 p->msg_handle == NULL)
56                 return NULL;
57
58         status = inet_aton(p->addr, &server_address.sin_addr);
59         if (status == 0)
60                 return NULL;
61
62         /* Memory allocation */
63         conn = calloc(1, sizeof(struct softnic_conn));
64         if (conn == NULL)
65                 return NULL;
66
67         conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1);
68         conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1);
69         conn->buf = calloc(1, p->buf_size);
70         conn->msg_in = calloc(1, p->msg_in_len_max + 1);
71         conn->msg_out = calloc(1, p->msg_out_len_max + 1);
72
73         if (conn->welcome == NULL ||
74                 conn->prompt == NULL ||
75                 conn->buf == NULL ||
76                 conn->msg_in == NULL ||
77                 conn->msg_out == NULL) {
78                 softnic_conn_free(conn);
79                 return NULL;
80         }
81
82         /* Server socket */
83         server_address.sin_family = AF_INET;
84         server_address.sin_port = htons(p->port);
85
86         fd_server = socket(AF_INET,
87                 SOCK_STREAM | SOCK_NONBLOCK,
88                 0);
89         if (fd_server == -1) {
90                 softnic_conn_free(conn);
91                 return NULL;
92         }
93
94         status = bind(fd_server,
95                 (struct sockaddr *)&server_address,
96                 sizeof(server_address));
97         if (status == -1) {
98                 softnic_conn_free(conn);
99                 close(fd_server);
100                 return NULL;
101         }
102
103         status = listen(fd_server, 16);
104         if (status == -1) {
105                 softnic_conn_free(conn);
106                 close(fd_server);
107                 return NULL;
108         }
109
110         /* Client group */
111         fd_client_group = epoll_create(1);
112         if (fd_client_group == -1) {
113                 softnic_conn_free(conn);
114                 close(fd_server);
115                 return NULL;
116         }
117
118         /* Fill in */
119         strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
120         strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX);
121         conn->buf_size = p->buf_size;
122         conn->msg_in_len_max = p->msg_in_len_max;
123         conn->msg_out_len_max = p->msg_out_len_max;
124         conn->msg_in_len = 0;
125         conn->fd_server = fd_server;
126         conn->fd_client_group = fd_client_group;
127         conn->msg_handle = p->msg_handle;
128         conn->msg_handle_arg = p->msg_handle_arg;
129
130         return conn;
131 }
132
133 void
134 softnic_conn_free(struct softnic_conn *conn)
135 {
136         if (conn == NULL)
137                 return;
138
139         if (conn->fd_client_group)
140                 close(conn->fd_client_group);
141
142         if (conn->fd_server)
143                 close(conn->fd_server);
144
145         free(conn->msg_out);
146         free(conn->msg_in);
147         free(conn->buf);
148         free(conn->prompt);
149         free(conn->welcome);
150         free(conn);
151 }
152
153 int
154 softnic_conn_poll_for_conn(struct softnic_conn *conn)
155 {
156         struct sockaddr_in client_address;
157         struct epoll_event event;
158         socklen_t client_address_length;
159         int fd_client, status;
160
161         /* Check input arguments */
162         if (conn == NULL)
163                 return -1;
164
165         /* Server socket */
166         client_address_length = sizeof(client_address);
167         fd_client = accept4(conn->fd_server,
168                 (struct sockaddr *)&client_address,
169                 &client_address_length,
170                 SOCK_NONBLOCK);
171         if (fd_client == -1) {
172                 if (errno == EAGAIN || errno == EWOULDBLOCK)
173                         return 0;
174
175                 return -1;
176         }
177
178         /* Client group */
179         event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
180         event.data.fd = fd_client;
181
182         status = epoll_ctl(conn->fd_client_group,
183                 EPOLL_CTL_ADD,
184                 fd_client,
185                 &event);
186         if (status == -1) {
187                 close(fd_client);
188                 return -1;
189         }
190
191         /* Client */
192         status = write(fd_client,
193                 conn->welcome,
194                 strlen(conn->welcome));
195         if (status == -1) {
196                 close(fd_client);
197                 return -1;
198         }
199
200         status = write(fd_client,
201                 conn->prompt,
202                 strlen(conn->prompt));
203         if (status == -1) {
204                 close(fd_client);
205                 return -1;
206         }
207
208         return 0;
209 }
210
211 static int
212 data_event_handle(struct softnic_conn *conn,
213         int fd_client)
214 {
215         ssize_t len, i, status;
216
217         /* Read input message */
218
219         len = read(fd_client,
220                 conn->buf,
221                 conn->buf_size);
222         if (len == -1) {
223                 if (errno == EAGAIN || errno == EWOULDBLOCK)
224                         return 0;
225
226                 return -1;
227         }
228         if (len == 0)
229                 return 0;
230
231         /* Handle input messages */
232         for (i = 0; i < len; i++) {
233                 if (conn->buf[i] == '\n') {
234                         size_t n;
235
236                         conn->msg_in[conn->msg_in_len] = 0;
237                         conn->msg_out[0] = 0;
238
239                         conn->msg_handle(conn->msg_in,
240                                 conn->msg_out,
241                                 conn->msg_out_len_max,
242                                 conn->msg_handle_arg);
243
244                         n = strlen(conn->msg_out);
245                         if (n) {
246                                 status = write(fd_client,
247                                         conn->msg_out,
248                                         n);
249                                 if (status == -1)
250                                         return status;
251                         }
252
253                         conn->msg_in_len = 0;
254                 } else if (conn->msg_in_len < conn->msg_in_len_max) {
255                         conn->msg_in[conn->msg_in_len] = conn->buf[i];
256                         conn->msg_in_len++;
257                 } else {
258                         status = write(fd_client,
259                                 MSG_CMD_TOO_LONG,
260                                 strlen(MSG_CMD_TOO_LONG));
261                         if (status == -1)
262                                 return status;
263
264                         conn->msg_in_len = 0;
265                 }
266         }
267
268         /* Write prompt */
269         status = write(fd_client,
270                 conn->prompt,
271                 strlen(conn->prompt));
272         if (status == -1)
273                 return status;
274
275         return 0;
276 }
277
278 static int
279 control_event_handle(struct softnic_conn *conn,
280         int fd_client)
281 {
282         int status;
283
284         status = epoll_ctl(conn->fd_client_group,
285                 EPOLL_CTL_DEL,
286                 fd_client,
287                 NULL);
288         if (status == -1)
289                 return -1;
290
291         status = close(fd_client);
292         if (status == -1)
293                 return -1;
294
295         return 0;
296 }
297
298 int
299 softnic_conn_poll_for_msg(struct softnic_conn *conn)
300 {
301         struct epoll_event event;
302         int fd_client, status, status_data = 0, status_control = 0;
303
304         /* Check input arguments */
305         if (conn == NULL)
306                 return -1;
307
308         /* Client group */
309         status = epoll_wait(conn->fd_client_group,
310                 &event,
311                 1,
312                 0);
313         if (status == -1)
314                 return -1;
315         if (status == 0)
316                 return 0;
317
318         fd_client = event.data.fd;
319
320         /* Data available */
321         if (event.events & EPOLLIN)
322                 status_data = data_event_handle(conn, fd_client);
323
324         /* Control events */
325         if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP))
326                 status_control = control_event_handle(conn, fd_client);
327
328         if (status_data || status_control)
329                 return -1;
330
331         return 0;
332 }