Step 2a (2/2): LLDP transmit, switch shows name and IP
- aes67_net/lldp.c: IEEE 802.1AB LLDPDU sent as a raw frame with esp_eth_transmit every 30 s (TTL 120 s) and immediately on a new IP or hostname change. TLVs: chassis/port ID (MAC), port description, system name (net.hostname), system description (project + version), capabilities station-only, IPv4 management address. - Docs: LLDP in the core Network section; step 2a ticked. - Verified on an HP 2530-8G-PoE+ (show lldp info remote-device 2): all fields shown, management address 192.168.192.244. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
idf_component_register(SRCS "aes67_net.c"
|
||||
idf_component_register(SRCS "aes67_net.c" "lldp.c"
|
||||
INCLUDE_DIRS "include"
|
||||
REQUIRES esp_eth esp_netif esp_event
|
||||
PRIV_REQUIRES aes67_web lwip espressif__mdns)
|
||||
PRIV_REQUIRES aes67_web lwip espressif__mdns esp_app_format esp_timer)
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "esp_event.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_netif.h"
|
||||
#include "lldp.h"
|
||||
#include "mdns.h"
|
||||
|
||||
static const char *TAG = "net";
|
||||
@@ -58,6 +59,7 @@ static void ip_event(void *arg, esp_event_base_t base, int32_t id, void *data)
|
||||
const ip_event_got_ip_t *ev = data;
|
||||
ESP_LOGI(TAG, "got IP " IPSTR " mask " IPSTR " gw " IPSTR,
|
||||
IP2STR(&ev->ip_info.ip), IP2STR(&ev->ip_info.netmask), IP2STR(&ev->ip_info.gw));
|
||||
lldp_changed();
|
||||
}
|
||||
|
||||
// Static addressing needs ip + mask; gw/dns are optional (none on the AES67 side with VLAN split).
|
||||
@@ -129,6 +131,7 @@ static void net_apply(const cJSON *g)
|
||||
if (mdns_hostname_set(host) == ESP_OK && mdns_instance_name_set(host) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "mDNS: now %s.local", host);
|
||||
}
|
||||
lldp_changed();
|
||||
}
|
||||
|
||||
static void net_status(cJSON *st)
|
||||
@@ -170,6 +173,7 @@ esp_err_t aes67_net_init(esp_eth_handle_t eth)
|
||||
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, ip_event, NULL));
|
||||
|
||||
mdns_setup();
|
||||
ESP_ERROR_CHECK(lldp_start(eth, s_netif));
|
||||
status_register(net_status);
|
||||
return esp_eth_start(eth);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
// LLDP (IEEE 802.1AB) transmit only: tells the switch our name and management IP.
|
||||
#include "lldp.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "aes67_cfg.h"
|
||||
#include "esp_app_desc.h"
|
||||
#include "esp_eth.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#define LLDP_INTERVAL_S 30
|
||||
#define LLDP_TTL_S (4 * LLDP_INTERVAL_S)
|
||||
#define FRAME_MAX 512
|
||||
#define FRAME_MIN 60 // without FCS
|
||||
|
||||
enum { TLV_END, TLV_CHASSIS_ID, TLV_PORT_ID, TLV_TTL, TLV_PORT_DESC, TLV_SYS_NAME, TLV_SYS_DESC,
|
||||
TLV_SYS_CAP, TLV_MGMT_ADDR };
|
||||
|
||||
static const char *TAG = "lldp";
|
||||
static const uint8_t LLDP_MCAST[6] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e };
|
||||
|
||||
static esp_eth_handle_t s_eth;
|
||||
static esp_netif_t *s_netif;
|
||||
static esp_timer_handle_t s_timer;
|
||||
static bool s_logged;
|
||||
|
||||
static size_t tlv(uint8_t *p, int type, const void *val, size_t len)
|
||||
{
|
||||
p[0] = (type << 1) | ((len >> 8) & 1);
|
||||
p[1] = len & 0xff;
|
||||
memcpy(p + 2, val, len);
|
||||
return 2 + len;
|
||||
}
|
||||
|
||||
static size_t tlv_str(uint8_t *p, int type, const char *s)
|
||||
{
|
||||
size_t len = strlen(s);
|
||||
return tlv(p, type, s, len > 255 ? 255 : len);
|
||||
}
|
||||
|
||||
void lldp_send(void)
|
||||
{
|
||||
if (!s_eth) {
|
||||
return;
|
||||
}
|
||||
uint8_t mac[6];
|
||||
if (esp_eth_ioctl(s_eth, ETH_CMD_G_MAC_ADDR, mac) != ESP_OK) {
|
||||
return;
|
||||
}
|
||||
cJSON *net = cfg_get("net");
|
||||
const char *host = cJSON_GetObjectItemCaseSensitive(net, "hostname")->valuestring;
|
||||
const esp_app_desc_t *app = esp_app_get_description();
|
||||
|
||||
uint8_t f[FRAME_MAX] = { 0 };
|
||||
size_t n = 0;
|
||||
memcpy(f, LLDP_MCAST, 6);
|
||||
memcpy(f + 6, mac, 6);
|
||||
f[12] = 0x88;
|
||||
f[13] = 0xcc;
|
||||
n = 14;
|
||||
|
||||
uint8_t id[7] = { 4 }; // chassis ID subtype 4: MAC address
|
||||
memcpy(id + 1, mac, 6);
|
||||
n += tlv(f + n, TLV_CHASSIS_ID, id, sizeof(id));
|
||||
id[0] = 3; // port ID subtype 3: MAC address
|
||||
n += tlv(f + n, TLV_PORT_ID, id, sizeof(id));
|
||||
uint8_t ttl[2] = { LLDP_TTL_S >> 8, LLDP_TTL_S & 0xff };
|
||||
n += tlv(f + n, TLV_TTL, ttl, sizeof(ttl));
|
||||
n += tlv_str(f + n, TLV_PORT_DESC, "eth0");
|
||||
n += tlv_str(f + n, TLV_SYS_NAME, host);
|
||||
char desc[128];
|
||||
snprintf(desc, sizeof(desc), "AES67 device, %s %s (ESP-IDF %s)", app->project_name, app->version, app->idf_ver);
|
||||
n += tlv_str(f + n, TLV_SYS_DESC, desc);
|
||||
uint8_t cap[4] = { 0x00, 0x80, 0x00, 0x80 }; // capabilities: station only (supported, enabled)
|
||||
n += tlv(f + n, TLV_SYS_CAP, cap, sizeof(cap));
|
||||
|
||||
esp_netif_ip_info_t ip = { 0 };
|
||||
bool have_ip = esp_netif_get_ip_info(s_netif, &ip) == ESP_OK && ip.ip.addr;
|
||||
if (have_ip) {
|
||||
// addr len 5, subtype IPv4, address, interface subtype ifIndex, ifIndex 1, OID len 0
|
||||
uint8_t m[12] = { 5, 1 };
|
||||
memcpy(m + 2, &ip.ip.addr, 4); // already network byte order
|
||||
m[6] = 2;
|
||||
m[10] = 1;
|
||||
n += tlv(f + n, TLV_MGMT_ADDR, m, sizeof(m));
|
||||
}
|
||||
n += tlv(f + n, TLV_END, NULL, 0);
|
||||
if (n < FRAME_MIN) {
|
||||
n = FRAME_MIN;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_eth_transmit(s_eth, f, n);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGW(TAG, "transmit failed: %s", esp_err_to_name(err));
|
||||
} else if (!s_logged || !have_ip) {
|
||||
// Log once per name/IP change, not every 30 s.
|
||||
ESP_LOGI(TAG, "sent: name %s, mgmt " IPSTR ", %u bytes", host, IP2STR(&ip.ip), (unsigned)n);
|
||||
s_logged = have_ip;
|
||||
}
|
||||
cJSON_Delete(net);
|
||||
}
|
||||
|
||||
void lldp_changed(void)
|
||||
{
|
||||
s_logged = false;
|
||||
lldp_send();
|
||||
}
|
||||
|
||||
static void timer_cb(void *arg)
|
||||
{
|
||||
lldp_send();
|
||||
}
|
||||
|
||||
esp_err_t lldp_start(esp_eth_handle_t eth, esp_netif_t *netif)
|
||||
{
|
||||
s_eth = eth;
|
||||
s_netif = netif;
|
||||
const esp_timer_create_args_t args = { .callback = timer_cb, .name = "lldp" };
|
||||
esp_err_t err = esp_timer_create(&args, &s_timer);
|
||||
if (err == ESP_OK) {
|
||||
err = esp_timer_start_periodic(s_timer, LLDP_INTERVAL_S * 1000000ULL);
|
||||
}
|
||||
return err;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_eth_driver.h"
|
||||
#include "esp_netif.h"
|
||||
|
||||
// Send an LLDPDU every 30 s (TTL 120 s).
|
||||
esp_err_t lldp_start(esp_eth_handle_t eth, esp_netif_t *netif);
|
||||
// Send now (e.g. after an IP or hostname change) and log the next one.
|
||||
void lldp_changed(void);
|
||||
void lldp_send(void);
|
||||
Reference in New Issue
Block a user