Updated code base

This commit is contained in:
2024-10-20 22:41:56 +05:30
parent 3ae6a02a3e
commit 2d364cfda5
7 changed files with 237 additions and 211 deletions

View File

@@ -35,7 +35,7 @@ _udp_server_create_and_start(void* arg) {
struct sockaddr_in server_addr; struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET; server_addr.sin_family = AF_INET;
server_addr.sin_port = port_no; server_addr.sin_port = htons(port_no);
server_addr.sin_addr.s_addr = INADDR_ANY; server_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(udp_sock_fd, (struct sockaddr*)&server_addr, if (bind(udp_sock_fd, (struct sockaddr*)&server_addr,
@@ -110,25 +110,37 @@ send_udp_msg(char* dest_ip_addr,
int sock_fd) { int sock_fd) {
struct sockaddr_in dest; struct sockaddr_in dest;
struct hostent* host;
// Clear the dest structure
memset(&dest, 0, sizeof(dest));
dest.sin_family = AF_INET; dest.sin_family = AF_INET;
dest.sin_port = dest_port_no; dest.sin_port = htons(dest_port_no); // Convert port to network byte order
struct hostent* host = (struct hostent*)gethostbyname(dest_ip_addr);
dest.sin_addr = *((struct in_addr*)host->h_addr_list); // Get the host by name
int addr_len = sizeof(struct sockaddr); host = gethostbyname(dest_ip_addr);
if (host == NULL) {
printf("Error resolving hostname: %s\n", dest_ip_addr);
return -1;
}
dest.sin_addr = *((struct in_addr*)host->h_addr_list[0]); // Assign IP address
if (sock_fd < 0) { if (sock_fd < 0) {
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); // Create UDP socket
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock_fd < 0) { if (sock_fd < 0) {
printf("socket creation failed, errno = %d\n", errno); printf("Socket creation failed, errno = %d\n", errno);
return -1; return -1;
} }
} }
sendto(sock_fd, msg, msg_size,
0, (struct sockaddr*)&dest, // Send the message
sizeof(struct sockaddr)); int bytes_sent = sendto(sock_fd, msg, msg_size, 0, (struct sockaddr*)&dest, sizeof(dest));
if (bytes_sent < 0) {
printf("Sendto failed, errno = %d\n", errno);
return -1;
}
return sock_fd; return sock_fd;
} }

View File

@@ -1,12 +1,13 @@
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <time.h> #include <time.h>
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include <stdio.h> #include <stdbool.h>
#include "utils.h" #include "utils.h"
#include "rt.h" #include "rt.h"
#include "stp_el.h"
#include "timerlib.h" #include "timerlib.h"
#include "stp_el.h"
rt_table_t* rt_table_t*
rt_create_new_rt_table(char* name) { rt_create_new_rt_table(char* name) {
@@ -18,6 +19,7 @@ rt_create_new_rt_table(char* name) {
static void static void
rt_entry_exp_timer_cbk(Timer_t* timer, void* arg) { rt_entry_exp_timer_cbk(Timer_t* timer, void* arg) {
rt_table_t* rt_table; rt_table_t* rt_table;
rt_table_entry_t* rt_table_entry = (rt_table_entry_t*)arg; rt_table_entry_t* rt_table_entry = (rt_table_entry_t*)arg;
@@ -47,7 +49,6 @@ rt_insert_new_entry(rt_table_t* rt,
rt_table_entry->rt_table = rt; rt_table_entry->rt_table = rt;
strncpy(rt_table_entry->dest, dest, 16); strncpy(rt_table_entry->dest, dest, 16);
rt_table_entry->mask = mask; rt_table_entry->mask = mask;
strncpy(rt_table_entry->gw, gw, 16); strncpy(rt_table_entry->gw, gw, 16);
@@ -56,7 +57,6 @@ rt_insert_new_entry(rt_table_t* rt,
rt_table_entry->prev = 0; rt_table_entry->prev = 0;
time(&rt_table_entry->last_updated_time); time(&rt_table_entry->last_updated_time);
if (exp_timer_in_millisec) { if (exp_timer_in_millisec) {
rt_table_entry->exp_timer = setup_timer( rt_table_entry->exp_timer = setup_timer(
rt_entry_exp_timer_cbk, rt_entry_exp_timer_cbk,
@@ -151,6 +151,12 @@ rt_update_rt_entry(rt_table_t* rt,
rt_table_entry->mask == mask && rt_table_entry->mask == mask &&
strncmp(rt_table_entry->gw, new_gw, 16) == 0 && strncmp(rt_table_entry->gw, new_gw, 16) == 0 &&
strncmp(rt_table_entry->oif, new_oif, 32) == 0) { strncmp(rt_table_entry->oif, new_oif, 32) == 0) {
/* Refresh the timer */
if (rt_table_entry->exp_timer) {
restart_timer(rt_table_entry->exp_timer);
}
return -1; return -1;
} }
@@ -159,6 +165,13 @@ rt_update_rt_entry(rt_table_t* rt,
strncpy(rt_table_entry->gw, new_gw, 16); strncpy(rt_table_entry->gw, new_gw, 16);
strncpy(rt_table_entry->oif, new_oif, 32); strncpy(rt_table_entry->oif, new_oif, 32);
time(&rt_table_entry->last_updated_time); time(&rt_table_entry->last_updated_time);
/* Refresh the timer */
if (rt_table_entry->exp_timer) {
restart_timer(rt_table_entry->exp_timer);
}
return 0; return 0;
} }
@@ -178,13 +191,13 @@ rt_display_rt_table(rt_table_t* rt) {
uptime_in_seconds = (unsigned int)difftime( uptime_in_seconds = (unsigned int)difftime(
curr_time, rt_table_entry->last_updated_time); curr_time, rt_table_entry->last_updated_time);
printf("%d. %-18s %-4d %-18s %-18s ", i, printf("%d. %-18s %-4d %-18s %-18s", i,
rt_table_entry->dest, rt_table_entry->dest,
rt_table_entry->mask, rt_table_entry->mask,
rt_table_entry->gw, rt_table_entry->gw,
rt_table_entry->oif); rt_table_entry->oif);
printf("Last updated : %s\n", hrs_min_sec_format(uptime_in_seconds));
printf("Last updated : %s ", hrs_min_sec_format(uptime_in_seconds)),
printf("Exp time : %lu\n", printf("Exp time : %lu\n",
rt_table_entry->exp_timer ? \ rt_table_entry->exp_timer ? \
timer_get_time_remaining_in_mill_sec(rt_table_entry->exp_timer) : 0); timer_get_time_remaining_in_mill_sec(rt_table_entry->exp_timer) : 0);

View File

@@ -6,9 +6,9 @@ typedef struct rt_table_ rt_table_t;
typedef struct rt_table_entry_ rt_table_entry_t; typedef struct rt_table_entry_ rt_table_entry_t;
typedef struct Timer_ Timer_t; typedef struct Timer_ Timer_t;
#define RT_ENTRY_EXP_TIMER 30 // 30 sec #define RT_ENTRY_EXP_TIMER 30 // 30 sec
#define ROUTE_CREATE 1 #define ROUTE_CREATE 1
#define ROUTE_UPDATE 2 #define ROUTE_UPDATE 2
#define ROUTE_DELETE 3 #define ROUTE_DELETE 3
@@ -20,12 +20,11 @@ struct rt_table_entry_ {
char gw[16]; char gw[16];
char oif[32]; char oif[32];
time_t last_updated_time; time_t last_updated_time;
Timer_t* exp_timer;
struct rt_table_entry_* next; struct rt_table_entry_* next;
struct rt_table_entry_* prev; struct rt_table_entry_* prev;
rt_table_t* rt_table; /* back ptr to owning rt table*/
Timer_t* exp_timer;
int exp_timer_msec; int exp_timer_msec;
rt_table_t* rt_table; /* back ptr to owning rt table*/
}; };
struct rt_table_ { struct rt_table_ {

View File

@@ -6,8 +6,9 @@
#include "rt.h" #include "rt.h"
#include "stp_el.h" #include "stp_el.h"
/* Import external global variable */
extern event_loop_t el; extern event_loop_t el;
rt_table_t* rt_table = NULL; rt_table_t* rt_table = NULL;
int int
@@ -37,7 +38,7 @@ pkt_process_fn(char* msg_recvd, uint32_t msg_size, char* sender_ip, uint32_t por
uint32_t* cmd_code = (uint32_t*)msg_recvd; uint32_t* cmd_code = (uint32_t*)msg_recvd;
rt_table_entry_t* rt_entry = (rt_table_entry_t*)(cmd_code + 1); rt_table_entry_t* rt_entry = (rt_table_entry_t*)(cmd_code + 1);
rt_entry->exp_timer_msec = RT_ENTRY_EXP_TIMER * 1000; rt_entry->exp_timer_msec = RT_ENTRY_EXP_TIMER * 1000;
//stp_update_routing_table(rt_table, *cmd_code, rt_entry);
el_stp_update_routing_table(rt_table, *cmd_code, rt_entry); el_stp_update_routing_table(rt_table, *cmd_code, rt_entry);
} }
@@ -48,8 +49,7 @@ cli_handler(int choice)
{ {
case 1: case 1:
//rt_display_rt_table(rt_table); //rt_display_rt_table(rt_table);
rt_display_rt_table_preemption_conext_save(rt_table);
rt_display_rt_table_preemption_context_save(rt_table);
printf("\n\n"); printf("\n\n");
break; break;
case 2: case 2:
@@ -61,6 +61,8 @@ cli_handler(int choice)
scanf("%s", rt_entry.gw); scanf("%s", rt_entry.gw);
printf("Enter OIF name : "); printf("Enter OIF name : ");
scanf("%s", rt_entry.oif); scanf("%s", rt_entry.oif);
rt_entry.exp_timer_msec = 0; /* rt entries created by CLI must never expire*/
//stp_update_routing_table(rt_table, ROUTE_CREATE, &rt_entry);
el_stp_update_routing_table(rt_table, ROUTE_CREATE, &rt_entry); el_stp_update_routing_table(rt_table, ROUTE_CREATE, &rt_entry);
} }
break; break;
@@ -72,10 +74,13 @@ cli_handler(int choice)
rt_table_entry_t rt_entry; rt_table_entry_t rt_entry;
printf("Enter Dest Address : "); printf("Enter Dest Address : ");
scanf("%s", rt_entry.dest); scanf("%s", rt_entry.dest);
if (el_stp_update_routing_table(rt_table, ROUTE_DELETE, &rt_entry)) #if 0
if (stp_update_routing_table(rt_table, ROUTE_DELETE, &rt_entry))
{ {
printf("No Such entry\n"); printf("No Such entry\n");
} }
#endif
el_stp_update_routing_table(rt_table, ROUTE_DELETE, &rt_entry);
} }
break; break;
case 5: case 5:
@@ -89,16 +94,14 @@ cli_handler(int choice)
break; break;
case 6: case 6:
{ {
rt_table_entry_t rt_entry_template; rt_table_entry_t rt_entry_tmplate;
printf("Entry Dest Address : "); printf("Enter Dest Address : ");
scanf("%s", rt_entry_template.dest); scanf("%s", rt_entry_tmplate.dest);
el_stp_serialize_and_send_rt_entry(rt_table, &rt_entry_template); el_stp_serialize_and_send_rt_entry(rt_table, &rt_entry_tmplate);
} }
break; break;
case 7: case 7:
{
el_stp_delete_rt_table(rt_table); el_stp_delete_rt_table(rt_table);
}
break; break;
case 8: case 8:
exit(0); exit(0);
@@ -116,6 +119,8 @@ main(int argc, char** argv) {
printf(" New Routing Table Created\n"); printf(" New Routing Table Created\n");
} }
stp_init_el(&el);
for (;;) { for (;;) {
printf("Main Menu\n"); printf("Main Menu\n");

View File

@@ -6,10 +6,10 @@
#include <stdio.h> #include <stdio.h>
#include "utils.h" #include "utils.h"
#include "network_utils.h" #include "network_utils.h"
#include "stp_el.h"
#include "timerlib.h" #include "timerlib.h"
#include "stp_el.h"
/* Take Event loop global variable */ /* Take Event Loop global variable */
event_loop_t el; event_loop_t el;
/* Import external function */ /* Import external function */
@@ -18,6 +18,7 @@ stp_update_routing_table(rt_table_t* rt_table, uint32_t cmd_code, rt_table_entry
void void
stp_init_el(event_loop_t* el) { stp_init_el(event_loop_t* el) {
event_loop_init(el); event_loop_init(el);
event_loop_run(el); event_loop_run(el);
} }
@@ -38,11 +39,12 @@ el_stp_update_routing_table_cbk(void* arg) {
} }
task_t* task_t*
el_stp_update_routing_table(rt_table_t* rt, int cmd_code, rt_table_entry_t* rt_entry) { el_stp_update_routing_table(rt_table_t* rt_table, int cmd_code, rt_table_entry_t* rt_entry) {
el_rt_table_update_data_t* el_rt_table_update_data = el_rt_table_update_data_t* el_rt_table_update_data =
(el_rt_table_update_data_t*)calloc(1, sizeof(el_rt_table_update_data_t)); (el_rt_table_update_data_t*)calloc(1, sizeof(el_rt_table_update_data_t));
el_rt_table_update_data->rt_table = rt; el_rt_table_update_data->rt_table = rt_table;
el_rt_table_update_data->cmd_code = cmd_code; el_rt_table_update_data->cmd_code = cmd_code;
el_rt_table_update_data->rt_entry = (rt_table_entry_t*)calloc(1, sizeof(rt_table_entry_t)); el_rt_table_update_data->rt_entry = (rt_table_entry_t*)calloc(1, sizeof(rt_table_entry_t));
@@ -52,19 +54,21 @@ el_stp_update_routing_table(rt_table_t* rt, int cmd_code, rt_table_entry_t* rt_e
task_t* task = task_create_new_job(&el, task_t* task = task_create_new_job(&el,
el_stp_update_routing_table_cbk, el_stp_update_routing_table_cbk,
(void*)el_rt_table_update_data, cmd_code == ROUTE_DELETE ? TASK_PRIORITY_LOW : TASK_PRIORITY_MEDIUM); (void*)el_rt_table_update_data,
cmd_code == ROUTE_DELETE ? TASK_PRIORITY_LOW : TASK_PRIORITY_MEDIUM);
return task; return task;
} }
typedef struct rt_table_print_cntxt_ { typedef struct rt_table_print_cntxt_ {
int i; int i;
rt_table_entry_t* rt_table_entry; rt_table_entry_t* rt_table_entry;
}rt_table_print_cntxt_t;
} rt_table_print_cntxt_t;
static EL_RES_T static EL_RES_T
rt_display_rt_table_preemption_context_save_cbk(void* arg) { rt_display_rt_table_preemption_conext_save_cbk(void* arg) {
time_t curr_time = time(NULL); time_t curr_time = time(NULL);
unsigned int uptime_in_seconds = 0; unsigned int uptime_in_seconds = 0;
@@ -76,25 +80,24 @@ rt_display_rt_table_preemption_context_save_cbk(void* arg) {
for (; rt_table_entry; for (; rt_table_entry;
rt_table_entry = rt_table_entry->next) { rt_table_entry = rt_table_entry->next) {
uptime_in_seconds = (unsigned int)difftime( uptime_in_seconds = (unsigned int)difftime(
curr_time, rt_table_entry->last_updated_time); curr_time, rt_table_entry->last_updated_time);
printf("%d. %-18s %-4d %18s %-18s", i, printf("%d. %-18s %-4d %-18s %-18s", i,
rt_table_entry->dest, rt_table_entry->dest,
rt_table_entry->mask, rt_table_entry->mask,
rt_table_entry->gw, rt_table_entry->gw,
rt_table_entry->oif); rt_table_entry->oif);
printf("Last updated : %s ", hrs_min_sec_format(uptime_in_seconds)); printf("Last updated : %s ", hrs_min_sec_format(uptime_in_seconds)),
printf("Exp time : %lu\n", printf("Exp time : %lu\n",
rt_table_entry->exp_timer ? \ rt_table_entry->exp_timer ? \
timer_get_time_remaining_in_mill_sec(rt_table_entry->exp_timer) : 0); timer_get_time_remaining_in_mill_sec(rt_table_entry->exp_timer) : 0);
i++; i++;
/* Save the context */ /* Save the context*/
if (i % 10 == 0 && rt_table_entry->next) { if (i % 10 == 0 && rt_table_entry->next) {
cntxt->rt_table_entry = rt_table_entry->next; cntxt->rt_table_entry = rt_table_entry->next;
cntxt->i = i++; cntxt->i = i++;
return EL_CONTINUE; return EL_CONTINUE;
@@ -105,8 +108,9 @@ rt_display_rt_table_preemption_context_save_cbk(void* arg) {
return EL_FINISH; return EL_FINISH;
} }
void void
rt_display_rt_table_preemption_context_save(rt_table_t* rt) { rt_display_rt_table_preemption_conext_save(rt_table_t* rt) {
rt_table_entry_t* rt_table_entry = rt->head; rt_table_entry_t* rt_table_entry = rt->head;
@@ -121,24 +125,24 @@ rt_display_rt_table_preemption_context_save(rt_table_t* rt) {
cntxt->rt_table_entry = rt_table_entry; cntxt->rt_table_entry = rt_table_entry;
task_create_new_job(&el, task_create_new_job(&el,
rt_display_rt_table_preemption_context_save_cbk, rt_display_rt_table_preemption_conext_save_cbk,
(void*)cntxt, TASK_PRIORITY_HIGH); (void*)cntxt, TASK_PRIORITY_HIGH);
} }
static EL_RES_T static EL_RES_T
rt_entry_serialize_and_send_task_cbk(void* arg) { rt_entry_serlialize_and_send_task_cbk(void* arg) {
rt_table_entry_t* rt_entry = (rt_table_entry_t*)arg; rt_table_entry_t* rt_entry = (rt_table_entry_t*)arg;
/* look up RT Entry in RT table */ /* look up RT entry in RT table*/
rt_table_entry_t* actual_rt_entry = rt_look_up_rt_table_entry( rt_table_entry_t* actual_rt_entry = rt_look_up_rt_table_entry(
rt_entry->rt_table, rt_entry->rt_table,
rt_entry->dest, rt_entry->dest,
rt_entry->mask rt_entry->mask);
);
if (!actual_rt_entry) { if (!actual_rt_entry) {
printf("Serialize Task : RT Entry do not exist\n"); printf("Serialize Task : RT Entry do not exist\n");
free(rt_entry); free(rt_entry);
return EL_FINISH; return EL_FINISH;
@@ -166,33 +170,34 @@ rt_entry_serialize_and_send_task_cbk(void* arg) {
free(msg_to_send); free(msg_to_send);
free(rt_entry); free(rt_entry);
return EL_FINISH; return EL_FINISH;
} }
void void
el_stp_serialize_and_send_rt_entry(rt_table_t* rt, rt_table_entry_t* rt_entry_template) { el_stp_serialize_and_send_rt_entry(rt_table_t* rt_table, rt_table_entry_t* rt_entry_tmplate) {
/* This new rt_entry serves the purpose of context-save structure for task */
/* This new rt_entry serves the purpose of context-save structure for this task*/
rt_table_entry_t* rt_entry = (rt_table_entry_t*)calloc(1, sizeof(rt_table_entry_t)); rt_table_entry_t* rt_entry = (rt_table_entry_t*)calloc(1, sizeof(rt_table_entry_t));
strncpy(rt_entry->dest, rt_entry_tmplate->dest, 16);
strncpy(rt_entry->dest, rt_entry_template->dest, 16);
rt_entry->mask = 32; rt_entry->mask = 32;
rt_entry->rt_table = rt; rt_entry->rt_table = rt_table;
task_create_new_job(&el, task_create_new_job(&el,
rt_entry_serialize_and_send_task_cbk, rt_entry_serlialize_and_send_task_cbk,
(void*)rt_entry, TASK_PRIORITY_MEDIUM); (void*)rt_entry, TASK_PRIORITY_MEDIUM);
} }
typedef struct rt_table_delete_context_ { typedef struct rt_table_delete_context_ {
rt_table_entry_t* rt_entry; rt_table_entry_t* rt_entry;
}rt_table_delete_context_t;
} rt_table_delete_context_t;
EL_RES_T EL_RES_T
rt_table_delete_cbk(void* arg) { rt_table_delete_cbk(void* arg) {
int i = 0; int i = 0;
rt_table_delete_context_t* cntxt = (rt_table_delete_context_t*)arg; rt_table_delete_context_t* cntxt = (rt_table_delete_context_t*)arg;
@@ -203,6 +208,7 @@ rt_table_delete_cbk(void* arg) {
printf("Resuming rt_table deletion Task\n"); printf("Resuming rt_table deletion Task\n");
do { do {
rt_entry_next = rt_entry->next; rt_entry_next = rt_entry->next;
if (rt_entry->exp_timer) { if (rt_entry->exp_timer) {
@@ -220,8 +226,8 @@ rt_table_delete_cbk(void* arg) {
return EL_CONTINUE; return EL_CONTINUE;
} }
rt_entry = rt_entry_next; rt_entry = rt_entry_next;
} while (rt_entry); } while (rt_entry);
free(cntxt); free(cntxt);
@@ -233,12 +239,11 @@ void
el_stp_delete_rt_table(rt_table_t* rt_table) { el_stp_delete_rt_table(rt_table_t* rt_table) {
/* This is the case when we need to delete a container data structures. /* This is the case when we need to delete a container data structures.
Container data structures must be isolated from remaining application data structures first */ Container data structures must be isloted from remaining application data structures first*/
if (!rt_table) return; if (!rt_table) return;
if (!rt_table->head) return; if (!rt_table->head) return;
/* Isolate RT Table */ /* Isolate RT Table*/
rt_table_entry_t* rt_entry = rt_table->head; rt_table_entry_t* rt_entry = rt_table->head;
rt_table->head = NULL; rt_table->head = NULL;

View File

@@ -1,32 +1,30 @@
#ifndef __STP_EL__ #ifndef __STP_EL__
#define __STP_EL__ #define __STP_EL__
#include <event_loop.h> #include "event_loop.h"
#include "rt.h" #include "rt.h"
/* Data structure to pack all argument to one single argument */ /* Data Structure to pack all arguments as one single argument */
typedef struct el_rt_table_update_data_ { typedef struct el_rt_table_update_data_ {
rt_table_t* rt_table; rt_table_t* rt_table;
int cmd_code; int cmd_code;
rt_table_entry_t* rt_entry; rt_table_entry_t* rt_entry;
}el_rt_table_update_data_t; } el_rt_table_update_data_t;
void void
stp_init_el(event_loop_t* el); stp_init_el(event_loop_t* el);
/* Now create APIs' to update routing table using Event loop */ /* Now create an API to update routing table using Event Loop */
task_t* task_t*
el_stp_update_routing_table(rt_table_t* rt, int cmd_code, rt_table_entry_t* rt_entry); el_stp_update_routing_table(rt_table_t* rt, int cmd_code, rt_table_entry_t* rt_entry);
void void
rt_display_rt_table_preemption_context_save(rt_table_t* rt); rt_display_rt_table_preemption_conext_save(rt_table_t* rt);
void void
el_stp_serialize_and_send_rt_entry(rt_table_t* rt, rt_table_entry_t* rt_entry_template); el_stp_serialize_and_send_rt_entry(rt_table_t* rt, rt_table_entry_t* rt_entry);
void void
el_stp_delete_rt_table(rt_table_t* rt_table); el_stp_delete_rt_table(rt_table_t* rt_table);

View File

@@ -42,18 +42,12 @@ typedef struct Timer_ {
* return a pointer to Timer object*/ * return a pointer to Timer object*/
Timer_t* Timer_t*
setup_timer( setup_timer(
/* Timer Callback with user data and user size*/ void (*timer_cb)(Timer_t*, void*), /* Timer Callback with user data*/
void (*)(Timer_t*, void*), unsigned long exp_timer, /* First expiration time interval in msec */
/* First expiration time interval in msec */ unsigned long sec_exp_timer, /* Subsequent expiration time interval in msec */
unsigned long, uint32_t threshold, /* Max no of expirations, 0 for infinite*/
/* Subsequent expiration time interval in msec */ void* user_arg, /* Arg to timer callback */
unsigned long, bool exponential_backoff);
/* Max no of expirations, 0 for infinite*/
uint32_t,
/* Arg to timer callback */
void*,
/* Is timer Exp back off */
bool);
static inline void static inline void
timer_delete_user_data(Timer_t* timer) { timer_delete_user_data(Timer_t* timer) {