308 lines
7.5 KiB
C
308 lines
7.5 KiB
C
|
/*
|
||
|
*------------------------------------------------------------------
|
||
|
* Copyright (c) 2020 Cisco and/or its affiliates.
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at:
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*------------------------------------------------------------------
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <inttypes.h>
|
||
|
#include <string.h>
|
||
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
#include <errno.h>
|
||
|
#include <unistd.h>
|
||
|
#include <getopt.h>
|
||
|
|
||
|
#include <libmemif.h>
|
||
|
#include <common.h>
|
||
|
|
||
|
#define APP_NAME "loopback_example"
|
||
|
#define IF0_NAME "lo0"
|
||
|
#define IF1_NAME "lo1"
|
||
|
|
||
|
memif_connection_t intf0, intf1;
|
||
|
int is_reverse;
|
||
|
int epfd;
|
||
|
|
||
|
int
|
||
|
packet_generator (memif_connection_t *c, uint16_t num_pkts)
|
||
|
{
|
||
|
int i, bi = 0;
|
||
|
memif_buffer_t *mb;
|
||
|
|
||
|
for (i = 0; (i < num_pkts) && (bi < c->tx_buf_num); i++)
|
||
|
{
|
||
|
mb = &c->tx_bufs[bi++];
|
||
|
memset (mb->data, 1, mb->len);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* informs user about connected status. private_ctx is used by user to identify
|
||
|
* connection */
|
||
|
int
|
||
|
on_connect (memif_conn_handle_t conn, void *private_ctx)
|
||
|
{
|
||
|
INFO ("memif connected!");
|
||
|
int err;
|
||
|
|
||
|
memif_connection_t *c = (memif_connection_t *) private_ctx;
|
||
|
|
||
|
c->is_connected = 1;
|
||
|
alloc_memif_buffers (c);
|
||
|
|
||
|
err = memif_refill_queue (conn, 0, -1, 0);
|
||
|
if (err != MEMIF_ERR_SUCCESS)
|
||
|
{
|
||
|
INFO ("memif_refill_queue: %s", memif_strerror (err));
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
print_memif_details (c);
|
||
|
|
||
|
/* Once both interfaces are connected send a test packet, master -> slave.
|
||
|
* Slave will use zero-copy method to reply the same pakcet back.
|
||
|
* (Configured by assigning responder_zero_copy as on_interrupt callback.)
|
||
|
*/
|
||
|
if ((intf0.is_connected == 1) && (intf1.is_connected == 1))
|
||
|
{
|
||
|
send_packets (is_reverse ? &intf1 : &intf0, 0, packet_generator, 1,
|
||
|
2048);
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* informs user about disconnected status. private_ctx is used by user to
|
||
|
* identify connection */
|
||
|
int
|
||
|
on_disconnect (memif_conn_handle_t conn, void *private_ctx)
|
||
|
{
|
||
|
INFO ("memif disconnected!");
|
||
|
|
||
|
memif_connection_t *c = (memif_connection_t *) private_ctx;
|
||
|
|
||
|
c->is_connected = 0;
|
||
|
free_memif_buffers (c);
|
||
|
|
||
|
/* stop event polling thread */
|
||
|
int err = memif_cancel_poll_event (memif_get_socket_handle (conn));
|
||
|
if (err != MEMIF_ERR_SUCCESS)
|
||
|
INFO ("We are doomed...");
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
verify_packet (memif_conn_handle_t conn, void *private_ctx, uint16_t qid)
|
||
|
{
|
||
|
memif_connection_t *c = (memif_connection_t *) private_ctx;
|
||
|
int err;
|
||
|
void *want;
|
||
|
|
||
|
err = memif_rx_burst (conn, qid, c->rx_bufs, MAX_MEMIF_BUFS, &c->rx_buf_num);
|
||
|
if (err != MEMIF_ERR_SUCCESS)
|
||
|
{
|
||
|
INFO ("meif_rx_burst: %s", memif_strerror (err));
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
want = malloc (c->rx_bufs[0].len);
|
||
|
if (want == NULL)
|
||
|
{
|
||
|
INFO ("Out of memory");
|
||
|
goto done;
|
||
|
}
|
||
|
|
||
|
memset (want, 1, c->rx_bufs[0].len);
|
||
|
|
||
|
err = memcmp (c->rx_bufs[0].data, want, c->rx_bufs[0].len);
|
||
|
if (err != 0)
|
||
|
{
|
||
|
INFO ("Received malformed data. ret: %d", err);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
INFO ("Received correct data.");
|
||
|
}
|
||
|
|
||
|
done:
|
||
|
err = memif_refill_queue (conn, qid, c->rx_buf_num, 0);
|
||
|
if (err != MEMIF_ERR_SUCCESS)
|
||
|
INFO ("memif_refill_queue: %s", memif_strerror (err));
|
||
|
|
||
|
/* stop polling and exit the program */
|
||
|
INFO ("Stopping the program");
|
||
|
err = memif_cancel_poll_event (memif_get_socket_handle (conn));
|
||
|
if (err != MEMIF_ERR_SUCCESS)
|
||
|
INFO ("We are doomed...");
|
||
|
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
int
|
||
|
create_memif_interface (memif_socket_handle_t memif_socket,
|
||
|
const char *if_name, int id, uint8_t is_master,
|
||
|
memif_connection_t *ctx)
|
||
|
{
|
||
|
memif_conn_args_t memif_conn_args = { 0 };
|
||
|
int err;
|
||
|
|
||
|
memif_conn_args.socket = memif_socket;
|
||
|
memif_conn_args.interface_id = id;
|
||
|
strncpy (memif_conn_args.interface_name, if_name,
|
||
|
sizeof (memif_conn_args.interface_name));
|
||
|
memif_conn_args.is_master = is_master;
|
||
|
|
||
|
err = memif_create (&ctx->conn, &memif_conn_args, on_connect, on_disconnect,
|
||
|
is_master ? verify_packet : responder_zero_copy,
|
||
|
(void *) ctx);
|
||
|
if (err != MEMIF_ERR_SUCCESS)
|
||
|
{
|
||
|
INFO ("memif_create_socket: %s", memif_strerror (err));
|
||
|
return err;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
print_help ()
|
||
|
{
|
||
|
printf ("LIBMEMIF EXAMPLE APP: %s", APP_NAME);
|
||
|
#ifdef ICMP_DBG
|
||
|
printf (" (debug)");
|
||
|
#endif
|
||
|
printf ("\n");
|
||
|
printf ("==============================\n");
|
||
|
printf ("libmemif version: %s", LIBMEMIF_VERSION);
|
||
|
#ifdef MEMIF_DBG
|
||
|
printf (" (debug)");
|
||
|
#endif
|
||
|
printf ("\n");
|
||
|
|
||
|
printf ("memif version: %s\n", memif_get_version_str ());
|
||
|
printf ("==============================\n");
|
||
|
printf ("In this example, two memif endpoints are connected to create a "
|
||
|
"loopback.\n");
|
||
|
printf ("Once connected, a test packet is sent out the memif master "
|
||
|
"interface to\n");
|
||
|
printf (
|
||
|
"the memif slave interface, which replies with the same packet in a\n");
|
||
|
printf ("zero-copy way.\n");
|
||
|
printf (
|
||
|
"In reverse mode, the packet is sent from the slave interface and is\n");
|
||
|
printf ("looped back by the master interface.\n");
|
||
|
printf ("==============================\n");
|
||
|
printf ("Usage: loopback [OPTIONS]\n\n");
|
||
|
printf ("Options:\n");
|
||
|
printf ("\t-r\tReverse mode, verification packet is sent by slave.\n");
|
||
|
printf ("\t-?\tShow help and exit.\n");
|
||
|
printf ("\t-v\tShow libmemif and memif version information and exit.\n");
|
||
|
}
|
||
|
|
||
|
int
|
||
|
main (int argc, char *argv[])
|
||
|
{
|
||
|
memif_socket_args_t memif_socket_args = { 0 };
|
||
|
memif_socket_handle_t memif_socket;
|
||
|
int opt, err, ret = 0;
|
||
|
is_reverse = 0;
|
||
|
|
||
|
while ((opt = getopt (argc, argv, "r?v")) != -1)
|
||
|
{
|
||
|
switch (opt)
|
||
|
{
|
||
|
case 'r':
|
||
|
is_reverse = 1;
|
||
|
break;
|
||
|
case '?':
|
||
|
print_help ();
|
||
|
return 0;
|
||
|
case 'v':
|
||
|
print_version ();
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/** Create memif socket
|
||
|
*
|
||
|
* Interfaces are internally stored in a database referenced by memif socket.
|
||
|
*/
|
||
|
/* Abstract socket supported */
|
||
|
memif_socket_args.path[0] = '@';
|
||
|
strncpy (memif_socket_args.path + 1, APP_NAME, strlen (APP_NAME));
|
||
|
/* Set application name */
|
||
|
strncpy (memif_socket_args.app_name, APP_NAME, strlen (APP_NAME));
|
||
|
|
||
|
err = memif_create_socket (&memif_socket, &memif_socket_args, NULL);
|
||
|
if (err != MEMIF_ERR_SUCCESS)
|
||
|
{
|
||
|
INFO ("memif_create_socket: %s", memif_strerror (err));
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
/** Create memif interfaces
|
||
|
*
|
||
|
* Both interaces are assigned the same socket and same id to create a
|
||
|
* loopback.
|
||
|
*/
|
||
|
|
||
|
/* prepare the private data */
|
||
|
memset (&intf0, 0, sizeof (intf0));
|
||
|
memset (&intf1, 0, sizeof (intf1));
|
||
|
if (is_reverse)
|
||
|
{
|
||
|
intf0.packet_handler = basic_packet_handler;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
intf1.packet_handler = basic_packet_handler;
|
||
|
}
|
||
|
|
||
|
err =
|
||
|
create_memif_interface (memif_socket, IF0_NAME, 0, /* master */ 1, &intf0);
|
||
|
if (err != 0)
|
||
|
{
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
err =
|
||
|
create_memif_interface (memif_socket, IF1_NAME, 0, /* slave */ 0, &intf1);
|
||
|
if (err != 0)
|
||
|
{
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
do
|
||
|
{
|
||
|
err = memif_poll_event (memif_socket, -1);
|
||
|
}
|
||
|
while (err == MEMIF_ERR_SUCCESS);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error:
|
||
|
ret = -1;
|
||
|
done:
|
||
|
free_memif_buffers (&intf0);
|
||
|
free_memif_buffers (&intf1);
|
||
|
memif_delete (&intf0.conn);
|
||
|
memif_delete (&intf1.conn);
|
||
|
memif_delete_socket (&memif_socket);
|
||
|
return ret;
|
||
|
}
|