From 86f24df3359c185fe31a97554f24ca2ab0b480e9 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Wed, 24 Mar 2010 22:57:30 +0100 Subject: [PATCH] Allow to set LLDP-MED network policy TLV from lldpctl. Patch from Philipp Kempgen. This adds a "-P" option to lldpctl that can be repeated several times. --- man/lldpctl.8 | 96 +++++++++++++++++++++++++++ src/client.c | 30 +++++++++ src/lldp.c | 20 ++++++ src/lldpctl.c | 179 +++++++++++++++++++++++++++++++++++++++++++++++++- src/lldpd.h | 1 + 5 files changed, 325 insertions(+), 1 deletion(-) diff --git a/man/lldpctl.8 b/man/lldpctl.8 index 416b63a6..16221ea7 100644 --- a/man/lldpctl.8 +++ b/man/lldpctl.8 @@ -23,6 +23,7 @@ .Nm .Op Fl d .Op Fl L Ar location +.Op Fl P Ar policy .Op Ar interface ... .Sh DESCRIPTION The @@ -149,6 +150,101 @@ and the second argument is the ELIN number. When setting a location for a given port, all previous locations are erased. To erase all location, just use the empty string. There is currently no way to get the location from the command line. +.Pp +.It Fl P Ar policy +Enable the transmission of LLDP-MED Network Policy TLVs for the given +interfaces. This option can be repeated several times to specify +different policies. Format (without spaces!): +.Pp +.Em App-Type +: +.Ar U +: +.Ar T +: +.Ar VLAN-ID +: +.Ar L2-Prio +: +.Ar DSCP +.Bl -tag -width "XX" +.It Ar App-Type +Valid application types (see ANSI/TIA-1057 table 12): +.Bl -tag -width "X." -compact +.It Sy 1 +Voice +.It Sy 2 +Voice Signaling +.It Sy 3 +Guest Voice +.It Sy 4 +Guest Voice Signaling +.It Sy 5 +Softphone Voice +.It Sy 6 +Video Conferencing +.It Sy 7 +Streaming Video +.It Sy 8 +Video Signaling +.El +.It Ar U +Unknown Policy Flag. +.Bl -tag -width "X." -compact +.It Sy 0 +Network policy for the specified application type is defined. +.It Sy 1 +Network policy for the specified application type is required by +the device but is currently unknown. This is used by Endpoint +Devices, not by Network Connectivity Devices. +.El +.It Ar T +Tagged Flag. +.Bl -tag -width "X." -compact +.It Sy 0 +Untagged VLAN. In this case the VLAN ID and the Layer 2 Priority +are ignored and only the DSCP value has relevance. +.It Sy 1 +Tagged VLAN. +.El +.It Ar VLAN-ID +IEEE 802.1q VLAN ID (VID). A value of 1 through 4094 defines a +VLAN ID. A value of 0 means that only the priority level is +significant. +.It Ar L2-Prio +IEEE 802.1d / IEEE 802.1p Layer 2 Priority, also known as Class of Service +(CoS), to be used for the specified application type. +.Bl -tag -width "X." -compact +.It Sy 1 +Background +.It Sy 2 +Spare +.It Sy 0 +Best Effort (default) +.It Sy 3 +Excellent Effort +.It Sy 4 +Controlled Load +.It Sy 5 +Video +.It Sy 6 +Voice +.It Sy 7 +Network Control +.El +.It Ar DSCP +DiffServ/Differentiated Services Code Point (DSCP) value as defined +in IETF RFC 2474 for the specified application type. Value: 0 (default +per RFC 2475) through 63. Note: The class selector DSCP values are +backwards compatible for devices that only support the old IP +precedence Type of Service (ToS) format. (See the RFCs for what +these values mean.) +.It Examples: +.Bl -tag -width "X." -compact +.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) .El .Sh FILES .Bl -tag -width "/var/run/lldpd.socketXX" -compact diff --git a/src/client.c b/src/client.c index 4b7d466c..7ee48e15 100644 --- a/src/client.c +++ b/src/client.c @@ -24,6 +24,7 @@ static struct client_handle client_handles[] = { { HMSG_GET_CHASSIS, client_handle_port_related }, #ifdef ENABLE_LLDPMED { HMSG_SET_LOCATION, client_handle_port_related }, + { HMSG_SET_POLICY, client_handle_port_related }, #endif #ifdef ENABLE_DOT1 { HMSG_GET_VLANS, client_handle_port_related }, @@ -166,6 +167,35 @@ client_handle_port_related(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) } hardware->h_lport.p_med_cap_enabled |= LLDPMED_CAP_LOCATION; break; + case HMSG_SET_POLICY: + p = (char*)&r->data + IFNAMSIZ; + for (i=0; i < LLDPMED_APPTYPE_LAST; i++) { + hardware->h_lport.p_med_policy[i].type = 0; + hardware->h_lport.p_med_policy[i].unknown = 0; + hardware->h_lport.p_med_policy[i].tagged = 0; + hardware->h_lport.p_med_policy[i].vid = 0; + hardware->h_lport.p_med_policy[i].priority = 0; + hardware->h_lport.p_med_policy[i].dscp = 0; + } + if (ctl_msg_unpack_structure( + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY, + hardware->h_lport.p_med_policy, + 8*sizeof(struct lldpd_med_policy), + r, &p) == -1) { + LLOG_WARNX("unable to set network policy for %s", ifname); + s->hdr.len = -1; + return; + } + hardware->h_lport.p_med_cap_enabled |= + LLDPMED_CAP_POLICY; + break; #endif case HMSG_GET_NB_PORTS: p = &s->data; diff --git a/src/lldp.c b/src/lldp.c index 6fc74933..2bb9e2ee 100644 --- a/src/lldp.c +++ b/src/lldp.c @@ -256,6 +256,26 @@ lldp_send(struct lldpd *global, goto toobig; } } + + /* LLDP-MED network policy */ + for (i = 0; i < LLDPMED_APPTYPE_LAST; i++) { + if (port->p_med_policy[i].type == i + 1) { + if (!( + POKE_START_LLDP_TLV(LLDP_TLV_ORG) && + POKE_BYTES(med, sizeof(med)) && + POKE_UINT8(LLDP_TLV_MED_POLICY) && + POKE_UINT32(( + ((port->p_med_policy[i].type %(1<< 8))<<24) | + ((port->p_med_policy[i].unknown %(1<< 1))<<23) | + ((port->p_med_policy[i].tagged %(1<< 1))<<22) | + /*((0 %(1<< 1))<<21) |*/ + ((port->p_med_policy[i].vid %(1<<12))<< 9) | + ((port->p_med_policy[i].priority %(1<< 3))<< 6) | + ((port->p_med_policy[i].dscp %(1<< 6))<< 0) )) && + POKE_END_LLDP_TLV)) + goto toobig; + } + } } #endif diff --git a/src/lldpctl.c b/src/lldpctl.c index 869d7d58..33111cfb 100644 --- a/src/lldpctl.c +++ b/src/lldpctl.c @@ -54,6 +54,9 @@ usage(void) fprintf(stderr, "-L location Enable the transmission of LLDP-MED location TLV for the\n"); fprintf(stderr, " given interfaces. Can be repeated to enable the transmission\n"); fprintf(stderr, " of the location in several formats.\n"); + 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"); #endif fprintf(stderr, "\n"); @@ -247,6 +250,106 @@ invalid_location: return -1; } +static int +lldpd_parse_policy(struct lldpd_port *port, const char *policy) +{ + const char *e; + int app_type = 0; + int unknown_policy_flag = 0; + int tagged_flag = 0; + int vlan_id = 0; + int l2_prio = 0; + int dscp = 0; + + if (strlen(policy) == 0) { + return 0; + } + + e = policy; + + /* Application Type: */ + app_type = atoi(e); + if (app_type < 1 || app_type > LLDPMED_APPTYPE_LAST) { + LLOG_WARNX("Application Type (%u) out of range.", app_type); + goto invalid_policy; + } + + /* Unknown Policy Flag (U): */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected Unknown Policy Flag (U)."); + goto invalid_policy; + } + e = e + 1; + unknown_policy_flag = atoi(e); + if (unknown_policy_flag < 0 || unknown_policy_flag > 1) { + LLOG_WARNX("Unknown Policy Flag (%u) out of range.", unknown_policy_flag); + goto invalid_policy; + } + + /* Tagged Flag (T): */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected Tagged Flag (T)."); + goto invalid_policy; + } + e = e + 1; + tagged_flag = atoi(e); + if (tagged_flag < 0 || tagged_flag > 1) { + LLOG_WARNX("Tagged Flag (%u) out of range.", tagged_flag); + goto invalid_policy; + } + + /* VLAN-ID (VID): */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected VLAN ID (VID)."); + goto invalid_policy; + } + e = e + 1; + vlan_id = atoi(e); + if (vlan_id < 0 || vlan_id > 4094) { + LLOG_WARNX("VLAN ID (%u) out of range.", vlan_id); + goto invalid_policy; + } + + /* Layer 2 Priority: */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected Layer 2 Priority."); + goto invalid_policy; + } + e = e + 1; + l2_prio = atoi(e); + if (l2_prio < 0 || l2_prio > 7) { + LLOG_WARNX("Layer 2 Priority (%u) out of range.", l2_prio); + goto invalid_policy; + } + + /* DSCP value: */ + if ((e = strchr(e, ':')) == NULL) { + LLOG_WARNX("Expected DSCP value."); + goto invalid_policy; + } + e = e + 1; + dscp = atoi(e); + if (dscp < 0 || dscp > 63) { + LLOG_WARNX("DSCP value (%u) out of range.", dscp); + goto invalid_policy; + } + + port->p_med_policy[app_type - 1].type = (u_int8_t) app_type; + port->p_med_policy[app_type - 1].unknown = (u_int8_t) unknown_policy_flag; + port->p_med_policy[app_type - 1].tagged = (u_int8_t) tagged_flag; + port->p_med_policy[app_type - 1].vid = (u_int16_t) vlan_id; + port->p_med_policy[app_type - 1].priority = (u_int8_t) l2_prio; + port->p_med_policy[app_type - 1].dscp = (u_int8_t) dscp; + + port->p_med_cap_enabled |= LLDPMED_CAP_POLICY; + return 0; + +invalid_policy: + LLOG_WARNX("The format of the policy is invalid (%s)", + policy); + return -1; +} + static void set_location(int s, int argc, char *argv[]) { @@ -301,6 +404,68 @@ set_location(int s, int argc, char *argv[]) LLOG_INFO("Location set succesfully for %s", iff->name); } } + +static void +set_policy(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, "dP:")) != -1) { + switch (ch) { + case 'P': + if ((lldpd_parse_policy(&port, optarg)) == -1) + fatalx("Incorrect Network Policy."); + 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_POLICY); + strlcpy((char *)&h->data, iff->name, IFNAMSIZ); + h->hdr.len += IFNAMSIZ; + p = (char*)&h->data + IFNAMSIZ; + if (ctl_msg_pack_structure( + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY + STRUCT_LLDPD_MED_POLICY, + port.p_med_policy, + 8*sizeof(struct lldpd_med_policy), h, &p) == -1) { + LLOG_WARNX("set_policy: Unable to set Network Policy for %s", iff->name); + fatalx("aborting"); + } + if (ctl_msg_send(s, h) == -1) + fatalx("set_policy: unable to send request"); + if (ctl_msg_recv(s, h) == -1) + fatalx("set_policy: unable to receive answer"); + if (h->hdr.type != HMSG_SET_POLICY) + fatalx("set_policy: unknown answer type received"); + LLOG_INFO("Network Policy successfully set for %s", iff->name); + } +} #endif int @@ -309,12 +474,13 @@ main(int argc, char *argv[]) int ch, s, debug = 1; char * fmt = "plain"; #define ACTION_SET_LOCATION 1 +#define ACTION_SET_POLICY 2 int action = 0; /* * Get and parse command line options */ - while ((ch = getopt(argc, argv, "hdf:L:")) != -1) { + while ((ch = getopt(argc, argv, "hdf:L:P:")) != -1) { switch (ch) { case 'h': usage(); @@ -331,6 +497,14 @@ main(int argc, char *argv[]) #else fprintf(stderr, "LLDP-MED support is not built-in\n"); usage(); +#endif + break; + case 'P': +#ifdef ENABLE_LLDPMED + action = ACTION_SET_POLICY; +#else + fprintf(stderr, "LLDP-MED support is not built-in\n"); + usage(); #endif break; default: @@ -352,6 +526,9 @@ main(int argc, char *argv[]) case ACTION_SET_LOCATION: set_location(s, argc, argv); break; + case ACTION_SET_POLICY: + set_policy(s, argc, argv); + break; #endif default: display_interfaces(s, fmt, argc, argv); diff --git a/src/lldpd.h b/src/lldpd.h index 96ef8d6f..51305e7c 100644 --- a/src/lldpd.h +++ b/src/lldpd.h @@ -319,6 +319,7 @@ enum hmsg_type { HMSG_GET_CHASSIS, HMSG_GET_VLANS, HMSG_SET_LOCATION, + HMSG_SET_POLICY, HMSG_SHUTDOWN }; -- 2.39.5