diff --git a/app/test-pmd/cmdline.c b/app/test-pmd/cmdline.c
index f01db2a..952a9df 100644
--- a/app/test-pmd/cmdline.c
+++ b/app/test-pmd/cmdline.c
@@ -8733,6 +8733,57 @@ cmdline_parse_inst_t cmd_set_hash_global_config = {
 	},
 };
 
+/* *** ADD/REMOVE A MULTICAST MAC ADDRESS TO/FROM A PORT *** */
+struct cmd_mcast_addr_result {
+	cmdline_fixed_string_t mcast_addr_cmd;
+	cmdline_fixed_string_t what;
+	uint8_t port_num;
+	struct ether_addr mc_addr;
+};
+
+static void cmd_mcast_addr_parsed(void *parsed_result,
+		__attribute__((unused)) struct cmdline *cl,
+		__attribute__((unused)) void *data)
+{
+	struct cmd_mcast_addr_result *res = parsed_result;
+
+	if (!is_multicast_ether_addr(&res->mc_addr)) {
+		printf("Invalid multicast addr %02X:%02X:%02X:%02X:%02X:%02X\n",
+		       res->mc_addr.addr_bytes[0], res->mc_addr.addr_bytes[1],
+		       res->mc_addr.addr_bytes[2], res->mc_addr.addr_bytes[3],
+		       res->mc_addr.addr_bytes[4], res->mc_addr.addr_bytes[5]);
+		return;
+	}
+	if (strcmp(res->what, "add") == 0)
+		mcast_addr_add(res->port_num, &res->mc_addr);
+	else
+		mcast_addr_remove(res->port_num, &res->mc_addr);
+}
+
+cmdline_parse_token_string_t cmd_mcast_addr_cmd =
+	TOKEN_STRING_INITIALIZER(struct cmd_mcast_addr_result,
+				 mcast_addr_cmd, "mcast_addr");
+cmdline_parse_token_string_t cmd_mcast_addr_what =
+	TOKEN_STRING_INITIALIZER(struct cmd_mcast_addr_result, what,
+				 "add#remove");
+cmdline_parse_token_num_t cmd_mcast_addr_portnum =
+	TOKEN_NUM_INITIALIZER(struct cmd_mcast_addr_result, port_num, UINT8);
+cmdline_parse_token_etheraddr_t cmd_mcast_addr_addr =
+	TOKEN_ETHERADDR_INITIALIZER(struct cmd_mac_addr_result, address);
+
+cmdline_parse_inst_t cmd_mcast_addr = {
+	.f = cmd_mcast_addr_parsed,
+	.data = (void *)0,
+	.help_str = "mcast_addr add|remove X <mcast_addr>: add/remove multicast MAC address on port X",
+	.tokens = {
+		(void *)&cmd_mcast_addr_cmd,
+		(void *)&cmd_mcast_addr_what,
+		(void *)&cmd_mcast_addr_portnum,
+		(void *)&cmd_mcast_addr_addr,
+		NULL,
+	},
+};
+
 /* ******************************************************************************** */
 
 /* list of instructions */
@@ -8862,6 +8913,7 @@ cmdline_parse_ctx_t main_ctx[] = {
 	(cmdline_parse_inst_t *)&cmd_set_sym_hash_ena_per_port,
 	(cmdline_parse_inst_t *)&cmd_get_hash_global_config,
 	(cmdline_parse_inst_t *)&cmd_set_hash_global_config,
+	(cmdline_parse_inst_t *)&cmd_mcast_addr,
 	NULL,
 };
 
diff --git a/app/test-pmd/config.c b/app/test-pmd/config.c
index f788ed5..52917c7 100644
--- a/app/test-pmd/config.c
+++ b/app/test-pmd/config.c
@@ -2130,3 +2130,145 @@ set_vf_rate_limit(portid_t port_id, uint16_t vf, uint16_t rate, uint64_t q_msk)
 		port_id, diag);
 	return diag;
 }
+
+/*
+ * Functions to manage the set of filtered Multicast MAC addresses.
+ *
+ * A pool of filtered multicast MAC addresses is associated with each port.
+ * The pool is allocated in chunks of MCAST_POOL_INC multicast addresses.
+ * The address of the pool and the number of valid multicast MAC addresses
+ * recorded in the pool are stored in the fields "mc_addr_pool" and
+ * "mc_addr_nb" of the "rte_port" data structure.
+ *
+ * The function "rte_eth_dev_set_mc_addr_list" of the PMDs API imposes
+ * to be supplied a contiguous array of multicast MAC addresses.
+ * To comply with this constraint, the set of multicast addresses recorded
+ * into the pool are systematically compacted at the beginning of the pool.
+ * Hence, when a multicast address is removed from the pool, all following
+ * addresses, if any, are copied back to keep the set contiguous.
+ */
+#define MCAST_POOL_INC 32
+
+static int
+mcast_addr_pool_extend(struct rte_port *port)
+{
+	struct ether_addr *mc_pool;
+	size_t mc_pool_size;
+
+	/*
+	 * If a free entry is available at the end of the pool, just
+	 * increment the number of recorded multicast addresses.
+	 */
+	if ((port->mc_addr_nb % MCAST_POOL_INC) != 0) {
+		port->mc_addr_nb++;
+		return 0;
+	}
+
+	/*
+	 * [re]allocate a pool with MCAST_POOL_INC more entries.
+	 * The previous test guarantees that port->mc_addr_nb is a multiple
+	 * of MCAST_POOL_INC.
+	 */
+	mc_pool_size = sizeof(struct ether_addr) * (port->mc_addr_nb +
+						    MCAST_POOL_INC);
+	mc_pool = (struct ether_addr *) realloc(port->mc_addr_pool,
+						mc_pool_size);
+	if (mc_pool == NULL) {
+		printf("allocation of pool of %u multicast addresses failed\n",
+		       port->mc_addr_nb + MCAST_POOL_INC);
+		return -ENOMEM;
+	}
+
+	port->mc_addr_pool = mc_pool;
+	port->mc_addr_nb++;
+	return 0;
+
+}
+
+static void
+mcast_addr_pool_remove(struct rte_port *port, uint32_t addr_idx)
+{
+	port->mc_addr_nb--;
+	if (addr_idx == port->mc_addr_nb) {
+		/* No need to recompact the set of multicast addressses. */
+		if (port->mc_addr_nb == 0) {
+			/* free the pool of multicast addresses. */
+			free(port->mc_addr_pool);
+			port->mc_addr_pool = NULL;
+		}
+		return;
+	}
+	memmove(&port->mc_addr_pool[addr_idx],
+		&port->mc_addr_pool[addr_idx + 1],
+		sizeof(struct ether_addr) * (port->mc_addr_nb - addr_idx));
+}
+
+static void
+eth_port_multicast_addr_list_set(uint8_t port_id)
+{
+	struct rte_port *port;
+	int diag;
+
+	port = &ports[port_id];
+	diag = rte_eth_dev_set_mc_addr_list(port_id, port->mc_addr_pool,
+					    port->mc_addr_nb);
+	if (diag == 0)
+		return;
+	printf("rte_eth_dev_set_mc_addr_list(port=%d, nb=%u) failed. diag=%d\n",
+	       port->mc_addr_nb, port_id, -diag);
+}
+
+void
+mcast_addr_add(uint8_t port_id, struct ether_addr *mc_addr)
+{
+	struct rte_port *port;
+	uint32_t i;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN))
+		return;
+
+	port = &ports[port_id];
+
+	/*
+	 * Check that the added multicast MAC address is not already recorded
+	 * in the pool of multicast addresses.
+	 */
+	for (i = 0; i < port->mc_addr_nb; i++) {
+		if (is_same_ether_addr(mc_addr, &port->mc_addr_pool[i])) {
+			printf("multicast address already filtered by port\n");
+			return;
+		}
+	}
+
+	if (mcast_addr_pool_extend(port) != 0)
+		return;
+	ether_addr_copy(mc_addr, &port->mc_addr_pool[i]);
+	eth_port_multicast_addr_list_set(port_id);
+}
+
+void
+mcast_addr_remove(uint8_t port_id, struct ether_addr *mc_addr)
+{
+	struct rte_port *port;
+	uint32_t i;
+
+	if (port_id_is_invalid(port_id, ENABLED_WARN))
+		return;
+
+	port = &ports[port_id];
+
+	/*
+	 * Search the pool of multicast MAC addresses for the removed address.
+	 */
+	for (i = 0; i < port->mc_addr_nb; i++) {
+		if (is_same_ether_addr(mc_addr, &port->mc_addr_pool[i]))
+			break;
+	}
+	if (i == port->mc_addr_nb) {
+		printf("multicast address not filtered by port %d\n", port_id);
+		return;
+	}
+
+	mcast_addr_pool_remove(port, i);
+	eth_port_multicast_addr_list_set(port_id);
+}
diff --git a/app/test-pmd/testpmd.h b/app/test-pmd/testpmd.h
index c3b6700..f2c84d9 100644
--- a/app/test-pmd/testpmd.h
+++ b/app/test-pmd/testpmd.h
@@ -162,6 +162,8 @@ struct rte_port {
 	uint8_t			dcb_flag;   /**< enable dcb */
 	struct rte_eth_rxconf   rx_conf;    /**< rx configuration */
 	struct rte_eth_txconf   tx_conf;    /**< tx configuration */
+	struct ether_addr       *mc_addr_pool; /**< pool of multicast addrs */
+	uint32_t                mc_addr_nb; /**< nb. of addr. in mc_addr_pool */
 };
 
 extern portid_t __rte_unused
@@ -563,6 +565,10 @@ void get_5tuple_filter(uint8_t port_id, uint16_t index);
 int rx_queue_id_is_invalid(queueid_t rxq_id);
 int tx_queue_id_is_invalid(queueid_t txq_id);
 
+/* Functions to manage the set of filtered Multicast MAC addresses */
+void mcast_addr_add(uint8_t port_id, struct ether_addr *mc_addr);
+void mcast_addr_remove(uint8_t port_id, struct ether_addr *mc_addr);
+
 enum print_warning {
 	ENABLED_WARN = 0,
 	DISABLED_WARN
