aboutsummaryrefslogtreecommitdiffstats
path: root/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd
diff options
context:
space:
mode:
authorSridhar K. N. Rao <sridhar.rao@spirent.com>2021-11-12 16:00:03 +0530
committerSridhar K. N. Rao <sridhar.rao@spirent.com>2021-11-12 16:01:30 +0530
commit24e68cf441d94ba09a13f21fdfc994d4a14dfde0 (patch)
tree127b699abb0bd1b17bce9f138f3b2648ee7d0ecf /tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd
parent4fa105c5b4113a9ff4311569709ca99a8fbf5028 (diff)
Docker: Forwarding Pods.
This patch adds source file required to build 2 forwarding pods. 1. L2 and L3 Fowarding 2. VPP Signed-off-by: Sridhar K. N. Rao <sridhar.rao@spirent.com> Change-Id: Ibffea4ebe34a575d040778e45b6ba9e92af5e8b6
Diffstat (limited to 'tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd')
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/Dockerfile87
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.c302
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.h7
-rwxr-xr-xtools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/docker-entrypoint.sh18
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.c446
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.h16
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_eal_init.txt39
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_parse_args.txt9
-rwxr-xr-xtools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_substitute.sh59
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_eal_init.txt39
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_parse_args.txt9
-rwxr-xr-xtools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_substitute.sh67
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_eal_init.txt28
-rw-r--r--tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_launch_args_parse.txt19
-rwxr-xr-xtools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_substitute.sh61
-rwxr-xr-xtools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/vhost_substitute.sh23
16 files changed, 1229 insertions, 0 deletions
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/Dockerfile b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/Dockerfile
new file mode 100644
index 00000000..c56e45cf
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/Dockerfile
@@ -0,0 +1,87 @@
+FROM centos:7
+
+#
+# Install required packages
+#
+RUN rpm --import https://mirror.go-repo.io/centos/RPM-GPG-KEY-GO-REPO && curl -s https://mirror.go-repo.io/centos/go-repo.repo | tee /etc/yum.repos.d/go-repo.repo
+RUN yum groupinstall -y "Development Tools"
+RUN yum install -y wget numactl-devel git golang make; yum clean all
+
+#
+# Download and Build APP-NetUtil
+#
+WORKDIR /root/go/src/
+RUN mkdir github.com && cd github.com && mkdir openshift && cd openshift && git clone https://github.com/openshift/app-netutil
+WORKDIR /root/go/src/github.com/openshift/app-netutil
+RUN make c_sample
+RUN cp bin/libnetutil_api.so /lib64/libnetutil_api.so; cp bin/libnetutil_api.h /usr/include/libnetutil_api.h
+
+#
+# Download and Build DPDK
+#
+ENV DPDK_VER 20.05
+ENV DPDK_DIR /usr/src/dpdk-${DPDK_VER}
+WORKDIR /usr/src/
+RUN wget http://fast.dpdk.org/rel/dpdk-${DPDK_VER}.tar.xz
+RUN tar -xpvf dpdk-${DPDK_VER}.tar.xz
+
+ENV RTE_TARGET=x86_64-native-linuxapp-gcc
+ENV RTE_SDK=${DPDK_DIR}
+WORKDIR ${DPDK_DIR}
+RUN sed -i -e 's/EAL_IGB_UIO=y/EAL_IGB_UIO=n/' config/common_linux
+RUN sed -i -e 's/KNI_KMOD=y/KNI_KMOD=n/' config/common_linux
+RUN sed -i -e 's/LIBRTE_KNI=y/LIBRTE_KNI=n/' config/common_linux
+RUN sed -i -e 's/LIBRTE_PMD_KNI=y/LIBRTE_PMD_KNI=n/' config/common_linux
+
+# Add vhost patch
+COPY ./vhost_substitute.sh ./vhost_substitute.sh
+RUN ./vhost_substitute.sh
+
+RUN make install T=${RTE_TARGET} DESTDIR=${RTE_SDK}
+
+#
+# Build l2fwd
+#
+WORKDIR ${DPDK_DIR}/examples/l2fwd
+COPY ./dpdk-args.c ./dpdk-args.c
+COPY ./dpdk-args.h ./dpdk-args.h
+COPY ./c_util.c ./c_util.c
+COPY ./c_util.h ./c_util.h
+COPY ./l2fwd_eal_init.txt ./l2fwd_eal_init.txt
+COPY ./l2fwd_parse_args.txt ./l2fwd_parse_args.txt
+COPY ./l2fwd_substitute.sh ./l2fwd_substitute.sh
+RUN ./l2fwd_substitute.sh
+RUN make
+RUN cp build/l2fwd /usr/bin/l2fwd
+
+#
+# Build l3fwd
+#
+WORKDIR ${DPDK_DIR}/examples/l3fwd
+COPY ./dpdk-args.c ./dpdk-args.c
+COPY ./dpdk-args.h ./dpdk-args.h
+COPY ./c_util.c ./c_util.c
+COPY ./c_util.h ./c_util.h
+COPY ./l3fwd_eal_init.txt ./l3fwd_eal_init.txt
+COPY ./l3fwd_parse_args.txt ./l3fwd_parse_args.txt
+COPY ./l3fwd_substitute.sh ./l3fwd_substitute.sh
+RUN ./l3fwd_substitute.sh
+RUN make
+RUN cp build/l3fwd /usr/bin/l3fwd
+
+# Copy default APP
+RUN cp /usr/bin/l2fwd /usr/bin/dpdk-app
+
+# -------- Import stage.
+FROM centos
+COPY --from=0 /usr/bin/dpdk-app /usr/bin/dpdk-app
+COPY --from=0 /usr/bin/l2fwd /usr/bin/l2fwd
+COPY --from=0 /usr/bin/l3fwd /usr/bin/l3fwd
+COPY --from=0 /usr/bin/testpmd /usr/bin/testpmd
+COPY --from=0 /lib64/libnetutil_api.so /lib64/libnetutil_api.so
+COPY --from=0 /usr/lib64/libnuma.so.1 /usr/lib64/libnuma.so.1
+COPY --from=0 /root/go/src/github.com/openshift/app-netutil/bin/c_sample /usr/bin/c_sample
+
+RUN yum install -y pciutils iproute ethtool lshw; yum clean all
+# END
+
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.c b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.c
new file mode 100644
index 00000000..73f46a9e
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright(c) 2021 Red Hat, Inc.
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "libnetutil_api.h"
+#include "c_util.h"
+
+
+extern void dumpInterfaces(struct InterfaceResponse *pIfaceRsp) {
+ int i, j;
+ bool printReturn;
+
+ if ((pIfaceRsp) && (pIfaceRsp->pIface)) {
+ for (i = 0; i < pIfaceRsp->numIfacePopulated; i++) {
+ printf(" Interface[%d]:\n", i);
+
+ printf(" ");
+ printf(" DeviceType=%s",
+ (pIfaceRsp->pIface[i].DeviceType == NETUTIL_TYPE_HOST) ? "host" :
+ (pIfaceRsp->pIface[i].DeviceType == NETUTIL_TYPE_SRIOV) ? "SR-IOV" :
+ (pIfaceRsp->pIface[i].DeviceType == NETUTIL_TYPE_PCI) ? "PCI" :
+ (pIfaceRsp->pIface[i].DeviceType == NETUTIL_TYPE_VHOST) ? "vHost" :
+ (pIfaceRsp->pIface[i].DeviceType == NETUTIL_TYPE_MEMIF) ? "memif" :
+ (pIfaceRsp->pIface[i].DeviceType == NETUTIL_TYPE_VDPA) ? "vDPA" :
+ (pIfaceRsp->pIface[i].DeviceType == NETUTIL_TYPE_UNKNOWN) ? "unknown" : "error");
+
+ if (pIfaceRsp->pIface[i].NetworkStatus.Name) {
+ printf(" Name=\"%s\"", pIfaceRsp->pIface[i].NetworkStatus.Name);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.Interface) {
+ printf(" Interface=\"%s\"", pIfaceRsp->pIface[i].NetworkStatus.Interface);
+ }
+ printf("\n");
+
+ printReturn = false;
+ if (pIfaceRsp->pIface[i].NetworkStatus.Mac) {
+ if (printReturn == false) {
+ printReturn = true;
+ printf(" ");
+ }
+ printf(" MAC=\"%s\"", pIfaceRsp->pIface[i].NetworkStatus.Mac);
+ }
+ for (j = 0; j < NETUTIL_NUM_IPS; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.IPs[j]) {
+ if (printReturn == false) {
+ printReturn = true;
+ printf(" DNS Nameservers: ");
+ }
+ printf(" IP=\"%s\"", pIfaceRsp->pIface[i].NetworkStatus.IPs[j]);
+ }
+ }
+ if (printReturn) {
+ printf("\n");
+ }
+
+ printReturn = false;
+ for (j = 0; j < NETUTIL_NUM_DNS_NAMESERVERS; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Nameservers[j]) {
+ if (printReturn == false) {
+ printReturn = true;
+ printf(" DNS Nameservers: ");
+ }
+ printf(" \"%s\"", pIfaceRsp->pIface[i].NetworkStatus.DNS.Nameservers[j]);
+ }
+ }
+ if (printReturn) {
+ printf("\n");
+ }
+
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Domain) {
+ printf(" DNS Domain: \"%s\"\n", pIfaceRsp->pIface[i].NetworkStatus.DNS.Domain);
+ }
+
+ printReturn = false;
+ for (j = 0; j < NETUTIL_NUM_DNS_SEARCH; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Search[j]) {
+ if (printReturn == false) {
+ printReturn = true;
+ printf(" DNS Search: ");
+ }
+ printf(" \"%s\"", pIfaceRsp->pIface[i].NetworkStatus.DNS.Search[j]);
+ }
+ }
+ if (printReturn) {
+ printf("\n");
+ }
+
+ printReturn = false;
+ for (j = 0; j < NETUTIL_NUM_DNS_OPTIONS; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Options[j]) {
+ if (printReturn == false) {
+ printReturn = true;
+ printf(" DNS Options: ");
+ }
+ printf(" \"%s\"", pIfaceRsp->pIface[i].NetworkStatus.DNS.Options[j]);
+ }
+ }
+ if (printReturn) {
+ printf("\n");
+ }
+
+ switch (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Type) {
+ case NETUTIL_TYPE_PCI:
+ printf(" Type=PCI");
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PciAddress) {
+ printf(" PCIAddress=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PciAddress);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.Vhostnet) {
+ printf(" Vhostnet=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.Vhostnet);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.RdmaDevice) {
+ printf(" RdmaDevice=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.RdmaDevice);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PfPciAddress) {
+ printf(" PF-PCIAddress=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PfPciAddress);
+ }
+ printf("\n");
+ break;
+ case NETUTIL_TYPE_VHOST:
+ printf(" Type=vHOST");
+ printf(" Mode=%s",
+ (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.VhostUser.Mode == NETUTIL_VHOST_MODE_CLIENT) ? "client" :
+ (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.VhostUser.Mode == NETUTIL_VHOST_MODE_SERVER) ? "server" : "error");
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.VhostUser.Path) {
+ printf(" Path=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.VhostUser.Path);
+ }
+ printf("\n");
+ break;
+ case NETUTIL_TYPE_MEMIF:
+ printf(" Type=Memif");
+ printf(" Role=%s",
+ (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Role == NETUTIL_MEMIF_ROLE_MASTER) ? "master" :
+ (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Role == NETUTIL_MEMIF_ROLE_SLAVE) ? "slave" : "error");
+ printf(" Mode=%s",
+ (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Mode == NETUTIL_MEMIF_MODE_ETHERNET) ? "ethernet" :
+ (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Mode == NETUTIL_MEMIF_MODE_IP) ? "ip" :
+ (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Mode == NETUTIL_MEMIF_MODE_INJECT_PUNT) ? "inject-punt" : "error");
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Path) {
+ printf(" Path=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Path);
+ }
+ printf("\n");
+ break;
+ case NETUTIL_TYPE_VDPA:
+ printf(" Type=vDPA");
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.ParentDevice) {
+ printf(" ParentDevice=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.ParentDevice);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Driver) {
+ printf(" Driver=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Driver);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Path) {
+ printf(" Path=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Path);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PciAddress) {
+ printf(" PCIAddress=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PciAddress);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PfPciAddress) {
+ printf(" PF-PCIAddress=%s", pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PfPciAddress);
+ }
+ printf("\n");
+ break;
+ }
+ }
+ }
+}
+
+extern void freeInterfaces(struct InterfaceResponse *pIfaceRsp) {
+ int i, j;
+
+ if ((pIfaceRsp) && (pIfaceRsp->pIface)) {
+ for (i = 0; i < pIfaceRsp->numIfacePopulated; i++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.Name) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.Name);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.Interface) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.Interface);
+ }
+
+ if (pIfaceRsp->pIface[i].NetworkStatus.Mac) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.Mac);
+ }
+ for (j = 0; j < NETUTIL_NUM_IPS; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.IPs[j]) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.IPs[j]);
+ }
+ }
+
+ for (j = 0; j < NETUTIL_NUM_DNS_NAMESERVERS; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Nameservers[j]) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DNS.Nameservers[j]);
+ }
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Domain) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DNS.Domain);
+ }
+ for (j = 0; j < NETUTIL_NUM_DNS_SEARCH; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Search[j]) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DNS.Search[j]);
+ }
+ }
+ for (j = 0; j < NETUTIL_NUM_DNS_OPTIONS; j++) {
+ if (pIfaceRsp->pIface[i].NetworkStatus.DNS.Options[j]) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DNS.Options[j]);
+ }
+ }
+
+ switch (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Type) {
+ case NETUTIL_TYPE_PCI:
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PciAddress) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PciAddress);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.Vhostnet) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.Vhostnet);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.RdmaDevice) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.RdmaDevice);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PfPciAddress) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Pci.PfPciAddress);
+ }
+ break;
+ case NETUTIL_TYPE_VHOST:
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.VhostUser.Path) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.VhostUser.Path);
+ }
+ break;
+ case NETUTIL_TYPE_MEMIF:
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Path) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Memif.Path);
+ }
+ break;
+ case NETUTIL_TYPE_VDPA:
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.ParentDevice) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.ParentDevice);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Driver) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Driver);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Path) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.Path);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PciAddress) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PciAddress);
+ }
+ if (pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PfPciAddress) {
+ free(pIfaceRsp->pIface[i].NetworkStatus.DeviceInfo.Vdpa.PfPciAddress);
+ }
+ break;
+ }
+ }
+
+ free(pIfaceRsp->pIface);
+ }
+}
+
+extern void dumpHugepages(struct HugepagesResponse *pHugepagesRsp) {
+ int i;
+
+ if (pHugepagesRsp) {
+ if (pHugepagesRsp->MyContainerName) {
+ printf(" MyContainerName=%s\n", pHugepagesRsp->MyContainerName);
+ }
+ if (pHugepagesRsp->pHugepages) {
+ for (i = 0; i < pHugepagesRsp->numStructPopulated; i++) {
+ printf(" Hugepages[%d]:\n", i);
+
+ printf(" ");
+ if (pHugepagesRsp->pHugepages[i].ContainerName) {
+ printf(" ContainerName=%s", pHugepagesRsp->pHugepages[i].ContainerName);
+ }
+ printf(" Request: 1G=%ld 2M=%ld Ukn=%ld Limit: 1G=%ld 2M=%ld Ukn=%ld\n",
+ pHugepagesRsp->pHugepages[i].Request1G,
+ pHugepagesRsp->pHugepages[i].Request2M,
+ pHugepagesRsp->pHugepages[i].Request,
+ pHugepagesRsp->pHugepages[i].Limit1G,
+ pHugepagesRsp->pHugepages[i].Limit2M,
+ pHugepagesRsp->pHugepages[i].Limit);
+ }
+ }
+ }
+}
+
+extern void freeHugepages(struct HugepagesResponse *pHugepagesRsp) {
+ int i;
+
+ if (pHugepagesRsp) {
+ if (pHugepagesRsp->MyContainerName) {
+ free(pHugepagesRsp->MyContainerName);
+ }
+ if (pHugepagesRsp->pHugepages) {
+ for (i = 0; i < pHugepagesRsp->numStructPopulated; i++) {
+ if (pHugepagesRsp->pHugepages[i].ContainerName) {
+ free(pHugepagesRsp->pHugepages[i].ContainerName);
+ }
+ }
+ free(pHugepagesRsp->pHugepages);
+ }
+ }
+}
+
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.h b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.h
new file mode 100644
index 00000000..5ce6dfa1
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/c_util.h
@@ -0,0 +1,7 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright(c) 2021 Red Hat, Inc.
+
+extern void dumpInterfaces(struct InterfaceResponse *pIfaceRsp);
+extern void freeInterfaces(struct InterfaceResponse *pIfaceRsp);
+extern void dumpHugepages(struct HugepagesResponse *pHugepagesRsp);
+extern void freeHugepages(struct HugepagesResponse *pHugepagesRsp);
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/docker-entrypoint.sh b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/docker-entrypoint.sh
new file mode 100755
index 00000000..bd3b0284
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/docker-entrypoint.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+set -e
+
+echo "DPDK_SAMPLE_APP is set as: $DPDK_SAMPLE_APP"
+
+if [[ $DPDK_SAMPLE_APP == l2fwd ]] ; then
+ echo "Calling: l2fwd"
+ exec "l2fwd"
+elif [[ $DPDK_SAMPLE_APP == l3fwd ]] ; then
+ echo "Calling: l3fwd"
+ exec "l3fwd"
+elif [[ $DPDK_SAMPLE_APP == testpmd ]] ; then
+ echo "Calling: testpmd"
+ exec "testpmd"
+else
+ echo "Net set so using default - Calling: $@"
+ exec "$@"
+fi
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.c b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.c
new file mode 100644
index 00000000..7803a971
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.c
@@ -0,0 +1,446 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright(c) 2021 Red Hat, Inc.
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "libnetutil_api.h"
+#include "dpdk-args.h"
+#include "c_util.h"
+
+bool debugArgs = true;
+
+#define DPDK_ARGS_MAX_ARGS (30)
+#define DPDK_ARGS_MAX_ARG_STRLEN (100)
+char myArgsArray[DPDK_ARGS_MAX_ARGS][DPDK_ARGS_MAX_ARG_STRLEN];
+char* myArgv[DPDK_ARGS_MAX_ARGS];
+
+//#define DPDK_ARGS_MAX_NUM_DIR (30)
+//static const char DEFAULT_DIR[] = "/var/lib/cni/";
+
+static char STR_MASTER[] = "master";
+static char STR_SLAVE[] = "slave";
+static char STR_ETHERNET[] = "ethernet";
+
+/* Large enough to hold: ",mac=aa:bb:cc:dd:ee:ff" */
+#define DPDK_ARGS_MAX_MAC_STRLEN (25)
+#define DPDK_ARGS_MAX_CONTAINERNAME_STRLEN (80)
+
+
+static int getInterfaces(int argc, int *pPortCnt, int *pPortMask) {
+ int i = 0;
+ int vhostCnt = 0;
+ int memifCnt = 1;
+ int sriovCnt = 0;
+ int err;
+ struct InterfaceResponse ifaceRsp;
+ char macStr[DPDK_ARGS_MAX_MAC_STRLEN];
+#if 0
+ // Refactor code later to find JSON file. Needs work so commented out for now.
+ DIR *d;
+ struct dirent *dir;
+ int currIndex = 0;
+ int freeIndex = 0;
+ char* dirList[DPDK_ARGS_MAX_NUM_DIR];
+ char* fileExt;
+
+ memset(dirList, 0, sizeof(char*)*DPDK_ARGS_MAX_NUM_DIR);
+
+ dirList[freeIndex] = malloc(sizeof(char) * (strlen(DEFAULT_DIR)+1));
+ strcpy(dirList[freeIndex++], DEFAULT_DIR);
+
+ while (dirList[currIndex] != NULL) {
+ printf(" Directory:%s\n", dirList[currIndex]);
+ d = opendir(dirList[currIndex]);
+ if (d)
+ {
+ while ((dir = readdir(d)) != NULL)
+ {
+ if ((dir->d_name) &&
+ (strcmp(dir->d_name, ".") != 0) &&
+ (strcmp(dir->d_name, "..") != 0))
+ {
+ printf(" Name:%s %d\n", dir->d_name, dir->d_type);
+ if (dir->d_type == DT_DIR) {
+ printf(" Add to Dir List:%s\n", dir->d_name);
+ dirList[freeIndex] = malloc(sizeof(char) * (strlen(DEFAULT_DIR)+strlen(dir->d_name)+1));
+ sprintf(dirList[freeIndex++], "%s%s/", DEFAULT_DIR, dir->d_name);
+ }
+ else
+ {
+ if (strstr(dir->d_name, "net") != NULL)
+ {
+ fileExt = strrchr(dir->d_name, '.');
+ if ((fileExt == NULL) || (strcmp(fileExt, ".json") != 0)) {
+ printf(" Adding to vdev list:%s\n", dir->d_name);
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "--vdev=virtio_user%d,path=%s%s", i, dirList[currIndex], dir->d_name);
+ i++;
+ }
+ else {
+ printf(" Invalid FileExt\n");
+ }
+ }
+ }
+ }
+ }
+ closedir(d);
+ }
+ free(dirList[currIndex]);
+ currIndex++;
+ }
+#endif
+
+ ifaceRsp.numIfaceAllocated = NETUTIL_NUM_NETWORKINTERFACE;
+ ifaceRsp.numIfacePopulated = 0;
+ ifaceRsp.pIface = malloc(ifaceRsp.numIfaceAllocated * sizeof(struct InterfaceData));
+ if (ifaceRsp.pIface) {
+ memset(ifaceRsp.pIface, 0, (ifaceRsp.numIfaceAllocated * sizeof(struct InterfaceData)));
+ err = GetInterfaces(&ifaceRsp);
+ if ((err == NETUTIL_ERRNO_SUCCESS) || (err == NETUTIL_ERRNO_SIZE_ERROR)) {
+
+ if (debugArgs) {
+ dumpInterfaces(&ifaceRsp);
+ }
+
+ for (i = 0; i < ifaceRsp.numIfacePopulated; i++) {
+ switch (ifaceRsp.pIface[i].DeviceType) {
+ case NETUTIL_TYPE_SRIOV:
+ if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Pci.PciAddress) {
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "-w %s", ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Pci.PciAddress);
+ sriovCnt++;
+
+ *pPortMask = *pPortMask | 1 << *pPortCnt;
+ *pPortCnt = *pPortCnt + 1;
+ } else {
+ printf("ERROR: PCI Address not found. Type=%d\n",
+ ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Type);
+ }
+ break;
+ case NETUTIL_TYPE_VHOST:
+ if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.VhostUser.Path) {
+ if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.VhostUser.Mode == NETUTIL_VHOST_MODE_SERVER) {
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "--vdev=virtio_user%d,path=%s,server=1",
+ vhostCnt, ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.VhostUser.Path);
+
+ vhostCnt++;
+ *pPortMask = *pPortMask | 1 << *pPortCnt;
+ *pPortCnt = *pPortCnt + 1;
+ }
+ else if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.VhostUser.Mode == NETUTIL_VHOST_MODE_CLIENT) {
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "--vdev=virtio_user%d,path=%s,queues=1",
+ vhostCnt, ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.VhostUser.Path);
+
+ vhostCnt++;
+ *pPortMask = *pPortMask | 1 << *pPortCnt;
+ *pPortCnt = *pPortCnt + 1;
+ } else {
+ printf("ERROR: Unknown vHost Mode=%d\n", ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.VhostUser.Mode);
+ }
+ } else {
+ printf("ERROR: vHost Path not found. Type=%d\n",
+ ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Type);
+ }
+ break;
+ case NETUTIL_TYPE_MEMIF:
+ if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Path) {
+ char *pRole = NULL;
+ char *pMode = NULL;
+
+ if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Role == NETUTIL_MEMIF_ROLE_MASTER) {
+ pRole = STR_MASTER;
+ }
+ else if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Role == NETUTIL_MEMIF_ROLE_SLAVE) {
+ pRole = STR_SLAVE;
+ }
+ else {
+ printf("ERROR: Unknown memif Role=%d\n", ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Role);
+ }
+
+ if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Mode == NETUTIL_MEMIF_MODE_ETHERNET) {
+ pMode = STR_ETHERNET;
+ }
+ else if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Mode == NETUTIL_MEMIF_MODE_IP) {
+ //pMode = "ip";
+ printf("ERROR: memif Mode=%d - Not Supported in DPDK!\n",
+ ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Mode);
+ }
+ else if (ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Mode == NETUTIL_MEMIF_MODE_INJECT_PUNT) {
+ //pMode = "inject-punt"";
+ printf("ERROR: memif Mode=%d - Not Supported in DPDK!\n",
+ ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Mode);
+ }
+ else {
+ printf("ERROR: Unknown memif Mode=%d\n",
+ ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Mode);
+ }
+
+ if ((ifaceRsp.pIface[i].NetworkStatus.Mac) &&
+ (strcmp(ifaceRsp.pIface[i].NetworkStatus.Mac,"") != 0)) {
+ snprintf(&macStr[0], DPDK_ARGS_MAX_MAC_STRLEN-1,
+ ",mac=%s", ifaceRsp.pIface[i].NetworkStatus.Mac);
+ }
+ else {
+ macStr[0] = '\0';
+ }
+
+ if ((pRole) && (pMode)) {
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "--vdev=net_memif%d,socket=%s,role=%s%s", memifCnt,
+ ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Memif.Path, pRole, &macStr[0]);
+
+ memifCnt++;
+ *pPortMask = *pPortMask | 1 << *pPortCnt;
+ *pPortCnt = *pPortCnt + 1;
+ }
+ } else {
+ printf("ERROR: Memif Path not found. Type=%d\n",
+ ifaceRsp.pIface[i].NetworkStatus.DeviceInfo.Type);
+ }
+ break;
+ }
+ } /* END of FOR EACH Interface */
+
+ if (sriovCnt == 0) {
+ strncpy(&myArgsArray[argc++][0], "--no-pci", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ }
+
+ freeInterfaces(&ifaceRsp);
+ }
+ else {
+ printf("Couldn't get network interface, err code: %d\n", err);
+ }
+ }
+
+ return(argc);
+}
+
+static int getHugepages(int argc) {
+ int i = 0;
+ int err;
+ int containerIndex = 0;
+ int64_t reqMemory = 0;
+ int64_t hugepageMemory = 1024;
+ struct HugepagesResponse hugepagesRsp;
+
+ memset(&hugepagesRsp, 0, sizeof(struct HugepagesResponse));
+ hugepagesRsp.numStructAllocated = NETUTIL_NUM_HUGEPAGES_DATA;
+ hugepagesRsp.pHugepages = malloc(hugepagesRsp.numStructAllocated * sizeof(struct HugepagesData));
+ if (hugepagesRsp.pHugepages) {
+ memset(hugepagesRsp.pHugepages, 0, (hugepagesRsp.numStructAllocated * sizeof(struct HugepagesData)));
+ err = GetHugepages(&hugepagesRsp);
+ if ((err == NETUTIL_ERRNO_SUCCESS) || (err == NETUTIL_ERRNO_SIZE_ERROR)) {
+
+ if (debugArgs) {
+ dumpHugepages(&hugepagesRsp);
+ }
+
+ /* Loop through the list of containers to match container name from env. */
+ if (hugepagesRsp.MyContainerName) {
+ for (i = 0; i < hugepagesRsp.numStructPopulated; i++) {
+ if (hugepagesRsp.pHugepages[i].ContainerName) {
+ if (strcmp(hugepagesRsp.MyContainerName, hugepagesRsp.pHugepages[i].ContainerName) == 0) {
+ containerIndex = i;
+ printf(" MATCH: ContainerName=%s, Index=%d\n", hugepagesRsp.pHugepages[i].ContainerName, containerIndex);
+ break;
+ }
+ }
+ }
+ }
+
+ /* Limit can never be less than Request. So use Limit if non-zero. */
+ /* However, for hugepages, Limit and Request should be the same, so */
+ /* either value should be fine. */
+ reqMemory =
+ (hugepagesRsp.pHugepages[containerIndex].Limit1G != 0) ? hugepagesRsp.pHugepages[containerIndex].Limit1G :
+ (hugepagesRsp.pHugepages[containerIndex].Limit2M != 0) ? hugepagesRsp.pHugepages[containerIndex].Limit2M :
+ (hugepagesRsp.pHugepages[containerIndex].Limit != 0) ? hugepagesRsp.pHugepages[containerIndex].Limit :
+ (hugepagesRsp.pHugepages[containerIndex].Request1G != 0) ? hugepagesRsp.pHugepages[containerIndex].Request1G :
+ (hugepagesRsp.pHugepages[containerIndex].Request2M != 0) ? hugepagesRsp.pHugepages[containerIndex].Request2M :
+ hugepagesRsp.pHugepages[containerIndex].Request;
+
+ if (reqMemory != 0) {
+ /* Assuming 2 NUMA sockets, only use what container has access too. */
+ /* TBD: Manage NUMA properly. */
+ hugepageMemory = reqMemory / 2;
+ }
+
+ freeHugepages(&hugepagesRsp);
+
+ } else {
+ printf(" Couldn't get Hugepage info, defaulting to %ld, err code: %d\n", hugepageMemory, err);
+ }
+ }
+
+ /* Build up memory portion of DPDK Args. */
+ strncpy(&myArgsArray[argc++][0], "-m", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "%ld", hugepageMemory);
+
+ strncpy(&myArgsArray[argc++][0], "-n", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "4", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ return(argc);
+}
+
+char** GetArgs(int *pArgc, eDpdkAppType appType)
+{
+ int argc = 0;
+ int i;
+ struct CPUResponse cpuRsp;
+ int err;
+ int portMask = 0;
+ int portCnt = 0;
+ int lcoreBase = 0;
+ int port;
+ int length = 0;
+
+ sleep(2);
+
+ memset(&cpuRsp, 0, sizeof(cpuRsp));
+ err = GetCPUInfo(&cpuRsp);
+ if (err) {
+ printf("Couldn't get CPU info, err code: %d\n", err);
+ }
+ if (cpuRsp.CPUSet) {
+ printf(" cpuRsp.CPUSet = %s\n", cpuRsp.CPUSet);
+
+ // Free the string
+ free(cpuRsp.CPUSet);
+ }
+
+
+ memset(&myArgsArray[0][0], 0, sizeof(char)*DPDK_ARGS_MAX_ARG_STRLEN*DPDK_ARGS_MAX_ARGS);
+ memset(&myArgv[0], 0, sizeof(char)*DPDK_ARGS_MAX_ARGS);
+
+ if (pArgc) {
+ /*
+ * Initialize EAL Options
+ */
+ strncpy(&myArgsArray[argc++][0], "dpdk-app", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ argc = getHugepages(argc);
+
+ //strncpy(&myArgsArray[argc++][0], "--file-prefix=dpdk-app_", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ if (appType == DPDK_APP_TESTPMD) {
+ strncpy(&myArgsArray[argc++][0], "-l", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "1-3", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ strncpy(&myArgsArray[argc++][0], "--master-lcore", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "1", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ argc = getInterfaces(argc, &portCnt, &portMask);
+
+ /*
+ * Initialize APP Specific Options
+ */
+ strncpy(&myArgsArray[argc++][0], "--", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ strncpy(&myArgsArray[argc++][0], "--auto-start", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "--tx-first", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "--no-lsc-interrupt", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ /* testpmd exits if there is not user enteraction, so print stats */
+ /* every so often to keep program running. */
+ strncpy(&myArgsArray[argc++][0], "--stats-period", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "60", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ }
+ else if (appType == DPDK_APP_L3FWD) {
+ /* NOTE: The l3fwd app requires a TX Queue per lcore. So seeting lcore to 1 */
+ /* until additional queues are added to underlying interface. */
+ strncpy(&myArgsArray[argc++][0], "-l", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "1", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ strncpy(&myArgsArray[argc++][0], "--master-lcore", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "1", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ lcoreBase = 1;
+
+ argc = getInterfaces(argc, &portCnt, &portMask);
+
+ /*
+ * Initialize APP Specific Options
+ */
+ strncpy(&myArgsArray[argc++][0], "--", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ /* Set the PortMask, Hexadecimal bitmask of ports used by app. */
+ strncpy(&myArgsArray[argc++][0], "-p", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "0x%x", portMask);
+
+ /* Set all ports to promiscuous mode so that packets are accepted */
+ /* regardless of the packet’s Ethernet MAC destination address. */
+ strncpy(&myArgsArray[argc++][0], "-P", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ /* Determines which queues from which ports are mapped to which cores. */
+ /* Usage: --config="(port,queue,lcore)[,(port,queue,lcore)]" */
+ length = 0;
+ for (port = 0; port < portCnt; port++) {
+ /* If the first port, add '--config="' to string. */
+ if (port == 0) {
+ length += snprintf(&myArgsArray[argc][length], DPDK_ARGS_MAX_ARG_STRLEN-length,
+ "--config=\"");
+ }
+ /* If not the first port, add a ',' to string. */
+ else {
+ length += snprintf(&myArgsArray[argc][length], DPDK_ARGS_MAX_ARG_STRLEN-length, ",");
+ }
+
+ /* Add each port data */
+ length += snprintf(&myArgsArray[argc][length], DPDK_ARGS_MAX_ARG_STRLEN-length,
+ "(%d,%d,%d)", port, 0 /* queue */, lcoreBase /*+port*/);
+
+ /* If the last port, add a trailing " to string. */
+ if (port == portCnt-1) {
+ length += snprintf(&myArgsArray[argc][length], DPDK_ARGS_MAX_ARG_STRLEN-length, "\"");
+ }
+ }
+ argc++;
+
+ /* Set to use software to analyze packet type. Without this option, */
+ /* hardware will check the packet type. Not sure if vHost supports. */
+ strncpy(&myArgsArray[argc++][0], "--parse-ptype", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ }
+ else if (appType == DPDK_APP_L2FWD) {
+ strncpy(&myArgsArray[argc++][0], "-l", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "1-3", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ argc = getInterfaces(argc, &portCnt, &portMask);
+
+ /*
+ * Initialize APP Specific Options
+ */
+ strncpy(&myArgsArray[argc++][0], "--", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ /* Set the PortMask, Hexadecimal bitmask of ports used by app. */
+ strncpy(&myArgsArray[argc++][0], "-p", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ snprintf(&myArgsArray[argc++][0], DPDK_ARGS_MAX_ARG_STRLEN-1,
+ "0x%x", portMask);
+
+ /* Set the PERIOD, statistics will be refreshed each PERIOD seconds. */
+ strncpy(&myArgsArray[argc++][0], "-T", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ strncpy(&myArgsArray[argc++][0], "120", DPDK_ARGS_MAX_ARG_STRLEN-1);
+
+ /* Set to no-mac-updating. When enabled: */
+ /* - source MAC address is replaced by the TX port MAC address */
+ /* - The destination MAC address is replaced by 02:00:00:00:00:TX_PORT_ID */
+ strncpy(&myArgsArray[argc++][0], "--no-mac-updating", DPDK_ARGS_MAX_ARG_STRLEN-1);
+ }
+
+ for (i = 0; i < argc; i++) {
+ myArgv[i] = &myArgsArray[i][0];
+ }
+ *pArgc = argc;
+ }
+
+ return(myArgv);
+}
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.h b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.h
new file mode 100644
index 00000000..69424c8b
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/dpdk-args.h
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: Apache-2.0
+// Copyright(c) 2021 Red Hat, Inc.
+
+#ifndef __DPDK_ARGS_H__
+#define __DPDK_ARGS_H__
+
+typedef enum {
+ DPDK_APP_TESTPMD = 1,
+ DPDK_APP_L3FWD,
+ DPDK_APP_L2FWD,
+ DPDK_APP_OTHER
+} eDpdkAppType;
+
+extern char** GetArgs(int *pArgc, eDpdkAppType appType);
+
+#endif /* __DPDK_ARGS_H__ */
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_eal_init.txt b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_eal_init.txt
new file mode 100644
index 00000000..43fd6c91
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_eal_init.txt
@@ -0,0 +1,39 @@
+#if 0
+ ret = rte_eal_init(argc, argv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+ argc -= ret;
+ argv += ret;
+#else
+ int i;
+ int myArgc = 0;
+ char **myArgv = NULL;
+
+ printf("ENTER dpdk-app:\n");
+ printf(" argc=%d\n", argc);
+ for (i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\n");
+
+ if (argc > 1) {
+ ret = rte_eal_init(argc, argv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+ argc -= ret;
+ argv += ret;
+ } else {
+ myArgv = GetArgs(&myArgc, DPDK_APP_L2FWD);
+ printf(" myArgc=%d\n", myArgc);
+ for (i = 0; i < myArgc; i++) {
+ printf(" %s", myArgv[i]);
+ }
+ printf("\n");
+
+ ret = rte_eal_init(myArgc, myArgv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+ myArgc -= ret;
+ myArgv += ret;
+ }
+#endif
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_parse_args.txt b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_parse_args.txt
new file mode 100644
index 00000000..a221ae94
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_parse_args.txt
@@ -0,0 +1,9 @@
+#if 0
+ ret = l2fwd_parse_args(argc, argv);
+#else
+ if (argc > 1) {
+ ret = l2fwd_parse_args(argc, argv);
+ } else {
+ ret = l2fwd_parse_args(myArgc, myArgv);
+ }
+#endif
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_substitute.sh b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_substitute.sh
new file mode 100755
index 00000000..83c5dbdb
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l2fwd_substitute.sh
@@ -0,0 +1,59 @@
+#!/bin/bash
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright(c) 2021 Red Hat, Inc.
+
+
+# Add new app-netutil headerfile to the main code so app-netutil
+# can be called to gather parameters.
+#
+# Search for line with: "#include <rte_mbuf.h>".
+# Append line: "#include "dpdk-args.h"".
+sed -i -e '/#include <rte_mbuf.h>/a #include "dpdk-args.h"' main.c
+
+
+# Replace the call to rte_eal_init() to call app-netutil code first
+# if no input parametes were passed in. app-netutil code generates
+# its own set of DPDK parameters that are used instead. If input
+# parameters were passed in, call rte_eal_init() with input parameters
+# and run as if app-netutil wasn't there.
+#
+# Search for the line with "ret = rte_eal_init(argc, argv);"
+# Create a label 'a' and continue searching and copying until
+# line with "argv += ret;" is found.
+# Replace that block of code with the contents of file 'l2fwd_eal_init.txt'.
+sed -i '/ret = rte_eal_init(argc, argv);/{
+:a;N;/argv += ret;/!ba;N;s/.*\n//g
+r l2fwd_eal_init.txt
+}' main.c
+
+
+# If no input parametes were passed in, use the parameter list generated
+# by app-netutil in the previous patch to call the local parameter
+# parsing code, l2fwd_parse_args(). If input parameters were passed in,
+# call l2fwd_parse_args() with input parameters and run as if app-netutil
+# wasn't there.
+#
+# Search for the line with "ret = l2fwd_parse_args(argc, argv);"
+# Replace that line of code with the contents of file
+# 'l2fwd_parse_args.txt'.
+sed -i '/ret = l2fwd_parse_args(argc, argv);/{
+s/ret = l2fwd_parse_args(argc, argv);//g
+r l2fwd_parse_args.txt
+}' main.c
+
+
+# Add new app-netutil source file to the Makefile.
+#
+# Search for line with: "SRCS-y :=".
+# Append line: "SRCS-y += c_util.c dpdk-args.c".
+sed -i -e '/SRCS-y :=/a SRCS-y += c_util.c dpdk-args.c' Makefile
+
+
+# Add new app-netutil shared library to the Makefile.
+# Contains the C API and GO package which collects the
+# interface data.
+#
+# Search for line with: "SRCS-y += c_util.c dpdk-args.c".
+# Append line: "LDLIBS += -lnetutil_api".
+sed -i -e '/SRCS-y += c_util.c dpdk-args.c/a LDLIBS += -lnetutil_api' Makefile
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_eal_init.txt b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_eal_init.txt
new file mode 100644
index 00000000..80e9af1f
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_eal_init.txt
@@ -0,0 +1,39 @@
+#if 0
+ ret = rte_eal_init(argc, argv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+ argc -= ret;
+ argv += ret;
+#else
+ /* int i; */
+ int myArgc = 0;
+ char **myArgv = NULL;
+
+ printf("ENTER dpdk-app:\n");
+ printf(" argc=%d\n", argc);
+ for (i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\n");
+
+ if (argc > 1) {
+ ret = rte_eal_init(argc, argv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+ argc -= ret;
+ argv += ret;
+ } else {
+ myArgv = GetArgs(&myArgc, DPDK_APP_L3FWD);
+ printf(" myArgc=%d\n", myArgc);
+ for (i = 0; i < myArgc; i++) {
+ printf(" %s", myArgv[i]);
+ }
+ printf("\n");
+
+ ret = rte_eal_init(myArgc, myArgv);
+ if (ret < 0)
+ rte_exit(EXIT_FAILURE, "Invalid EAL parameters\n");
+ myArgc -= ret;
+ myArgv += ret;
+ }
+#endif
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_parse_args.txt b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_parse_args.txt
new file mode 100644
index 00000000..57b4e8d3
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_parse_args.txt
@@ -0,0 +1,9 @@
+#if 0
+ ret = parse_args(argc, argv);
+#else
+ if (argc > 1) {
+ ret = parse_args(argc, argv);
+ } else {
+ ret = parse_args(myArgc, myArgv);
+ }
+#endif
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_substitute.sh b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_substitute.sh
new file mode 100755
index 00000000..020886f5
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/l3fwd_substitute.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright(c) 2021 Red Hat, Inc.
+
+
+# Add new app-netutil headerfile to the main code so app-netutil
+# can be called to gather parameters.
+#
+# Search for line with: "#include "l3fwd.h"".
+# Append line: "#include "dpdk-args.h"".
+sed -i -e '/#include "l3fwd.h"/a #include "dpdk-args.h"' main.c
+
+
+# L3fwd code defaults to using some Checksum Offload that doesn't
+# work on all hardware. Turned off.
+#
+# Search for: ".offloads = DEV_RX_OFFLOAD_CHECKSUM,".
+# Replace with: ".offloads = 0, /*DEV_RX_OFFLOAD_CHECKSUM,*/".
+sed -i -e 's!.offloads = DEV_RX_OFFLOAD_CHECKSUM,!.offloads = 0, /*DEV_RX_OFFLOAD_CHECKSUM,*/!' main.c
+
+
+# Replace the call to rte_eal_init() to call app-netutil code first
+# if no input parametes were passed in. app-netutil code generates
+# its own set of DPDK parameters that are used instead. If input
+# parameters were passed in, call rte_eal_init() with input parameters
+# and run as if app-netutil wasn't there.
+#
+# Search for the line with "ret = rte_eal_init(argc, argv);"
+# Create a label 'a' and continue searching and copying until
+# line with "argv += ret;" is found.
+# Replace that block of code with the contents of file 'l3fwd_eal_init.txt'.
+sed -i '/ret = rte_eal_init(argc, argv);/{
+:a;N;/argv += ret;/!ba;N;s/.*\n//g
+r l3fwd_eal_init.txt
+}' main.c
+
+
+# If no input parametes were passed in, use the parameter list generated
+# by app-netutil in the previous patch to call the local parameter
+# parsing code, parse_args(). If input parameters were passed in,
+# call parse_args() with input parameters and run as if app-netutil
+# wasn't there.
+#
+# Search for the line with "ret = parse_args(argc, argv);"
+# Replace that line of code with the contents of file
+# 'l3fwd_parse_args.txt'.
+sed -i '/ret = parse_args(argc, argv);/{
+s/ret = parse_args(argc, argv);//g
+r l3fwd_parse_args.txt
+}' main.c
+
+
+# Add new app-netutil source file to the Makefile.
+#
+# Search for line with: "SRCS-y :=".
+# Append line: "SRCS-y += c_util.c dpdk-args.c".
+sed -i -e '/SRCS-y :=/a SRCS-y += c_util.c dpdk-args.c' Makefile
+
+
+# Add new app-netutil shared library to the Makefile.
+# Contains the C API and GO package which collects the
+# interface data.
+#
+# Search for line with: "SRCS-y += c_util.c dpdk-args.c".
+# Append line: "LDLIBS += -lnetutil_api".
+sed -i -e '/SRCS-y += c_util.c dpdk-args.c/a LDLIBS += -lnetutil_api' Makefile
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_eal_init.txt b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_eal_init.txt
new file mode 100644
index 00000000..a0da4d50
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_eal_init.txt
@@ -0,0 +1,28 @@
+#if 0
+ diag = rte_eal_init(argc, argv);
+#else
+ int i;
+ int myArgc = 0;
+ char **myArgv = NULL;
+
+ printf("ENTER dpdk-app:\n");
+ printf(" argc=%d\n", argc);
+ for (i = 0; i < argc; i++) {
+ printf(" %s", argv[i]);
+ }
+ printf("\n");
+
+ if (argc > 1) {
+ diag = rte_eal_init(argc, argv);
+ } else {
+ myArgv = GetArgs(&myArgc, DPDK_APP_TESTPMD);
+ printf(" myArgc=%d\n", myArgc);
+ for (i = 0; i < myArgc; i++) {
+ printf(" %s", myArgv[i]);
+ }
+ printf("\n");
+
+ diag = rte_eal_init(myArgc, myArgv);
+ }
+#endif
+
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_launch_args_parse.txt b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_launch_args_parse.txt
new file mode 100644
index 00000000..7c2254bf
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_launch_args_parse.txt
@@ -0,0 +1,19 @@
+#if 0
+ argc -= diag;
+ argv += diag;
+ if (argc > 1)
+ launch_args_parse(argc, argv);
+#else
+ if (argc > 1) {
+ argc -= diag;
+ argv += diag;
+ if (argc > 1)
+ launch_args_parse(argc, argv);
+ } else {
+ myArgc -= diag;
+ myArgv += diag;
+ if (myArgc > 1)
+ launch_args_parse(myArgc, myArgv);
+ }
+#endif
+
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_substitute.sh b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_substitute.sh
new file mode 100755
index 00000000..30e222e9
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/testpmd_substitute.sh
@@ -0,0 +1,61 @@
+#!/bin/bash
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright(c) 2021 Red Hat, Inc.
+
+
+# Add new app-netutil headerfile to the main code so app-netutil
+# can be called to gather parameters.
+#
+# Search for: "#include "testpmd.h"".
+# Append: "#include "dpdk-args.h"".
+sed -i -e '/#include "testpmd.h"/a #include "dpdk-args.h"' testpmd.c
+
+
+# Replace the call to rte_eal_init() to call app-netutil code first
+# if no input parametes were passed in. app-netutil code generates
+# its own set of DPDK parameters that are used instead. If input
+# parameters were passed in, call rte_eal_init() with input parameters
+# and run as if app-netutil wasn't there.
+#
+# Search for the line with "diag = rte_eal_init(argc, argv);"
+# Replace that line of code with the contents of file
+# 'testpmd_eal_init.txt'.
+sed -i '/diag = rte_eal_init(argc, argv);/{
+s/diag = rte_eal_init(argc, argv);//g
+r testpmd_eal_init.txt
+}' testpmd.c
+
+
+# If no input parametes were passed in, use the parameter list generated
+# by app-netutil in the previous patch to call the local parameter
+# parsing code, launch_args_parse(). If input parameters were passed in,
+# call launch_args_parse() with input parameters and run as if app-netutil
+# wasn't there.
+#
+# Search for the line with "argc -= diag;"
+# Create a label 'a' and continue searching and copying until
+# line with "launch_args_parse(argc, argv);" is found.
+# Replace that block of code with the contents of file
+# 'testpmd_launch_args_parse.txt'.
+sed -i '/argc -= diag;/{
+:a;N;/launch_args_parse(argc, argv);/!ba;N;s/.*\n//g
+r testpmd_launch_args_parse.txt
+}' testpmd.c
+
+
+# Add new app-netutil source file to the Makefile.
+#
+# Search for line with: "SRCS-y += parameters.c".
+# Replace with line: "SRCS-y += parameters.c c_util.c dpdk-args.c".
+sed -i -e 's/SRCS-y += parameters.c/SRCS-y += parameters.c c_util.c dpdk-args.c/' Makefile
+
+
+# Add new app-netutil shared library to the Makefile.
+# Contains the C API and GO package which collects the
+# interface data.
+#
+# Search for line with: "SRCS-y += util.c".
+# Append line: "LDLIBS += -lnetutil_api".
+sed -i -e '/SRCS-y += util.c/a LDLIBS += -lnetutil_api' Makefile
+
diff --git a/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/vhost_substitute.sh b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/vhost_substitute.sh
new file mode 100755
index 00000000..471eecc5
--- /dev/null
+++ b/tools/docker/test-containers/dpdk-forwarding-pods/l2l3fwd/vhost_substitute.sh
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+# SPDX-License-Identifier: Apache-2.0
+# Copyright(c) 2021 Red Hat, Inc.
+
+
+# The first two commands update one of the 'if' checks to remove
+# the check for 'master == VHOST_USER_SET_VRING_CALL'.
+#
+# Search for: " !(dev->flags & VIRTIO_DEV_VDPA_CONFIGURED) &&".
+# Replace with: " !(dev->flags & VIRTIO_DEV_VDPA_CONFIGURED)) {".
+sed -i -e 's/ !(dev->flags & VIRTIO_DEV_VDPA_CONFIGURED) &&/ !(dev->flags \& VIRTIO_DEV_VDPA_CONFIGURED)) {/g' lib/librte_vhost/vhost_user.c
+#
+# Search for line with: " msg.request.master == VHOST_USER_SET_VRING_CALL) {".
+# Delete the line.
+sed -i -e '/ msg\.request\.master == VHOST_USER_SET_VRING_CALL) {/d' lib/librte_vhost/vhost_user.c
+
+
+# Force an RARP message to be sent out.
+#
+# Search for line with: " hw->started = true;".
+# Append line: " virtio_notify_peers(dev);".
+sed -i -e '/ hw->started = true;/a virtio_notify_peers(dev);' drivers/net/virtio/virtio_ethdev.c