17709a63bSJasvinder Singh /* SPDX-License-Identifier: BSD-3-Clause
27709a63bSJasvinder Singh * Copyright(c) 2010-2018 Intel Corporation
37709a63bSJasvinder Singh */
47709a63bSJasvinder Singh
57709a63bSJasvinder Singh #include <string.h>
67709a63bSJasvinder Singh #include <stdlib.h>
77709a63bSJasvinder Singh #include <stdio.h>
87709a63bSJasvinder Singh #include <unistd.h>
97709a63bSJasvinder Singh #include <sys/types.h>
107709a63bSJasvinder Singh
117709a63bSJasvinder Singh #include <sys/socket.h>
127709a63bSJasvinder Singh
137709a63bSJasvinder Singh #include <sys/epoll.h>
147709a63bSJasvinder Singh #include <netinet/in.h>
157709a63bSJasvinder Singh #include <arpa/inet.h>
167709a63bSJasvinder Singh #include <errno.h>
177709a63bSJasvinder Singh
187709a63bSJasvinder Singh #include "conn.h"
197709a63bSJasvinder Singh
207709a63bSJasvinder Singh #define MSG_CMD_TOO_LONG "Command too long."
217709a63bSJasvinder Singh
227709a63bSJasvinder Singh struct softnic_conn {
237709a63bSJasvinder Singh char *welcome;
247709a63bSJasvinder Singh char *prompt;
257709a63bSJasvinder Singh char *buf;
267709a63bSJasvinder Singh char *msg_in;
277709a63bSJasvinder Singh char *msg_out;
287709a63bSJasvinder Singh size_t buf_size;
297709a63bSJasvinder Singh size_t msg_in_len_max;
307709a63bSJasvinder Singh size_t msg_out_len_max;
317709a63bSJasvinder Singh size_t msg_in_len;
327709a63bSJasvinder Singh int fd_server;
337709a63bSJasvinder Singh int fd_client_group;
347709a63bSJasvinder Singh softnic_conn_msg_handle_t msg_handle;
357709a63bSJasvinder Singh void *msg_handle_arg;
367709a63bSJasvinder Singh };
377709a63bSJasvinder Singh
387709a63bSJasvinder Singh struct softnic_conn *
softnic_conn_init(struct softnic_conn_params * p)397709a63bSJasvinder Singh softnic_conn_init(struct softnic_conn_params *p)
407709a63bSJasvinder Singh {
417709a63bSJasvinder Singh struct sockaddr_in server_address;
427709a63bSJasvinder Singh struct softnic_conn *conn;
437709a63bSJasvinder Singh int fd_server, fd_client_group, status;
447709a63bSJasvinder Singh
457709a63bSJasvinder Singh memset(&server_address, 0, sizeof(server_address));
467709a63bSJasvinder Singh
477709a63bSJasvinder Singh /* Check input arguments */
487709a63bSJasvinder Singh if (p == NULL ||
497709a63bSJasvinder Singh p->welcome == NULL ||
507709a63bSJasvinder Singh p->prompt == NULL ||
517709a63bSJasvinder Singh p->addr == NULL ||
527709a63bSJasvinder Singh p->buf_size == 0 ||
537709a63bSJasvinder Singh p->msg_in_len_max == 0 ||
547709a63bSJasvinder Singh p->msg_out_len_max == 0 ||
557709a63bSJasvinder Singh p->msg_handle == NULL)
567709a63bSJasvinder Singh return NULL;
577709a63bSJasvinder Singh
587709a63bSJasvinder Singh status = inet_aton(p->addr, &server_address.sin_addr);
597709a63bSJasvinder Singh if (status == 0)
607709a63bSJasvinder Singh return NULL;
617709a63bSJasvinder Singh
627709a63bSJasvinder Singh /* Memory allocation */
637709a63bSJasvinder Singh conn = calloc(1, sizeof(struct softnic_conn));
647709a63bSJasvinder Singh if (conn == NULL)
657709a63bSJasvinder Singh return NULL;
667709a63bSJasvinder Singh
677709a63bSJasvinder Singh conn->welcome = calloc(1, CONN_WELCOME_LEN_MAX + 1);
687709a63bSJasvinder Singh conn->prompt = calloc(1, CONN_PROMPT_LEN_MAX + 1);
697709a63bSJasvinder Singh conn->buf = calloc(1, p->buf_size);
707709a63bSJasvinder Singh conn->msg_in = calloc(1, p->msg_in_len_max + 1);
717709a63bSJasvinder Singh conn->msg_out = calloc(1, p->msg_out_len_max + 1);
727709a63bSJasvinder Singh
737709a63bSJasvinder Singh if (conn->welcome == NULL ||
747709a63bSJasvinder Singh conn->prompt == NULL ||
757709a63bSJasvinder Singh conn->buf == NULL ||
767709a63bSJasvinder Singh conn->msg_in == NULL ||
777709a63bSJasvinder Singh conn->msg_out == NULL) {
787709a63bSJasvinder Singh softnic_conn_free(conn);
797709a63bSJasvinder Singh return NULL;
807709a63bSJasvinder Singh }
817709a63bSJasvinder Singh
827709a63bSJasvinder Singh /* Server socket */
837709a63bSJasvinder Singh server_address.sin_family = AF_INET;
847709a63bSJasvinder Singh server_address.sin_port = htons(p->port);
857709a63bSJasvinder Singh
867709a63bSJasvinder Singh fd_server = socket(AF_INET,
877709a63bSJasvinder Singh SOCK_STREAM | SOCK_NONBLOCK,
887709a63bSJasvinder Singh 0);
897709a63bSJasvinder Singh if (fd_server == -1) {
907709a63bSJasvinder Singh softnic_conn_free(conn);
917709a63bSJasvinder Singh return NULL;
927709a63bSJasvinder Singh }
937709a63bSJasvinder Singh
947709a63bSJasvinder Singh status = bind(fd_server,
957709a63bSJasvinder Singh (struct sockaddr *)&server_address,
967709a63bSJasvinder Singh sizeof(server_address));
977709a63bSJasvinder Singh if (status == -1) {
987709a63bSJasvinder Singh softnic_conn_free(conn);
997709a63bSJasvinder Singh close(fd_server);
1007709a63bSJasvinder Singh return NULL;
1017709a63bSJasvinder Singh }
1027709a63bSJasvinder Singh
1037709a63bSJasvinder Singh status = listen(fd_server, 16);
1047709a63bSJasvinder Singh if (status == -1) {
1057709a63bSJasvinder Singh softnic_conn_free(conn);
1067709a63bSJasvinder Singh close(fd_server);
1077709a63bSJasvinder Singh return NULL;
1087709a63bSJasvinder Singh }
1097709a63bSJasvinder Singh
1107709a63bSJasvinder Singh /* Client group */
1117709a63bSJasvinder Singh fd_client_group = epoll_create(1);
1127709a63bSJasvinder Singh if (fd_client_group == -1) {
1137709a63bSJasvinder Singh softnic_conn_free(conn);
1147709a63bSJasvinder Singh close(fd_server);
1157709a63bSJasvinder Singh return NULL;
1167709a63bSJasvinder Singh }
1177709a63bSJasvinder Singh
1187709a63bSJasvinder Singh /* Fill in */
1197709a63bSJasvinder Singh strncpy(conn->welcome, p->welcome, CONN_WELCOME_LEN_MAX);
1207709a63bSJasvinder Singh strncpy(conn->prompt, p->prompt, CONN_PROMPT_LEN_MAX);
1217709a63bSJasvinder Singh conn->buf_size = p->buf_size;
1227709a63bSJasvinder Singh conn->msg_in_len_max = p->msg_in_len_max;
1237709a63bSJasvinder Singh conn->msg_out_len_max = p->msg_out_len_max;
1247709a63bSJasvinder Singh conn->msg_in_len = 0;
1257709a63bSJasvinder Singh conn->fd_server = fd_server;
1267709a63bSJasvinder Singh conn->fd_client_group = fd_client_group;
1277709a63bSJasvinder Singh conn->msg_handle = p->msg_handle;
1287709a63bSJasvinder Singh conn->msg_handle_arg = p->msg_handle_arg;
1297709a63bSJasvinder Singh
1307709a63bSJasvinder Singh return conn;
1317709a63bSJasvinder Singh }
1327709a63bSJasvinder Singh
1337709a63bSJasvinder Singh void
softnic_conn_free(struct softnic_conn * conn)1347709a63bSJasvinder Singh softnic_conn_free(struct softnic_conn *conn)
1357709a63bSJasvinder Singh {
1367709a63bSJasvinder Singh if (conn == NULL)
1377709a63bSJasvinder Singh return;
1387709a63bSJasvinder Singh
1397709a63bSJasvinder Singh if (conn->fd_client_group)
1407709a63bSJasvinder Singh close(conn->fd_client_group);
1417709a63bSJasvinder Singh
1427709a63bSJasvinder Singh if (conn->fd_server)
1437709a63bSJasvinder Singh close(conn->fd_server);
1447709a63bSJasvinder Singh
1457709a63bSJasvinder Singh free(conn->msg_out);
1467709a63bSJasvinder Singh free(conn->msg_in);
147*ae2b3ba6SDapeng Yu free(conn->buf);
1487709a63bSJasvinder Singh free(conn->prompt);
1497709a63bSJasvinder Singh free(conn->welcome);
1507709a63bSJasvinder Singh free(conn);
1517709a63bSJasvinder Singh }
1527709a63bSJasvinder Singh
1537709a63bSJasvinder Singh int
softnic_conn_poll_for_conn(struct softnic_conn * conn)1547709a63bSJasvinder Singh softnic_conn_poll_for_conn(struct softnic_conn *conn)
1557709a63bSJasvinder Singh {
1567709a63bSJasvinder Singh struct sockaddr_in client_address;
1577709a63bSJasvinder Singh struct epoll_event event;
1587709a63bSJasvinder Singh socklen_t client_address_length;
1597709a63bSJasvinder Singh int fd_client, status;
1607709a63bSJasvinder Singh
1617709a63bSJasvinder Singh /* Check input arguments */
1627709a63bSJasvinder Singh if (conn == NULL)
1637709a63bSJasvinder Singh return -1;
1647709a63bSJasvinder Singh
1657709a63bSJasvinder Singh /* Server socket */
1667709a63bSJasvinder Singh client_address_length = sizeof(client_address);
1677709a63bSJasvinder Singh fd_client = accept4(conn->fd_server,
1687709a63bSJasvinder Singh (struct sockaddr *)&client_address,
1697709a63bSJasvinder Singh &client_address_length,
1707709a63bSJasvinder Singh SOCK_NONBLOCK);
1717709a63bSJasvinder Singh if (fd_client == -1) {
1727709a63bSJasvinder Singh if (errno == EAGAIN || errno == EWOULDBLOCK)
1737709a63bSJasvinder Singh return 0;
1747709a63bSJasvinder Singh
1757709a63bSJasvinder Singh return -1;
1767709a63bSJasvinder Singh }
1777709a63bSJasvinder Singh
1787709a63bSJasvinder Singh /* Client group */
1797709a63bSJasvinder Singh event.events = EPOLLIN | EPOLLRDHUP | EPOLLHUP;
1807709a63bSJasvinder Singh event.data.fd = fd_client;
1817709a63bSJasvinder Singh
1827709a63bSJasvinder Singh status = epoll_ctl(conn->fd_client_group,
1837709a63bSJasvinder Singh EPOLL_CTL_ADD,
1847709a63bSJasvinder Singh fd_client,
1857709a63bSJasvinder Singh &event);
1867709a63bSJasvinder Singh if (status == -1) {
1877709a63bSJasvinder Singh close(fd_client);
1887709a63bSJasvinder Singh return -1;
1897709a63bSJasvinder Singh }
1907709a63bSJasvinder Singh
1917709a63bSJasvinder Singh /* Client */
1927709a63bSJasvinder Singh status = write(fd_client,
1937709a63bSJasvinder Singh conn->welcome,
1947709a63bSJasvinder Singh strlen(conn->welcome));
1957709a63bSJasvinder Singh if (status == -1) {
1967709a63bSJasvinder Singh close(fd_client);
1977709a63bSJasvinder Singh return -1;
1987709a63bSJasvinder Singh }
1997709a63bSJasvinder Singh
2007709a63bSJasvinder Singh status = write(fd_client,
2017709a63bSJasvinder Singh conn->prompt,
2027709a63bSJasvinder Singh strlen(conn->prompt));
2037709a63bSJasvinder Singh if (status == -1) {
2047709a63bSJasvinder Singh close(fd_client);
2057709a63bSJasvinder Singh return -1;
2067709a63bSJasvinder Singh }
2077709a63bSJasvinder Singh
2087709a63bSJasvinder Singh return 0;
2097709a63bSJasvinder Singh }
2107709a63bSJasvinder Singh
2117709a63bSJasvinder Singh static int
data_event_handle(struct softnic_conn * conn,int fd_client)2127709a63bSJasvinder Singh data_event_handle(struct softnic_conn *conn,
2137709a63bSJasvinder Singh int fd_client)
2147709a63bSJasvinder Singh {
2157709a63bSJasvinder Singh ssize_t len, i, status;
2167709a63bSJasvinder Singh
2177709a63bSJasvinder Singh /* Read input message */
2187709a63bSJasvinder Singh
2197709a63bSJasvinder Singh len = read(fd_client,
2207709a63bSJasvinder Singh conn->buf,
2217709a63bSJasvinder Singh conn->buf_size);
2227709a63bSJasvinder Singh if (len == -1) {
2237709a63bSJasvinder Singh if (errno == EAGAIN || errno == EWOULDBLOCK)
2247709a63bSJasvinder Singh return 0;
2257709a63bSJasvinder Singh
2267709a63bSJasvinder Singh return -1;
2277709a63bSJasvinder Singh }
2287709a63bSJasvinder Singh if (len == 0)
2297709a63bSJasvinder Singh return 0;
2307709a63bSJasvinder Singh
2317709a63bSJasvinder Singh /* Handle input messages */
2327709a63bSJasvinder Singh for (i = 0; i < len; i++) {
2337709a63bSJasvinder Singh if (conn->buf[i] == '\n') {
2347709a63bSJasvinder Singh size_t n;
2357709a63bSJasvinder Singh
2367709a63bSJasvinder Singh conn->msg_in[conn->msg_in_len] = 0;
2377709a63bSJasvinder Singh conn->msg_out[0] = 0;
2387709a63bSJasvinder Singh
2397709a63bSJasvinder Singh conn->msg_handle(conn->msg_in,
2407709a63bSJasvinder Singh conn->msg_out,
2417709a63bSJasvinder Singh conn->msg_out_len_max,
2427709a63bSJasvinder Singh conn->msg_handle_arg);
2437709a63bSJasvinder Singh
2447709a63bSJasvinder Singh n = strlen(conn->msg_out);
2457709a63bSJasvinder Singh if (n) {
2467709a63bSJasvinder Singh status = write(fd_client,
2477709a63bSJasvinder Singh conn->msg_out,
2487709a63bSJasvinder Singh n);
2497709a63bSJasvinder Singh if (status == -1)
2507709a63bSJasvinder Singh return status;
2517709a63bSJasvinder Singh }
2527709a63bSJasvinder Singh
2537709a63bSJasvinder Singh conn->msg_in_len = 0;
2547709a63bSJasvinder Singh } else if (conn->msg_in_len < conn->msg_in_len_max) {
2557709a63bSJasvinder Singh conn->msg_in[conn->msg_in_len] = conn->buf[i];
2567709a63bSJasvinder Singh conn->msg_in_len++;
2577709a63bSJasvinder Singh } else {
2587709a63bSJasvinder Singh status = write(fd_client,
2597709a63bSJasvinder Singh MSG_CMD_TOO_LONG,
2607709a63bSJasvinder Singh strlen(MSG_CMD_TOO_LONG));
2617709a63bSJasvinder Singh if (status == -1)
2627709a63bSJasvinder Singh return status;
2637709a63bSJasvinder Singh
2647709a63bSJasvinder Singh conn->msg_in_len = 0;
2657709a63bSJasvinder Singh }
2667709a63bSJasvinder Singh }
2677709a63bSJasvinder Singh
2687709a63bSJasvinder Singh /* Write prompt */
2697709a63bSJasvinder Singh status = write(fd_client,
2707709a63bSJasvinder Singh conn->prompt,
2717709a63bSJasvinder Singh strlen(conn->prompt));
2727709a63bSJasvinder Singh if (status == -1)
2737709a63bSJasvinder Singh return status;
2747709a63bSJasvinder Singh
2757709a63bSJasvinder Singh return 0;
2767709a63bSJasvinder Singh }
2777709a63bSJasvinder Singh
2787709a63bSJasvinder Singh static int
control_event_handle(struct softnic_conn * conn,int fd_client)2797709a63bSJasvinder Singh control_event_handle(struct softnic_conn *conn,
2807709a63bSJasvinder Singh int fd_client)
2817709a63bSJasvinder Singh {
2827709a63bSJasvinder Singh int status;
2837709a63bSJasvinder Singh
2847709a63bSJasvinder Singh status = epoll_ctl(conn->fd_client_group,
2857709a63bSJasvinder Singh EPOLL_CTL_DEL,
2867709a63bSJasvinder Singh fd_client,
2877709a63bSJasvinder Singh NULL);
2887709a63bSJasvinder Singh if (status == -1)
2897709a63bSJasvinder Singh return -1;
2907709a63bSJasvinder Singh
2917709a63bSJasvinder Singh status = close(fd_client);
2927709a63bSJasvinder Singh if (status == -1)
2937709a63bSJasvinder Singh return -1;
2947709a63bSJasvinder Singh
2957709a63bSJasvinder Singh return 0;
2967709a63bSJasvinder Singh }
2977709a63bSJasvinder Singh
2987709a63bSJasvinder Singh int
softnic_conn_poll_for_msg(struct softnic_conn * conn)2997709a63bSJasvinder Singh softnic_conn_poll_for_msg(struct softnic_conn *conn)
3007709a63bSJasvinder Singh {
3017709a63bSJasvinder Singh struct epoll_event event;
3027709a63bSJasvinder Singh int fd_client, status, status_data = 0, status_control = 0;
3037709a63bSJasvinder Singh
3047709a63bSJasvinder Singh /* Check input arguments */
3057709a63bSJasvinder Singh if (conn == NULL)
3067709a63bSJasvinder Singh return -1;
3077709a63bSJasvinder Singh
3087709a63bSJasvinder Singh /* Client group */
3097709a63bSJasvinder Singh status = epoll_wait(conn->fd_client_group,
3107709a63bSJasvinder Singh &event,
3117709a63bSJasvinder Singh 1,
3127709a63bSJasvinder Singh 0);
3137709a63bSJasvinder Singh if (status == -1)
3147709a63bSJasvinder Singh return -1;
3157709a63bSJasvinder Singh if (status == 0)
3167709a63bSJasvinder Singh return 0;
3177709a63bSJasvinder Singh
3187709a63bSJasvinder Singh fd_client = event.data.fd;
3197709a63bSJasvinder Singh
3207709a63bSJasvinder Singh /* Data available */
3217709a63bSJasvinder Singh if (event.events & EPOLLIN)
3227709a63bSJasvinder Singh status_data = data_event_handle(conn, fd_client);
3237709a63bSJasvinder Singh
3247709a63bSJasvinder Singh /* Control events */
3257709a63bSJasvinder Singh if (event.events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP))
3267709a63bSJasvinder Singh status_control = control_event_handle(conn, fd_client);
3277709a63bSJasvinder Singh
3287709a63bSJasvinder Singh if (status_data || status_control)
3297709a63bSJasvinder Singh return -1;
3307709a63bSJasvinder Singh
3317709a63bSJasvinder Singh return 0;
3327709a63bSJasvinder Singh }
333