990cf40fce2f8fcedd05108ae334064e0acca6bc
[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 #define __USE_GNU
12 #include <sys/socket.h>
13
14 #include <sys/epoll.h>
15 #include <netinet/in.h>
16 #include <arpa/inet.h>
17 #include <errno.h>
18
19 #include "conn.h"
20
21 #define MSG_CMD_TOO_LONG "Command too long."
22
23 struct softnic_conn {
24         char *welcome;
25         char *prompt;
26         char *buf;
27         char *msg_in;
28         char *msg_out;
29         size_t buf_size;
30         size_t msg_in_len_max;
31         size_t msg_out_len_max;
32         size_t msg_in_len;
33         int fd_server;
34         int fd_client_group;
35         softnic_conn_msg_handle_t msg_handle;
36         void *msg_handle_arg;
37 };
38
39 struct softnic_conn *
40 softnic_conn_init(struct softnic_conn_params *p)
41 {
42         struct sockaddr_in server_address;
43         struct softnic_conn *conn;
44         int fd_server, fd_client_group, status;
45
46         memset(&server_address, 0, sizeof(server_address));
47
48         /* Check input arguments */
49         if (p == NULL ||
50                 p->welcome == NULL ||
51                 p->prompt == NULL ||
52                 p->addr == NULL ||
53                 p->buf_size == 0 ||
54                 p->msg_in_len_max == 0 ||
55                 p->msg_out_len_max == 0 ||
56                 p->msg_handle == NULL)
57                 return NULL;
58
59         status = inet_aton(p->addr, &server_address.sin_addr);
60         if (status == 0)
61                 return NULL;
62
63         /* Memory allocation */
64         conn = calloc(1, sizeof(struct softnic_conn));
65         if (conn == NULL)
66                 return NULL;
67
68         conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1);
69         conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1);
70         conn->buf = calloc(1, p->buf_size);
71         conn->msg_in = calloc(1, p->msg_in_len_max + 1);
72         conn->msg_out = calloc(1, p->msg_out_len_max + 1);
73
74         if (conn->welcome == NULL ||
75                 conn->prompt == NULL ||
76                 conn->buf == NULL ||
77                 conn->msg_in == NULL ||
78                 conn->msg_out == NULL) {
79                 softnic_conn_free(conn);
80                 return NULL;
81         }
82
83         /* Server socket */
84         server_address.sin_family = AF_INET;
85         server_address.sin_port = htons(p->port);
86
87         fd_server = socket(AF_INET,
88                 SOCK_STREAM | SOCK_NONBLOCK,
89                 0);
90         if (fd_server == -1) {
91                 softnic_conn_free(conn);
92                 return NULL;
93         }
94
95         status = bind(fd_server,
96                 (struct sockaddr *)&server_address,
97                 sizeof(server_address));
98         if (status == -1) {
99                 softnic_conn_free(conn);
100                 close(fd_server);
101                 return NULL;
102         }
103
104         status = listen(fd_server, 16);
105         if (status == -1) {
106                 softnic_conn_free(conn);
107                 close(fd_server);
108                 return NULL;
109         }
110
111         /* Client group */
112         fd_client_group = epoll_create(1);
113         if (fd_client_group == -1) {
114                 softnic_conn_free(conn);
115                 close(fd_server);
116                 return NULL;
117         }
118
119         /* Fill in */
120         strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
121         strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX);
122         conn->buf_size = p->buf_size;
123         conn->msg_in_len_max = p->msg_in_len_max;
124         conn->msg_out_len_max = p->msg_out_len_max;
125         conn->msg_in_len = 0;
126         conn->fd_server = fd_server;
127         conn->fd_client_group = fd_client_group;
128         conn->msg_handle = p->msg_handle;
129         conn->msg_handle_arg = p->msg_handle_arg;
130
131         return conn;
132 }
133
134 void
135 softnic_conn_free(struct softnic_conn *conn)
136 {
137         if (conn == NULL)
138                 return;
139
140         if (conn->fd_client_group)
141                 close(conn->fd_client_group);
142
143         if (conn->fd_server)
144                 close(conn->fd_server);
145
146         free(conn->msg_out);
147         free(conn->msg_in);
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 }