/* * Portions of this file are subject to the following copyright(s). See * the Net-SNMP's COPYING file for more details and other copyrights * that may apply: * * Portions of this file are copyrighted by: * Copyright (c) 2016 VMware, Inc. All rights reserved. * Use is subject to license terms specified in the COPYING file * distributed with the Net-SNMP package. */ #include #include #include #include #include #if HAVE_STRING_H #include #else #include #endif #include #include #include #if HAVE_DMALLOC_H #include #endif netsnmp_feature_child_of(table_tdata_all, mib_helpers) netsnmp_feature_child_of(table_tdata, table_tdata_all) netsnmp_feature_child_of(table_tdata_delete_table, table_tdata_all) netsnmp_feature_child_of(table_tdata_extract_table, table_tdata_all) netsnmp_feature_child_of(table_tdata_remove_row, table_tdata_all) netsnmp_feature_child_of(table_tdata_insert_row, table_tdata_all) #ifdef NETSNMP_FEATURE_REQUIRE_TABLE_TDATA netsnmp_feature_require(table_container_row_insert) #ifdef NETSNMP_FEATURE_REQUIRE_TABLE_TDATA_REMOVE_ROW netsnmp_feature_require(table_container_row_remove) #endif /* NETSNMP_FEATURE_REQUIRE_TABLE_TDATA_REMOVE_ROW */ #endif /* NETSNMP_FEATURE_REQUIRE_TABLE_TDATA */ #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA /** @defgroup tdata tdata * Implement a table with datamatted storage. * @ingroup table * * This helper helps you implement a table where all the rows are * expected to be stored within the agent itself and not in some * external storage location. It can be used to store a list of * rows, where a row consists of the indexes to the table and a * generic data pointer. You can then implement a subhandler which * is passed the exact row definition and data it must return data * for or accept data for. Complex GETNEXT handling is greatly * simplified in this case. * * @{ */ /* ================================== * * TData API: Table maintenance * * ================================== */ /* * generates the index portion of an table oid from a varlist. */ void _netsnmp_tdata_generate_index_oid(netsnmp_tdata_row *row) { build_oid(&row->oid_index.oids, &row->oid_index.len, NULL, 0, row->indexes); } /** creates and returns a 'tdata' table data structure */ netsnmp_tdata * netsnmp_tdata_create_table(const char *name, long flags) { netsnmp_tdata *table = SNMP_MALLOC_TYPEDEF(netsnmp_tdata); if ( !table ) return NULL; table->flags = flags; if (name) table->name = strdup(name); if (!(table->flags & TDATA_FLAG_NO_CONTAINER)) { table->container = netsnmp_container_find( name ); if (!table->container) table->container = netsnmp_container_find( "table_container" ); if (table->container && name) table->container->container_name = strdup(name); } return table; } #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_DELETE_TABLE /** creates and returns a 'tdata' table data structure */ void netsnmp_tdata_delete_table(netsnmp_tdata *table) { if (!table) return; if (table->name) free(table->name); if (table->container) CONTAINER_FREE(table->container); SNMP_FREE(table); return; } #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_DELETE_TABLE */ /** creates and returns a pointer to new row data structure */ netsnmp_tdata_row * netsnmp_tdata_create_row(void) { netsnmp_tdata_row *row = SNMP_MALLOC_TYPEDEF(netsnmp_tdata_row); return row; } /** clones a 'tdata' row. DOES NOT CLONE THE TABLE-SPECIFIC ENTRY DATA. */ netsnmp_feature_child_of(tdata_clone_row, table_tdata_all) #ifndef NETSNMP_FEATURE_REMOVE_TDATA_CLONE_ROW netsnmp_tdata_row * netsnmp_tdata_clone_row(netsnmp_tdata_row *row) { netsnmp_tdata_row *newrow = NULL; if (!row) return NULL; newrow = netsnmp_memdup(row, sizeof(netsnmp_tdata_row)); if (!newrow) return NULL; if (row->indexes) { newrow->indexes = snmp_clone_varbind(newrow->indexes); if (!newrow->indexes) { SNMP_FREE(newrow); return NULL; } } if (row->oid_index.oids) { newrow->oid_index.oids = snmp_duplicate_objid(row->oid_index.oids, row->oid_index.len); if (!newrow->oid_index.oids) { if (newrow->indexes) snmp_free_varbind(newrow->indexes); SNMP_FREE(newrow); return NULL; } } return newrow; } #endif /* NETSNMP_FEATURE_REMOVE_TDATA_CLONE_ROW */ /** copy the contents of a 'tdata' row. DOES NOT COPY THE TABLE-SPECIFIC ENTRY DATA. */ netsnmp_feature_child_of(tdata_copy_row, table_tdata_all) #ifndef NETSNMP_FEATURE_REMOVE_TDATA_COPY_ROW int netsnmp_tdata_copy_row(netsnmp_tdata_row *dst_row, netsnmp_tdata_row *src_row) { if ( !src_row || !dst_row ) return -1; memcpy((u_char *) dst_row, (u_char *) src_row, sizeof(netsnmp_tdata_row)); if (src_row->indexes) { dst_row->indexes = snmp_clone_varbind(src_row->indexes); if (!dst_row->indexes) return -1; } if (src_row->oid_index.oids) { dst_row->oid_index.oids = snmp_duplicate_objid(src_row->oid_index.oids, src_row->oid_index.len); if (!dst_row->oid_index.oids) return -1; } return 0; } #endif /* NETSNMP_FEATURE_REMOVE_TDATA_COPY_ROW */ /** deletes the memory used by the specified row * returns the table-specific entry data * (that it doesn't know how to delete) */ void * netsnmp_tdata_delete_row(netsnmp_tdata_row *row) { void *data; if (!row) return NULL; /* * free the memory we can */ if (row->indexes) snmp_free_varbind(row->indexes); SNMP_FREE(row->oid_index.oids); data = row->data; free(row); /* * return the void * pointer */ return data; } /** * Adds a row to the given table (stored in proper lexographical order). * * returns SNMPERR_SUCCESS on successful addition. * or SNMPERR_GENERR on failure (E.G., indexes already existed) */ int netsnmp_tdata_add_row(netsnmp_tdata *table, netsnmp_tdata_row *row) { if (!row || !table) return SNMPERR_GENERR; if (row->indexes) _netsnmp_tdata_generate_index_oid(row); if (!row->oid_index.oids) { snmp_log(LOG_ERR, "illegal data attempted to be added to table %s (no index)\n", table->name); return SNMPERR_GENERR; } /* * The individual index values probably won't be needed, * so this memory can be released. * Note that this is purely internal to the helper. * The calling application can set this flag as * a hint to the helper that these values aren't * required, but it's up to the helper as to * whether it takes any notice or not! */ if (table->flags & TDATA_FLAG_NO_STORE_INDEXES) { snmp_free_varbind(row->indexes); row->indexes = NULL; } /* * add this row to the stored table */ if (CONTAINER_INSERT( table->container, row ) != 0) return SNMPERR_GENERR; DEBUGMSGTL(("tdata_add_row", "added row (%p)\n", row)); return SNMPERR_SUCCESS; } /** swaps out origrow with newrow. This does *not* delete/free anything! */ netsnmp_feature_child_of(tdata_replace_row, table_tdata_all) #ifndef NETSNMP_FEATURE_REMOVE_TDATA_REPLACE_ROW void netsnmp_tdata_replace_row(netsnmp_tdata *table, netsnmp_tdata_row *origrow, netsnmp_tdata_row *newrow) { netsnmp_tdata_remove_row(table, origrow); netsnmp_tdata_add_row(table, newrow); } #endif /* NETSNMP_FEATURE_REMOVE_TDATA_REPLACE_ROW */ /** * removes a row from the given table and returns it (no free's called) * * returns the row pointer itself on successful removing. * or NULL on failure (bad arguments) */ netsnmp_tdata_row * netsnmp_tdata_remove_row(netsnmp_tdata *table, netsnmp_tdata_row *row) { if (!row || !table) return NULL; CONTAINER_REMOVE( table->container, row ); return row; } /** * removes and frees a row of the given table and * returns the table-specific entry data * * returns the void * pointer on successful deletion. * or NULL on failure (bad arguments) */ void * netsnmp_tdata_remove_and_delete_row(netsnmp_tdata *table, netsnmp_tdata_row *row) { if (!row || !table) return NULL; /* * remove it from the list */ netsnmp_tdata_remove_row(table, row); return netsnmp_tdata_delete_row(row); } /* ================================== * * TData API: MIB maintenance * * ================================== */ Netsnmp_Node_Handler _netsnmp_tdata_helper_handler; /** Creates a tdata handler and returns it */ netsnmp_mib_handler * netsnmp_get_tdata_handler(netsnmp_tdata *table) { netsnmp_mib_handler *ret = NULL; if (!table) { snmp_log(LOG_INFO, "netsnmp_get_tdata_handler(NULL) called\n"); return NULL; } ret = netsnmp_create_handler(TABLE_TDATA_NAME, _netsnmp_tdata_helper_handler); if (ret) { ret->flags |= MIB_HANDLER_AUTO_NEXT; ret->myvoid = (void *) table; } return ret; } /* * The helper handler that takes care of passing a specific row of * data down to the lower handler(s). The table_container helper * has already taken care of identifying the appropriate row of the * table (and converting GETNEXT requests into an equivalent GET request) * So all we need to do here is make sure that the row is accessible * using tdata-style retrieval techniques as well. */ int _netsnmp_tdata_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_tdata *table = (netsnmp_tdata *) handler->myvoid; netsnmp_request_info *request; netsnmp_table_request_info *table_info; netsnmp_tdata_row *row; int need_processing; switch ( reqinfo->mode ) { case MODE_GET: #ifndef NETSNMP_NO_WRITE_SUPPORT case MODE_SET_RESERVE1: #endif /* NETSNMP_NO_WRITE_SUPPORT */ need_processing = reqinfo->mode == MODE_GET ? 0 : 1; for (request = requests; request; request = request->next) { if (request->processed) continue; table_info = netsnmp_extract_table_info(request); if (!table_info) { netsnmp_assert(table_info); /* yes, this will always hit */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; /* eek */ } row = (netsnmp_tdata_row*)netsnmp_container_table_row_extract( request ); if (!row && (reqinfo->mode == MODE_GET)) { netsnmp_assert(row); /* yes, this will always hit */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; /* eek */ } ++need_processing; netsnmp_request_add_list_data(request, netsnmp_create_data_list( TABLE_TDATA_TABLE, table, NULL)); netsnmp_request_add_list_data(request, netsnmp_create_data_list( TABLE_TDATA_ROW, row, NULL)); } /** skip next handler if processing not needed */ if (!need_processing) handler->flags |= MIB_HANDLER_AUTO_NEXT_OVERRIDE_ONCE; } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; } /** registers a tdata-based MIB table */ int netsnmp_tdata_register(netsnmp_handler_registration *reginfo, netsnmp_tdata *table, netsnmp_table_registration_info *table_info) { netsnmp_mib_handler *handler = netsnmp_get_tdata_handler(table); if (!reginfo || !table || !table_info || !handler || (netsnmp_inject_handler(reginfo, handler) != SNMPERR_SUCCESS)) { snmp_log(LOG_ERR, "could not create tdata handler\n"); netsnmp_handler_free(handler); netsnmp_handler_registration_free(reginfo); return SNMP_ERR_GENERR; } return netsnmp_container_table_register(reginfo, table_info, table->container, TABLE_CONTAINER_KEY_NETSNMP_INDEX); } netsnmp_feature_child_of(tdata_unregister, table_tdata_all) #ifndef NETSNMP_FEATURE_REMOVE_TDATA_UNREGISTER int netsnmp_tdata_unregister(netsnmp_handler_registration *reginfo) { /* free table; */ return netsnmp_container_table_unregister(reginfo); } #endif /* NETSNMP_FEATURE_REMOVE_TDATA_UNREGISTER */ #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_EXTRACT_TABLE /** extracts the tdata table from the request structure */ netsnmp_tdata * netsnmp_tdata_extract_table(netsnmp_request_info *request) { return (netsnmp_tdata *) netsnmp_request_get_list_data(request, TABLE_TDATA_TABLE); } #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_EXTRACT_TABLE */ /** extracts the tdata container from the request structure */ netsnmp_feature_child_of(tdata_extract_container, table_tdata_all) #ifndef NETSNMP_FEATURE_REMOVE_TDATA_EXTRACT_CONTAINER netsnmp_container * netsnmp_tdata_extract_container(netsnmp_request_info *request) { netsnmp_tdata *tdata = (netsnmp_tdata*) netsnmp_request_get_list_data(request, TABLE_TDATA_TABLE); return ( tdata ? tdata->container : NULL ); } #endif /* NETSNMP_FEATURE_REMOVE_TDATA_EXTRACT_CONTAINER */ /** extracts the tdata row being accessed from the request structure */ netsnmp_tdata_row * netsnmp_tdata_extract_row(netsnmp_request_info *request) { return (netsnmp_tdata_row *) netsnmp_container_table_row_extract(request); } /** extracts the (table-specific) entry being accessed from the * request structure */ void * netsnmp_tdata_extract_entry(netsnmp_request_info *request) { netsnmp_tdata_row *row = (netsnmp_tdata_row *) netsnmp_tdata_extract_row(request); if (row) return row->data; else return NULL; } #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_INSERT_ROW /** inserts a newly created tdata row into a request */ NETSNMP_INLINE void netsnmp_insert_tdata_row(netsnmp_request_info *request, netsnmp_tdata_row *row) { netsnmp_container_table_row_insert(request, (netsnmp_index *)row); } #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_INSERT_ROW */ #ifndef NETSNMP_FEATURE_REMOVE_TABLE_TDATA_REMOVE_ROW /** inserts a newly created tdata row into a request */ NETSNMP_INLINE void netsnmp_remove_tdata_row(netsnmp_request_info *request, netsnmp_tdata_row *row) { netsnmp_container_table_row_remove(request, (netsnmp_index *)row); } #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA_REMOVE_ROW */ /* ================================== * * Generic API: Row operations * * ================================== */ /** returns the (table-specific) entry data for a given row */ void * netsnmp_tdata_row_entry( netsnmp_tdata_row *row ) { if (row) return row->data; else return NULL; } /** returns the first row in the table */ netsnmp_tdata_row * netsnmp_tdata_row_first(netsnmp_tdata *table) { return (netsnmp_tdata_row *)CONTAINER_FIRST( table->container ); } /** finds a row in the 'tdata' table given another row */ netsnmp_tdata_row * netsnmp_tdata_row_get( netsnmp_tdata *table, netsnmp_tdata_row *row) { return (netsnmp_tdata_row*)CONTAINER_FIND( table->container, row ); } /** returns the next row in the table */ netsnmp_tdata_row * netsnmp_tdata_row_next( netsnmp_tdata *table, netsnmp_tdata_row *row) { return (netsnmp_tdata_row *)CONTAINER_NEXT( table->container, row ); } /** finds a row in the 'tdata' table given the index values */ netsnmp_tdata_row * netsnmp_tdata_row_get_byidx(netsnmp_tdata *table, netsnmp_variable_list *indexes) { oid searchfor[ MAX_OID_LEN]; size_t searchfor_len = MAX_OID_LEN; build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, indexes); return netsnmp_tdata_row_get_byoid(table, searchfor, searchfor_len); } /** finds a row in the 'tdata' table given the index OID */ netsnmp_tdata_row * netsnmp_tdata_row_get_byoid(netsnmp_tdata *table, oid * searchfor, size_t searchfor_len) { netsnmp_index index; if (!table) return NULL; index.oids = searchfor; index.len = searchfor_len; return (netsnmp_tdata_row*)CONTAINER_FIND( table->container, &index ); } /** finds the lexically next row in the 'tdata' table given the index values */ netsnmp_tdata_row * netsnmp_tdata_row_next_byidx(netsnmp_tdata *table, netsnmp_variable_list *indexes) { oid searchfor[ MAX_OID_LEN]; size_t searchfor_len = MAX_OID_LEN; build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, indexes); return netsnmp_tdata_row_next_byoid(table, searchfor, searchfor_len); } /** finds the lexically next row in the 'tdata' table given the index OID */ netsnmp_tdata_row * netsnmp_tdata_row_next_byoid(netsnmp_tdata *table, oid * searchfor, size_t searchfor_len) { netsnmp_index index; if (!table) return NULL; index.oids = searchfor; index.len = searchfor_len; return (netsnmp_tdata_row*)CONTAINER_NEXT( table->container, &index ); } netsnmp_feature_child_of(tdata_row_count, table_tdata_all) #ifndef NETSNMP_FEATURE_REMOVE_TDATA_ROW_COUNT int netsnmp_tdata_row_count(netsnmp_tdata *table) { if (!table) return 0; return CONTAINER_SIZE( table->container ); } #endif /* NETSNMP_FEATURE_REMOVE_TDATA_ROW_COUNT */ /* ================================== * * Generic API: Index operations on a 'tdata' table * * ================================== */ /** compare a row with the given index values */ netsnmp_feature_child_of(tdata_compare_idx, table_tdata_all) #ifndef NETSNMP_FEATURE_REMOVE_TDATA_COMPARE_IDX int netsnmp_tdata_compare_idx(netsnmp_tdata_row *row, netsnmp_variable_list *indexes) { oid searchfor[ MAX_OID_LEN]; size_t searchfor_len = MAX_OID_LEN; build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, indexes); return netsnmp_tdata_compare_oid(row, searchfor, searchfor_len); } #endif /* NETSNMP_FEATURE_REMOVE_TDATA_COMPARE_IDX */ /** compare a row with the given index OID */ int netsnmp_tdata_compare_oid(netsnmp_tdata_row *row, oid * compareto, size_t compareto_len) { netsnmp_index *index = (netsnmp_index *)row; return snmp_oid_compare( index->oids, index->len, compareto, compareto_len); } int netsnmp_tdata_compare_subtree_idx(netsnmp_tdata_row *row, netsnmp_variable_list *indexes) { oid searchfor[ MAX_OID_LEN]; size_t searchfor_len = MAX_OID_LEN; build_oid_noalloc(searchfor, MAX_OID_LEN, &searchfor_len, NULL, 0, indexes); return netsnmp_tdata_compare_subtree_oid(row, searchfor, searchfor_len); } int netsnmp_tdata_compare_subtree_oid(netsnmp_tdata_row *row, oid * compareto, size_t compareto_len) { netsnmp_index *index = (netsnmp_index *)row; return snmp_oidtree_compare( index->oids, index->len, compareto, compareto_len); } #else /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA */ netsnmp_feature_unused(table_tdata); #endif /* NETSNMP_FEATURE_REMOVE_TABLE_TDATA */ /** @} */