From: Vincent Bernat Date: Fri, 26 Mar 2010 23:55:02 +0000 (+0100) Subject: Allow to set LLDP-MED POE-MDI TLV from lldpctl. X-Git-Tag: 0.5.1~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=009ddd234e6e8cd774daed03a1e6d96ff1929462;p=thirdparty%2Flldpd.git Allow to set LLDP-MED POE-MDI TLV from lldpctl. This adds a "-O" option to lldpctl. --- diff --git a/man/lldpctl.8 b/man/lldpctl.8 index 16221ea7..2ff5c426 100644 --- a/man/lldpctl.8 +++ b/man/lldpctl.8 @@ -24,6 +24,7 @@ .Op Fl d .Op Fl L Ar location .Op Fl P Ar policy +.Op Fl O Ar poe .Op Ar interface ... .Sh DESCRIPTION The @@ -244,7 +245,79 @@ these values mean.) .It Sy 1:0:1:500:6:46 Voice (1): not unknown (0), tagged (1), VLAN-ID 500, l2 prio Voice (6), DSCP 46 (EF, Expedited Forwarding) .It Sy 2:0:1:500:3:24 -Voice Signaling (2): not unknown (0), tagged (1), VLAN-ID 500, l2 prio Excellent Effort (3), DSCP 24 (CS3, Class Selector 3) +Voice Signaling (2): not unknown (0), tagged (1), VLAN-ID 500, l2 prio +Excellent Effort (3), DSCP 24 (CS3, Class Selector 3) +.El +.El +.Pp +.It Fl O Ar poe +Enable the transmission of LLDP-MED POE-MDI TLV for the given +interfaces. One can act as a PD (power consumer) or a PSE (power +provider). No check is done on the validity of the parameters while +LLDP-MED requires some restrictions: +.Bl -bullet +.It +PD shall never request more power than physical 802.3af class. +.It +PD shall never draw more than the maximum power advertised by PSE. +.It +PSE shall not reduce power allocated to PD when this power is in use. +.It +PSE may request reduced power using conservation mode +.El +.Pp +The format of this option is (without spaces): +.Pp +.Em type +: +.Ar source +: +.Ar priority +: +.Ar value +: +.Bl -tag -width "XX" +.It Ar type +Valid types are: +.Bl -tag -width "XXX." -compact +.It Sy PSE +Power Sourcing Entity (power provider) +.It Sy PD +Power Device (power consumer) +.El +.It Ar source +Valid sources are: +.Bl -tag -width "X." -compact +.It Sy 0 +Unknown +.It Sy 1 +For PD, the power source is the PSE. For PSE, the power source is the +primary power source. +.It Sy 2 +For PD, the power source is a local source. For PSE, the power source +is the backup power source or a power conservation mode is asked (the +PSE may be running on UPS for example). +.It Sy 3 +For PD, the power source is both the PSE and a local source. For PSE, +this value should not be used. +.El +.It Ar priority +Four priorities are available: +.Bl -tag -width "X." -compact +.It Sy 0 +Unknown priority +.It Sy 1 +Critical +.It Sy 2 +High +.It Sy 3 +Low +.El +.It Ar value +For PD, the power value is the total power in tenth of watts required +by a PD device from the PSE device. This value should range from 0 to +1023 tenth of watts. +.El .El .Sh FILES .Bl -tag -width "/var/run/lldpd.socketXX" -compact diff --git a/src/client.c b/src/client.c index 7ee48e15..3f39feb8 100644 --- a/src/client.c +++ b/src/client.c @@ -25,6 +25,7 @@ static struct client_handle client_handles[] = { #ifdef ENABLE_LLDPMED { HMSG_SET_LOCATION, client_handle_port_related }, { HMSG_SET_POLICY, client_handle_port_related }, + { HMSG_SET_POWER, client_handle_port_related }, #endif #ifdef ENABLE_DOT1 { HMSG_GET_VLANS, client_handle_port_related }, @@ -196,6 +197,33 @@ client_handle_port_related(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_POLICY; break; + case HMSG_SET_POWER: + p = (char*)&r->data + IFNAMSIZ; + memset(&hardware->h_lport.p_med_power, 0, + sizeof(struct lldpd_med_power)); + if (ctl_msg_unpack_structure(STRUCT_LLDPD_MED_POWER, + &hardware->h_lport.p_med_power, + sizeof(struct lldpd_med_power), + r, &p) == -1) { + LLOG_WARNX("unable to set POE-MDI for %s", + ifname); + s->hdr.len = -1; + return; + } + hardware->h_lport.p_med_cap_enabled &= ~( + LLDPMED_CAP_MDI_PD | LLDPMED_CAP_MDI_PSE); + switch (hardware->h_lport.p_med_power.devicetype) + { + case LLDPMED_POW_TYPE_PSE: + hardware->h_lport.p_med_cap_enabled |= + LLDPMED_CAP_MDI_PSE; + break; + case LLDPMED_POW_TYPE_PD: + hardware->h_lport.p_med_cap_enabled |= + LLDPMED_CAP_MDI_PD; + break; + } + break; #endif case HMSG_GET_NB_PORTS: p = &s->data; diff --git a/src/lldp.c b/src/lldp.c index 4d9be3e2..35633008 100644 --- a/src/lldp.c +++ b/src/lldp.c @@ -276,6 +276,45 @@ lldp_send(struct lldpd *global, goto toobig; } } + + /* LLDP-MED POE-MDI */ + if ((port->p_med_power.devicetype == LLDPMED_POW_TYPE_PSE) || + (port->p_med_power.devicetype == LLDPMED_POW_TYPE_PD)) { + int devicetype = 0, source = 0; + if (!( + POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(med, sizeof(med)) && + POKE_UINT8(LLDP_TLV_MED_MDI))) + goto toobig; + switch (port->p_med_power.devicetype) { + case LLDPMED_POW_TYPE_PSE: + devicetype = 0; + switch (port->p_med_power.source) { + case LLDPMED_POW_SOURCE_PRIMARY: source = 1; break; + case LLDPMED_POW_SOURCE_BACKUP: source = 2; break; + case LLDPMED_POW_SOURCE_RESERVED: source = 3; break; + default: source = 0; break; + } + break; + case LLDPMED_POW_TYPE_PD: + devicetype = 1; + switch (port->p_med_power.source) { + case LLDPMED_POW_SOURCE_PSE: source = 1; break; + case LLDPMED_POW_SOURCE_LOCAL: source = 2; break; + case LLDPMED_POW_SOURCE_BOTH: source = 3; break; + default: source = 0; break; + } + break; + } + if (!( + POKE_UINT8(( + ((devicetype %(1<< 2))<<6) | + ((source %(1<< 2))<<4) | + ((port->p_med_power.priority %(1<< 4))<<0) )) && + POKE_UINT16(port->p_med_power.val) && + POKE_END_LLDP_TLV)) + goto toobig; + } } #endif @@ -670,27 +709,13 @@ lldp_decode(struct lldpd *cfg, char *frame, int s, port->p_med_power.devicetype = LLDPMED_POW_TYPE_RESERVED; } - switch (power & 0x0F) { - case 0x0: + if (((power & 0x0F) < 0) || + ((power & 0x0F) > LLDPMED_POW_PRIO_LOW)) port->p_med_power.priority = LLDPMED_POW_PRIO_UNKNOWN; - break; - case 0x1: - port->p_med_power.priority = - LLDPMED_POW_PRIO_CRITICAL; - break; - case 0x2: - port->p_med_power.priority = - LLDPMED_POW_PRIO_HIGH; - break; - case 0x3: + else port->p_med_power.priority = - LLDPMED_POW_PRIO_LOW; - break; - default: - port->p_med_power.priority = - LLDPMED_POW_PRIO_UNKNOWN; - } + power & 0x0F; port->p_med_power.val = PEEK_UINT16; break; case LLDP_TLV_MED_IV_HW: diff --git a/src/lldp.h b/src/lldp.h index 76f091b3..f1b5f947 100644 --- a/src/lldp.h +++ b/src/lldp.h @@ -205,10 +205,10 @@ enum { #define LLDPMED_POW_SOURCE_LOCAL 6 #define LLDPMED_POW_SOURCE_BOTH 7 -#define LLDPMED_POW_PRIO_UNKNOWN 1 -#define LLDPMED_POW_PRIO_CRITICAL 2 -#define LLDPMED_POW_PRIO_HIGH 3 -#define LLDPMED_POW_PRIO_LOW 4 +#define LLDPMED_POW_PRIO_UNKNOWN 0 +#define LLDPMED_POW_PRIO_CRITICAL 1 +#define LLDPMED_POW_PRIO_HIGH 2 +#define LLDPMED_POW_PRIO_LOW 3 #define LLDPMED_CAP_CAP 0x01 #define LLDPMED_CAP_POLICY 0x02 diff --git a/src/lldpctl.c b/src/lldpctl.c index 316ce4a3..7e710793 100644 --- a/src/lldpctl.c +++ b/src/lldpctl.c @@ -41,7 +41,7 @@ get_interfaces(int s, struct interfaces *ifs); extern void display_interfaces(int s, const char * fmt, int argc, char *argv[]); -#define LLDPCTL_ARGS "hdf:L:P:" +#define LLDPCTL_ARGS "hdf:L:P:O:" static void usage(void) @@ -59,6 +59,8 @@ usage(void) fprintf(stderr, "-P policy Enable the transmission of LLDP-MED Network Policy TLVs\n"); fprintf(stderr, " for the given interfaces. Can be repeated to specify\n"); fprintf(stderr, " different policies.\n"); + fprintf(stderr, "-O poe Enable the trabsmission of LLDP-MED POE-MDI TLV\n"); + fprintf(stderr, " for the given interfaces.\n"); #endif fprintf(stderr, "\n"); @@ -352,6 +354,106 @@ invalid_policy: return -1; } +static int +lldpd_parse_power(struct lldpd_port *port, const char *poe) +{ + const char *e; + int device_type = 0; + int source = 0; + int priority = 0; + int val = 0; + + if (strlen(poe) == 0) + return 0; + e = poe; + + /* Device type */ + if (!strncmp(e, "PD", 2)) + device_type = LLDPMED_POW_TYPE_PD; + else if (!strncmp(e, "PSE", 3)) + device_type = LLDPMED_POW_TYPE_PSE; + else { + LLOG_WARNX("Device type should be either 'PD' or 'PSE'."); + goto invalid_poe; + } + + /* Source */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected power source."); + goto invalid_poe; + } + source = atoi(++e); + if (source < 0 || source > 3) { + LLOG_WARNX("Power source out of range (%d).", source); + goto invalid_poe; + } + + /* Priority */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected power priority."); + goto invalid_poe; + } + priority = atoi(++e); + if (priority < 0 || priority > 3) { + LLOG_WARNX("Power priority out of range (%d).", priority); + goto invalid_poe; + } + + /* Value */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected power value."); + goto invalid_poe; + } + val = atoi(++e); + if (val < 0 || val > 1023) { + LLOG_WARNX("Power value out of range (%d).", val); + goto invalid_poe; + } + + port->p_med_power.devicetype = device_type; + port->p_med_power.priority = priority; + port->p_med_power.val = val; + + switch (device_type) { + case LLDPMED_POW_TYPE_PD: + switch (source) { + case 1: + port->p_med_power.source = LLDPMED_POW_SOURCE_PSE; + break; + case 2: + port->p_med_power.source = LLDPMED_POW_SOURCE_LOCAL; + break; + case 3: + port->p_med_power.source = LLDPMED_POW_SOURCE_BOTH; + break; + default: + port->p_med_power.source = LLDPMED_POW_SOURCE_UNKNOWN; + break; + } + port->p_med_cap_enabled |= LLDPMED_CAP_MDI_PD; + break; + case LLDPMED_POW_TYPE_PSE: + switch (source) { + case 1: + port->p_med_power.source = LLDPMED_POW_SOURCE_PRIMARY; + break; + case 2: + port->p_med_power.source = LLDPMED_POW_SOURCE_BACKUP; + break; + default: + port->p_med_power.source = LLDPMED_POW_SOURCE_UNKNOWN; + break; + } + port->p_med_cap_enabled |= LLDPMED_CAP_MDI_PSE; + break; + } + return 0; + + invalid_poe: + LLOG_WARNX("The format POE-MDI is invalid (%s)", poe); + return -1; +} + static void set_location(int s, int argc, char *argv[]) { @@ -468,6 +570,60 @@ set_policy(int s, int argc, char *argv[]) LLOG_INFO("Network Policy successfully set for %s", iff->name); } } + +static void +set_power(int s, int argc, char *argv[]) +{ + int i, ch; + struct interfaces ifs; + struct lldpd_interface *iff; + struct lldpd_port port; + void *p; + struct hmsg *h; + + if ((h = (struct hmsg *)malloc(MAX_HMSGSIZE)) == NULL) + fatal(NULL); + + memset(&port, 0, sizeof(struct lldpd_port)); + optind = 1; + while ((ch = getopt(argc, argv, LLDPCTL_ARGS)) != -1) { + switch (ch) { + case 'O': + if ((lldpd_parse_power(&port, optarg)) == -1) + fatalx("Incorrect POE-MDI."); + break; + } + } + + get_interfaces(s, &ifs); + TAILQ_FOREACH(iff, &ifs, next) { + if (optind < argc) { + for (i = optind; i < argc; i++) + if (strncmp(argv[i], iff->name, IFNAMSIZ) == 0) + break; + if (i == argc) + continue; + } + + ctl_msg_init(h, HMSG_SET_POWER); + strlcpy((char *)&h->data, iff->name, IFNAMSIZ); + h->hdr.len += IFNAMSIZ; + p = (char*)&h->data + IFNAMSIZ; + if (ctl_msg_pack_structure(STRUCT_LLDPD_MED_POWER, + &port.p_med_power, + sizeof(struct lldpd_med_power), h, &p) == -1) { + LLOG_WARNX("set_power: Unable to set POE-MDI for %s", iff->name); + fatalx("aborting"); + } + if (ctl_msg_send(s, h) == -1) + fatalx("set_power: unable to send request"); + if (ctl_msg_recv(s, h) == -1) + fatalx("set_power: unable to receive answer"); + if (h->hdr.type != HMSG_SET_POWER) + fatalx("set_power: unknown answer type received"); + LLOG_INFO("POE-MDI successfully set for %s", iff->name); + } +} #endif int @@ -477,6 +633,7 @@ main(int argc, char *argv[]) char * fmt = "plain"; #define ACTION_SET_LOCATION (1 << 0) #define ACTION_SET_POLICY (1 << 1) +#define ACTION_SET_POWER (1 << 2) int action = 0; /* @@ -494,16 +651,14 @@ main(int argc, char *argv[]) fmt = optarg; break; case 'L': -#ifdef ENABLE_LLDPMED - action |= ACTION_SET_LOCATION; -#else - fprintf(stderr, "LLDP-MED support is not built-in\n"); - usage(); -#endif - break; case 'P': + case 'O': #ifdef ENABLE_LLDPMED - action |= ACTION_SET_POLICY; + switch (ch) { + case 'L': action |= ACTION_SET_LOCATION; break; + case 'P': action |= ACTION_SET_POLICY; break; + case 'O': action |= ACTION_SET_POWER; break; + } #else fprintf(stderr, "LLDP-MED support is not built-in\n"); usage(); @@ -528,6 +683,8 @@ main(int argc, char *argv[]) set_location(s, argc, argv); if (action & ACTION_SET_POLICY) set_policy(s, argc, argv); + if (action & ACTION_SET_POWER) + set_power(s, argc, argv); #endif if (!action) display_interfaces(s, fmt, argc, argv); diff --git a/src/lldpd.h b/src/lldpd.h index 331691b9..947307b7 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -325,6 +325,7 @@ enum hmsg_type { HMSG_GET_VLANS, HMSG_SET_LOCATION, HMSG_SET_POLICY, + HMSG_SET_POWER, HMSG_SHUTDOWN };