Implementing a New DPI Client Plugin
The updated approach (framework) on DPI plugin development makes sure that a developer only has to focus on the essence of the work to be performed. The framework abstracts a significant share of the common parts required by a client plugin, such as initialization, and generic configuration loading.
In the remainder of this page, implementation of fsm_dpi_adt
is used as a basis for showcasing the recommended way of implementing a new DPI client plugin.
Code Layout
The code layout follows the same pattern as the rest of the code found in src/lib
:
inc/
src/
tests/
futs/
ut/
Ideally, the code in src/
is split into two files: one that implements the interface to the DPI client plugin (here, fsm_dpi_adt.c
), and another one containing the code specific to the plugin (here, dpi_adt.c
). While not a requirement, this helps with file size and complexity (and testing).
Implementing the DPI Client Interface
The client interface requires five main functions to be defined (the actual code is provided to highlight important parts in the client plugin. Refer to fsm_dpi_adt.c
).
fsm_dpi_adt_init()
This function is defined in OVSBD as the entry point to the plugin itself (as such it is required).int fsm_dpi_adt_init(struct fsm_session *session) { struct fsm_dpi_client_session *client_session; struct fsm_dpi_plugin_client_ops *client_ops; struct fsm_dpi_adt_report_aggregator *aggr; struct fsm_dpi_adt_session *adt_session; int ret; /** * Initialize generic client (similar to the C++ parent constructor) */ ret = fsm_dpi_client_init(session); if (ret != 0) return ret; /** * Now we need to stitch the client specific functions to the ops structure. * This can be viewed as the "C" version of a function overload, as the ops * structure was generically initialized within fsm_dpi_client_init() above. */ session->ops.update = fsm_dpi_adt_update; session->ops.periodic = fsm_dpi_adt_periodic; session->ops.exit = fsm_dpi_adt_exit; /* Set the plugin specific ops */ client_ops = &session->p_ops->dpi_plugin_client_ops; client_ops->process_attr = fsm_dpi_adt_process_attr; /* Fetch the specific updates for this client plugin */ session->ops.update(session); /** * From this point on, we are actually performing client specific * initializations and setups. */ client_session = (struct fsm_dpi_client_session *)session->handler_ctxt; adt_session = (struct fsm_dpi_adt_session *)client_session->private_session; if (adt_session == NULL) { adt_session = CALLOC(1, sizeof(*adt_session)); if (adt_session == NULL) return -1; adt_session->adt_aggr = CALLOC(1, sizeof(*aggr)); if (adt_session->adt_aggr == NULL) return -1; client_session->private_session = adt_session; } aggr = adt_session->adt_aggr; if (aggr->initialized) return 0; aggr->node_id = STRDUP(session->node_id); if (aggr->node_id == NULL) goto cleanup; aggr->location_id = STRDUP(session->location_id); if (aggr->location_id == NULL) goto cleanup; aggr->data_max = FSM_DPI_ADT_MAX_DATAPOINTS; aggr->data_idx = 0; aggr->data_prov = aggr->data_max; aggr->data = CALLOC(aggr->data_prov, sizeof(*aggr->data)); if (aggr->data == NULL) goto cleanup; /* Make eventual testing easier */ aggr->send_report = qm_conn_send_direct; aggr->initialized = true; LOGD("%s: Added session %s", __func__, session->name); return 0; cleanup: fsm_dpi_client_exit(session); return -1; }
fsm_dpi_adt_exit()
Cleans up any client specific data allocated.void fsm_dpi_adt_exit(struct fsm_session *session) { struct fsm_dpi_adt_report_aggregator *aggr; struct fsm_dpi_adt_session *adt_session; /** * Firs we perform the client specific cleanups */ aggr = NULL; adt_session = fsm_dpi_adt_get_session(session); if (adt_session == NULL) goto clean_all; aggr = adt_session->adt_aggr; if (aggr == NULL) goto clean_all; if (!aggr->initialized) goto clean_all; dpi_adt_send_report(session); /* This should be empty, but make extra sure */ dpi_adt_free_aggr_store(aggr); FREE(aggr->data); FREE(aggr->location_id); FREE(aggr->node_id); FREE(adt_session); aggr->initialized = false; clean_all: FREE(aggr); /** * Free the generic client (a-la C++ destructor) */ fsm_dpi_client_exit(session); }
fsm_dpi_adt_update()
Just like other plugins, the function is called whenever a configuration for the specific plugin is updated inside OVSDB.void fsm_dpi_adt_update(struct fsm_session *session) { /** * Generic config first */ fsm_dpi_client_update(session); /** * ADT specific entries (currently none) */ LOGD("%s: Updating ADT config", __func__); }
fsm_dpi_adt_periodic()
As for other plugins, calls are made periodically to report specific information for the client plugin.fsm_dpi_adt_process_attr()
This is the main processing function. This function gets called whenever the Walleye engine reports an attribute and triggers the callback function.
Building and Installing a DPI Client Plugin
Since we are expanding the fsm_dpi_client
“object”, the unit.mk
file must provide the following dependency:
This ensures that the create libfsm_dpi_adt.so
has an explicit dependency into libfsm_dpi_client.so
. Because of this explicit dependency, both shared libraries must be installed on the target device.