Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add initial support for SNMP Traps. #147

Merged
merged 6 commits into from
Mar 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,12 @@ Fanpico supports following commands:
* [SYStem:SNMP:LOCAtion?](#systemsnmplocation-1)
* [SYStem:SNMP:WRITecommunity](#systemsnmpwritecommunity)
* [SYStem:SNMP:WRITecommunity?](#systemsnmpwritecommunity-1)
* [SYStem:SNMP:TRAPs:AUTH](#systemsnmptrapsauth)
* [SYStem:SNMP:TRAPs:AUTH?](#systemsnmptrapsauth-1)
* [SYStem:SNMP:TRAPs:COMMunity](#systemsnmptrapscommunity)
* [SYStem:SNMP:TRAPs:COMMunity?](#systemsnmptrapscommunity-1)
* [SYStem:SNMP:TRAPs:DESTination](#systemsnmptrapsdestination)
* [SYStem:SNMP:TRAPs:DESTination?](#systemsnmptrapsdestination-1)
* [SYStem:SPI](#systemspi)
* [SYStem:SPI?](#systemspi-1)
* [SYStem:TELNET:SERVer](#systemtelnetserver)
Expand Down Expand Up @@ -3050,6 +3056,65 @@ private
```


#### SYStem:SNMP:TRAPs:AUTH
Enable or disable sending authentication traps.

Example:
```
SYS:SNMP:TRAP:AUTH ON
```

#### SYStem:SNMP:TRAPs:AUTH?
Display status of sending of authentication traps.

Example:
```
SYS:SNMP:TRAP:AUTH?
OFF
```


#### SYStem:SNMP:TRAP:COMMunity
Set SNMP traps community name. (Default: <one>)

To enable SNMP traps both trap community and trap destination (IP) must be set.

Example:
```
SYS:SNMP:TRAP:COMM public
```

#### SYStem:SNMP:TRAP:COMMunity?
Display current SNMP traps community name.

Example:
```
SYS:SNMP:TRAP:COMM?
public
```


#### SYStem:SNMP:TRAP:DESTination
Set destination IP address for sending SNMP traps to.

To disable sending traps, set IP to: 0.0.0.0

(Default: 0.0.0.0)

Example:
```
SYS:SNMP:TRAP:DEST 192.168.42.42
```

#### SYStem:SNMP:TRAP:DESTination?
Display current SNMP trap destination IP?

Example:
```
SYS:SNMP:TRAP:DEST?
192.168.42.42
```


#### SYStem:SPI
Enable or disable SPI bus (on boards that have "SPI" connector).
Expand Down
26 changes: 26 additions & 0 deletions src/command.c
Original file line number Diff line number Diff line change
Expand Up @@ -2691,6 +2691,24 @@ int cmd_snmp_location(const char *cmd, const char *args, int query, struct prev_
conf->snmp_location, sizeof(conf->snmp_location),
"SNMP sysLocation", NULL);
}

int cmd_snmp_auth_traps(const char *cmd, const char *args, int query, struct prev_cmd_t *prev_cmd)
{
return bool_setting(cmd, args, query, prev_cmd,
&conf->snmp_auth_traps, "SNMP Authentication Traps");
}

int cmd_snmp_community_trap(const char *cmd, const char *args, int query, struct prev_cmd_t *prev_cmd)
{
return string_setting(cmd, args, query, prev_cmd,
conf->snmp_community_trap, sizeof(conf->snmp_community_trap),
"SNMP Traps Community", NULL);
}

int cmd_snmp_trap_dst(const char *cmd, const char *args, int query, struct prev_cmd_t *prev_cmd)
{
return ip_change(cmd, args, query, prev_cmd, "SNMP Trap Destination", &conf->snmp_trap_dst);
}
#endif /* WIFI_SUPPOERT */

int cmd_time(const char *cmd, const char *args, int query, struct prev_cmd_t *prev_cmd)
Expand Down Expand Up @@ -3022,11 +3040,19 @@ const struct cmd_t mqtt_commands[] = {
{ 0, 0, 0, 0 }
};

const struct cmd_t snmp_trap_commands[] = {
{ "AUTH", 4, NULL, cmd_snmp_auth_traps },
{ "COMMunity", 4, NULL, cmd_snmp_community_trap },
{ "DESTination", 4, NULL, cmd_snmp_trap_dst },
{ 0, 0, 0, 0 }
};

const struct cmd_t snmp_commands[] = {
{ "AGENT", 5, NULL, cmd_snmp_agent },
{ "COMMunity", 4, NULL, cmd_snmp_community },
{ "CONTact", 4, NULL, cmd_snmp_contact },
{ "LOCAtion", 4, NULL, cmd_snmp_location },
{ "TRAPs", 4, snmp_trap_commands, NULL },
{ "WRITecommunity", 4, NULL, cmd_snmp_community_write },
{ 0, 0, 0, 0 }
};
Expand Down
20 changes: 20 additions & 0 deletions src/config.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,9 @@ void clear_config(struct fanpico_config *cfg)
strncopy(cfg->snmp_community_write, "private", sizeof(cfg->snmp_community_write));
cfg->snmp_contact[0] = 0;
cfg->snmp_location[0] = 0;
cfg->snmp_community_trap[0] = 0;
cfg->snmp_auth_traps = false;
ip_addr_set_any(0, &cfg->snmp_trap_dst);
#endif

mutex_exit(config_mutex);
Expand Down Expand Up @@ -794,6 +797,12 @@ cJSON *config_to_json(const struct fanpico_config *cfg)
cJSON_AddItemToObject(config, "snmp_contact", cJSON_CreateString(cfg->snmp_contact));
if (strlen(cfg->snmp_location) > 0)
cJSON_AddItemToObject(config, "snmp_location", cJSON_CreateString(cfg->snmp_location));
if (strlen(cfg->snmp_community_trap) > 0)
cJSON_AddItemToObject(config, "snmp_community_trap", cJSON_CreateString(cfg->snmp_community_trap));
if (cfg->snmp_auth_traps)
cJSON_AddItemToObject(config, "snmp_auth_traps", cJSON_CreateNumber(cfg->snmp_auth_traps));
if (!ip_addr_isany(&cfg->snmp_trap_dst))
cJSON_AddItemToObject(config, "snmp_trap_dst", cJSON_CreateString(ipaddr_ntoa(&cfg->snmp_trap_dst)));
#endif

/* Fan outputs */
Expand Down Expand Up @@ -1197,6 +1206,17 @@ int json_to_config(cJSON *config, struct fanpico_config *cfg)
if ((val = cJSON_GetStringValue(ref)))
strncopy(cfg->snmp_location, val, sizeof(cfg->snmp_location));
}
if ((ref = cJSON_GetObjectItem(config, "snmp_community_trap"))) {
if ((val = cJSON_GetStringValue(ref)))
strncopy(cfg->snmp_community_trap, val, sizeof(cfg->snmp_community_trap));
}
if ((ref = cJSON_GetObjectItem(config, "snmp_auth_traps"))) {
cfg->snmp_auth_traps = cJSON_GetNumberValue(ref);
}
if ((ref = cJSON_GetObjectItem(config, "snmp_trap_dst"))) {
if ((val = cJSON_GetStringValue(ref)))
ipaddr_aton(val, &cfg->snmp_trap_dst);
}
#endif

/* Fan output configurations */
Expand Down
47 changes: 35 additions & 12 deletions src/fanpico.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,16 +75,22 @@ static void init_persistent_memory()
struct persistent_memory_block *m = persistent_mem;
uint32_t crc;

if (m->id == PERSISTENT_MEMORY_ID) {
if (m->id == PERSISTENT_MEMORY_ID && m->len == sizeof(persistent_memory)) {
crc = xcrc32((unsigned char*)m, PERSISTENT_MEMORY_CRC_LEN, 0);
if (crc == m->crc32) {
printf("Found persistent memory block\n");
if (strlen(m->timezone) > 0) {
/* Restore timezone */
setenv("TZ", m->timezone, 1);
tzset();
}
if (m->saved_time.tv_sec > 0)
aon_timer_start(&m->saved_time);
if (m->uptime) {
m->prev_uptime = m->uptime;
update_persistent_memory_crc();
}
m->warmstart++;
update_persistent_memory_crc();
return;
}
printf("Found corrupt persistent memory block"
Expand All @@ -94,6 +100,7 @@ static void init_persistent_memory()
printf("Initializing persistent memory block...\n");
memset(m, 0, sizeof(*m));
m->id = PERSISTENT_MEMORY_ID;
m->len = sizeof(persistent_memory);
update_persistent_memory_crc();
}

Expand All @@ -115,6 +122,19 @@ void update_persistent_memory()
}
}

void update_persistent_memory_tz(const char *tz)
{
struct persistent_memory_block *m = persistent_mem;

if (mutex_enter_timeout_us(pmem_mutex, 100)) {
strncopy(m->timezone, tz, sizeof(m->timezone));
update_persistent_memory_crc();
mutex_exit(pmem_mutex);
} else {
log_msg(LOG_DEBUG, "update_persistent_memory_tz(): Failed to get pmem_mutex");
}
}


void boot_reason()
{
Expand All @@ -127,6 +147,7 @@ void boot_reason()

static void setup()
{
struct persistent_memory_block *m = persistent_mem;
char buf[32];
int i = 0;

Expand Down Expand Up @@ -172,10 +193,19 @@ static void setup()
printf("\n");

log_msg(LOG_NOTICE, "System starting...");
if (persistent_mem->prev_uptime) {
log_msg(LOG_NOTICE, "Uptime before soft reset: %llus\n",
persistent_mem->prev_uptime / 1000000);
if (m->prev_uptime) {
log_msg(LOG_NOTICE, "Uptime before soft reset: %llus (soft reset count: %lu)",
m->prev_uptime / 1000000, m->warmstart);
}

/* Setup timezone */
if (strlen(cfg->timezone) > 1) {
log_msg(LOG_NOTICE, "Set timezone: %s", cfg->timezone);
update_persistent_memory_tz(cfg->timezone);
setenv("TZ", cfg->timezone, 1);
tzset();
}

if (aon_timer_is_running()) {
struct timespec ts;
aon_timer_get_time(&ts);
Expand Down Expand Up @@ -227,13 +257,6 @@ static void setup()
/* Configure 1-Wire pins... */
setup_onewire_bus();

/* Setup timezone */
if (strlen(cfg->timezone) > 1) {
log_msg(LOG_NOTICE, "Set Timezone: %s", cfg->timezone);
setenv("TZ", cfg->timezone, 1);
tzset();
}

log_msg(LOG_NOTICE, "System initialization complete.");
}

Expand Down
10 changes: 9 additions & 1 deletion src/fanpico.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* fanpico.h
Copyright (C) 2021-2024 Timo Kokkonen <[email protected]>
Copyright (C) 2021-2025 Timo Kokkonen <[email protected]>

SPDX-License-Identifier: GPL-3.0-or-later

Expand Down Expand Up @@ -288,6 +288,9 @@ struct fanpico_config {
char snmp_community_write[32 + 1];
char snmp_contact[32 + 1];
char snmp_location[32 + 1];
char snmp_community_trap[32 + 1];
bool snmp_auth_traps;
ip_addr_t snmp_trap_dst;
#endif
/* Non-config items */
float vtemp[VSENSOR_MAX_COUNT];
Expand Down Expand Up @@ -322,9 +325,12 @@ struct fanpico_state {

struct persistent_memory_block {
uint32_t id;
uint32_t len;
struct timespec saved_time;
uint64_t uptime;
uint64_t prev_uptime;
uint32_t warmstart;
char timezone[64];
uint32_t crc32;
};

Expand Down Expand Up @@ -352,6 +358,7 @@ extern bool rebooted_by_watchdog;
extern mutex_t *state_mutex;
void update_display_state();
void update_persistent_memory();
void update_persistent_memory_tz(const char *tz);

/* bi_decl.c */
void set_binary_info();
Expand Down Expand Up @@ -441,6 +448,7 @@ void telnetserver_init();

/* snmp.c */
void fanpico_snmp_init();
void fanpico_snmp_startup_trap(bool warmstart);

#endif

Expand Down
Loading