4444
4545#include "config.h"
4646
47+ #define ASN_LOOKUP_STRING "autonomous_system_number"
48+
4749/** Plugin description */
4850IPX_API struct ipx_plugin_info ipx_plugin_info = {
4951 // Plugin type
@@ -81,6 +83,234 @@ struct ipx_modifier_field asn_fields[] = {
8183 [ASN_DST ] = {.id = 17 , .length = 4 , .en = 0 } // bgpDestinationASNumber
8284};
8385
86+ /**
87+ * \brief Get the autonomous system (AS) number from database
88+ *
89+ * Function takes IP address, tries to find it in ASN database and return AS number.
90+ * If number is not found, 0 is returned. If any error occurs, \p err is set to non-zero value.
91+ * \param[in] db ASN database
92+ * \param[in] address IP address to search for
93+ * \param[out] err MMDB error code
94+ * \return AS number if successful
95+ * \return 0 (reserved AS number) if number was not found
96+ */
97+ static uint32_t
98+ mmdb_lookup (MMDB_s * db , struct sockaddr * address , int * err )
99+ {
100+ assert (db != NULL && address != NULL && err != NULL );
101+
102+ int rc = MMDB_SUCCESS ;
103+ MMDB_lookup_result_s result ;
104+
105+ // Search for address
106+ result = MMDB_lookup_sockaddr (db , address , & rc );
107+ if (rc ) {
108+ * err = rc ;
109+ return 0 ;
110+ }
111+
112+ // IP address not found
113+ if (!result .found_entry ) {
114+ return 0 ;
115+ }
116+
117+ // Found address, look for AS number
118+ MMDB_entry_data_s data ;
119+ rc = MMDB_get_value (& (result .entry ), & data , ASN_LOOKUP_STRING , NULL );
120+ if (rc ) {
121+ * err = rc ;
122+ return 0 ;
123+ }
124+
125+ // Address was not found
126+ if (!data .has_data ) {
127+ return 0 ;
128+ }
129+
130+ return data .uint32 ;
131+ }
132+
133+ /**
134+ * \brief Fill output buffer with AS number from ASN databse
135+ *
136+ * \param[in] db ASN database
137+ * \param[in] address IP address
138+ * \param[in] length Length of address (4 or 16)
139+ * \return #IPX_OK if successful
140+ * \return #IPX_ERR_ARG if argument was not valid IP address
141+ * \return #IPX_ERR_DENIED if MMDB error occured
142+ */
143+ static int
144+ get_asn (MMDB_s * db , struct ipx_modifier_output * out , uint8_t * address , size_t length )
145+ {
146+ int rc ;
147+ uint32_t asn ;
148+ struct sockaddr * addr ;
149+
150+ if (length == 4 ) {
151+ // IPv4 address
152+ struct sockaddr_in addr_4 = {
153+ .sin_family = AF_INET ,
154+ .sin_addr .s_addr = * ((uint32_t * )address )
155+ };
156+ addr = (struct sockaddr * ) & addr_4 ;
157+ } else if (length == 6 ) {
158+ // IPv6 address
159+ struct sockaddr_in6 addr_6 = {
160+ .sin6_family = AF_INET6
161+ };
162+ memcpy (& (addr_6 .sin6_addr ), address , 16 );
163+ addr = (struct sockaddr * ) & addr_6 ;
164+ } else {
165+ // Unknown address format
166+ return IPX_ERR_ARG ;
167+ }
168+
169+ asn = mmdb_lookup (db , addr , & rc );
170+ if (rc ) {
171+ // MMDB error
172+ return IPX_ERR_DENIED ;
173+ }
174+
175+ int len = 4 ;
176+ memcpy (out -> raw , & asn , len );
177+ out -> length = len ;
178+ }
179+
180+
181+ /**
182+ * \brief Set output buffers with autonomous system numbers
183+ *
184+ * Output buffers are then used in modifier to modify given record with filled values.
185+ * Unknown AS numbers are skipped, indicated by negative length in output buffer item.
186+ * \param[in] rec Data record from IPFIX message
187+ * \param[out] output Output buffers
188+ * \param[in] data Callback data
189+ * \return #IPX_OK on success
190+ */
191+ int
192+ modifier_asn_callback (const struct fds_drec * rec , struct ipx_modifier_output output [], void * data )
193+ {
194+ int rc ;
195+ MMDB_s * db = (MMDB_s * ) data ;
196+
197+ // Source address
198+ struct fds_drec_field field ;
199+ if (fds_drec_find ((struct fds_drec * )rec , 0 , 8 , & field ) != FDS_EOC ) {
200+ // IPv4
201+ assert (field .size == 4 );
202+ rc = get_asn (db , & output [ASN_SRC ], field .data , 4 );
203+ } else if (fds_drec_find ((struct fds_drec * )rec , 0 , 27 , & field ) != FDS_EOC ) {
204+ // IPv6
205+ assert (field .size == 16 );
206+ rc = get_asn (db , & output [ASN_SRC ], field .data , 16 );
207+ }
208+
209+ if (rc ) {
210+ return rc ;
211+ }
212+
213+ // Destination address
214+ if (fds_drec_find ((struct fds_drec * )rec , 0 , 12 , & field ) != FDS_EOC ) {
215+ // IPv4
216+ assert (field .size == 4 );
217+ get_asn (db , & output [ASN_DST ], field .data , 4 );
218+ } else if (fds_drec_find ((struct fds_drec * )rec , 0 , 28 , & field ) != FDS_EOC ) {
219+ // IPv6
220+ assert (field .size == 16 );
221+ get_asn (db , & output [ASN_DST ], field .data , 16 );
222+ }
223+
224+ if (rc ) {
225+ return rc ;
226+ }
227+ }
228+
229+ /**
230+ * \brief Process session message
231+ *
232+ * In the event of closing session, information about the particular
233+ * Transport Session will be removed.
234+ * \param[in] ctx Plugin context
235+ * \param[in] modifier Modifier component
236+ * \param[in] msg Transport session message
237+ * \return Always #IPX_OK
238+ */
239+ static int
240+ process_session (ipx_ctx_t * ctx , ipx_modifier_t * modifier , ipx_msg_session_t * msg )
241+ {
242+ // Only process session close events
243+ if (ipx_msg_session_get_event (msg ) != IPX_MSG_SESSION_CLOSE ) {
244+ ipx_ctx_msg_pass (ctx , ipx_msg_session2base (msg ));
245+ return IPX_OK ;
246+ }
247+
248+ int rc ;
249+ const struct ipx_session * session = ipx_msg_session_get_session (msg );
250+
251+ ipx_msg_garbage_t * garbage ;
252+ if ((rc = ipx_modifier_remove_session (modifier , session , & garbage )) == IPX_OK ) {
253+ // No error occured, pass the original message
254+ ipx_ctx_msg_pass (ctx , ipx_msg_session2base (msg ));
255+
256+ /* Send garbage after session message because other plugins might have references to
257+ templates linked to that session */
258+ if (garbage == NULL ) {
259+ IPX_CTX_WARNING (ctx , "A memory allocation failed (%s:%d)." , __FILE__ , __LINE__ );
260+ return IPX_OK ;
261+ }
262+
263+ ipx_ctx_msg_pass (ctx , ipx_msg_garbage2base (garbage ));
264+ return IPX_OK ;
265+ }
266+
267+ // Possible internal errors
268+ switch (rc ) {
269+ case IPX_ERR_NOTFOUND :
270+ IPX_CTX_ERROR (ctx , "Received an event about closing of unknown Transport Session '%s'." ,
271+ session -> ident );
272+ break ;
273+ default :
274+ IPX_CTX_ERROR (ctx , "ipx_modifier_session_remove() returned an unexpected value (%s:%d, "
275+ "code: %d)." , __FILE__ , __LINE__ , rc );
276+ break ;
277+ }
278+
279+ return IPX_OK ;
280+ }
281+
282+ /**
283+ * \brief Process IPFIX message
284+ *
285+ * Iterate through all data records in message and modify them, store modified records
286+ * in new message which is passed instead of original message.
287+ * \param[in] ctx Plugin context
288+ * \param[in] modifier Modifier component
289+ * \param[in] builder Message builder
290+ * \param[in] msg IPFIX message
291+ * \return
292+ */
293+ static int
294+ process_ipfix (ipx_ctx_t * ctx , ipx_modifier_t * modifier , ipx_msg_builder_t * builder ,
295+ ipx_msg_ipfix_t * msg )
296+ {
297+
298+ ipx_msg_garbage_t * session_garbage ;
299+ ipx_modifier_add_session (modifier , msg , & session_garbage );
300+
301+ uint32_t rec_cnt = ipx_msg_ipfix_get_drec_cnt (msg );
302+
303+ struct ipx_ipfix_record * rec ;
304+ ipx_msg_garbage_t * ipfix_garbage ;
305+ for (uint32_t i = 0 ; i < rec_cnt ; i ++ ) {
306+ rec = ipx_msg_ipfix_get_drec (msg , i );
307+
308+ ipx_modifier_modify (modifier , & (rec -> rec ), & ipfix_garbage );
309+ }
310+
311+ return IPX_OK ;
312+ }
313+
84314// -------------------------------------------------------------------------------------------------
85315
86316int
@@ -111,7 +341,7 @@ ipx_plugin_init(ipx_ctx_t *ctx, const char *params)
111341 const char * ident = ipx_ctx_name_get (ctx );
112342 size_t fsize = sizeof (asn_fields ) / sizeof (* asn_fields );
113343
114- data -> modifier = ipx_modifier_create (asn_fields , fsize , NULL , iemgr , & verb , ident );
344+ data -> modifier = ipx_modifier_create (asn_fields , fsize , & ( data -> database ) , iemgr , & verb , ident );
115345 if (!data -> modifier ) {
116346 config_destroy (data -> config );
117347 MMDB_close (& (data -> database ));
@@ -137,6 +367,9 @@ ipx_plugin_init(ipx_ctx_t *ctx, const char *params)
137367 return IPX_ERR_DENIED ;
138368 }
139369
370+ // Set adder callback
371+ ipx_modifier_set_adder_cb (data -> modifier , & modifier_asn_callback );
372+
140373 return IPX_OK ;
141374}
142375
@@ -146,7 +379,7 @@ ipx_plugin_destroy(ipx_ctx_t *ctx, void *cfg)
146379 (void ) ctx ; // Suppress warnings
147380 struct instance_data * data = (struct instance_data * ) cfg ;
148381
149- /* Create garbage message when destroying modifier because orher plugins might be
382+ /* Create garbage message when destroying modifier because other plugins might be
150383 referencing templates in modifier */
151384
152385 ipx_msg_garbage_cb cb = (ipx_msg_garbage_cb ) & ipx_modifier_destroy ;
@@ -155,7 +388,7 @@ ipx_plugin_destroy(ipx_ctx_t *ctx, void *cfg)
155388 ipx_ctx_msg_pass (ctx , ipx_msg_garbage2base (gb_msg ));
156389 } else {
157390 /* If creating garbage message fails, don't destroy modifier (memory leak) because
158- of possible segfault */
391+ of possible segfault */
159392 IPX_CTX_WARNING (ctx , "Could not destroy modifier (%s)" , ipx_ctx_name_get (ctx ));
160393 }
161394
169402ipx_plugin_process (ipx_ctx_t * ctx , void * cfg , ipx_msg_t * msg )
170403{
171404 struct instance_data * data = (struct instance_data * ) cfg ;
405+ int rc ;
406+
407+ // Get message type, call appropriate functions for message type
408+ enum ipx_msg_type type = ipx_msg_get_type (msg );
409+ switch (type ) {
410+ case IPX_MSG_SESSION :
411+ rc = process_session (ctx , data -> modifier , ipx_msg_base2session (msg ));
412+ break ;
413+ case IPX_MSG_IPFIX :
414+ rc = process_ipfix (ctx , data -> modifier , data -> builder , ipx_msg_base2ipfix (msg ));
415+ break ;
416+ default :
417+ // Should not reach
418+ assert (false);
419+ }
420+
421+ if (rc ) {
422+ // Any problem occured in processing message
423+ return IPX_ERR_DENIED ;
424+ }
172425
173- // Always pass the message
174- ipx_ctx_msg_pass (ctx , msg );
175426 return IPX_OK ;
176427}
0 commit comments