From 115ff55c3b71a45d0ec1036eb57c391e6a6440a3 Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Sat, 13 Dec 2008 18:14:22 +0100 Subject: [PATCH] Allow to send LLDP-MED location. --- man/lldpd.8 | 106 ++++++++++++++++++++++++- src/lldp.c | 30 ++++++- src/lldpctl.c | 2 +- src/lldpd.c | 216 +++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 338 insertions(+), 16 deletions(-) diff --git a/man/lldpd.8 b/man/lldpd.8 index db8057fd..d2cad2a7 100644 --- a/man/lldpd.8 +++ b/man/lldpd.8 @@ -25,6 +25,8 @@ .Op Fl m Ar management .Op Fl p Ar probe time .Op Fl M Ar class +.Op Fl L Ar location +.Op Fl P Ar policy .Sh DESCRIPTION .Nm is a daemon able to receive and send @@ -96,13 +98,13 @@ are enabled. Enable emission of LLDP-MED frame. The class should be one of the following value: .Bl -tag -width "0:XX" -compact -.It Ar 1 +.It Sy 1 Generic Endpoint (Class I) -.It Ar 2 +.It Sy 2 Media Endpoint (Class II) -.It Ar 3 +.It Sy 3 Communication Device Endpoints (Class III) -.It Ar 4 +.It Sy 4 Network Connectivity Device .El .It Fl i @@ -111,6 +113,102 @@ Disable LLDP-MED inventory TLV transmission. will still receive (and publish using SNMP if enabled) those LLDP-MED TLV but will not send them. Use this option if you don't want to transmit sensible information like serial numbers. +.It Fl L Ar location +Enable the transmission of LLDP-MED location TLV. This option can be +repeated several times to enable the transmission of the location in +several formats. Several formats are accepted: +.Bl -tag -width "XX" +.It Em Coordinate based location +The format of +.Ar location +is +.Ar 1:48.85667:N:2.2014:E:117.47:m:1 +The first digit is always +.Ar 1 . +It is followed by the latitude, a letter for the direction ( +.Ar E +or +.Ar W +for East or West), the longitude and a letter for the direction ( +.Ar N +or +.Ar S +). The next figure is the altitude. It can be expressed in meters (the +next letter is then +.Ar m +) or in floors (the letter should be +.Ar f +). The last digit is the datum. It can either be +.Ar 1 +(WGS84), +.Ar 2 +(NAD83) or +.Ar 3 +(NAD83/MLLW). +.It Em Civic address +The location can be expressed as an address. The format of the +location is then +.Ar 2:FR:6:Commercial Rd:3:Roseville:19:4 +The first digit is always +.Ar 2 . +The next two letters are the country code. Then, arguments are paired +to form the address. The first member of the pair is a digit +indicating the type of the second member. Here is the list of +valid types: +.Bl -tag -width "XXXX." -compact +.It Sy 0 +Language +.It Sy 1 +National subdivisions +.It Sy 2 +County, parish, district +.It Sy 3 +City, township +.It Sy 4 +City division, borough, ward +.It Sy 5 +Neighborhood, block +.It Sy 6 +Street +.It Sy 16 +Leading street direction +.It Sy 17 +Trailing street suffix +.It Sy 18 +Street suffix +.It Sy 19 +House number +.It Sy 20 +House number suffix +.It Sy 21 +Landmark or vanity address +.It Sy 22 +Additional location info +.It Sy 23 +Name +.It Sy 24 +Postal/ZIP code +.It Sy 25 +Building +.It Sy 26 +Unit +.It Sy 27 +Floor +.It Sy 28 +Room number +.It Sy 29 +Place type +.It Sy 128 +Script +.El +.It ECS ELIN +This is a numerical string using for setting up emergency call. The +format of the location is then the following: +.Ar 3:0000000911 +where the first digit should be +.Ar 3 +and the second argument is the ELIN number. +.El .Sh FILES .Bl -tag -width "/var/run/lldpd.socketXX" -compact .It /var/run/lldpd.socket diff --git a/src/lldp.c b/src/lldp.c index d37c52bb..c455dfb3 100644 --- a/src/lldp.c +++ b/src/lldp.c @@ -52,10 +52,11 @@ lldp_send(struct lldpd *global, struct lldpd_chassis *chassis, struct lldp_macphy macphy; #endif #ifdef ENABLE_LLDPMED + int i; const u_int8_t med[] = LLDP_TLV_ORG_MED; struct lldpmed_cap medcap; struct lldp_org medhw, medfw, medsw, medsn, - medmodel, medasset, medmanuf; + medmodel, medasset, medmanuf, medloc[3]; #endif struct lldpd_port *port = &hardware->h_lport; u_int c = -1, len = 0; @@ -302,6 +303,33 @@ lldp_send(struct lldpd *global, struct lldpd_chassis *chassis, LLDP_INVENTORY(global->g_lchassis.c_med_asset, medasset, LLDP_TLV_MED_IV_ASSET); } + + /* LLDP-MED location */ + for (i = 0; i < LLDPMED_LOCFORMAT_LAST; i++) { + if (global->g_lchassis.c_med_location[i].format == i + 1) { + memset(&medloc[i], 0, sizeof(struct lldp_org)); + medloc[i].tlv_head.type_len = + LLDP_TLV_HEAD(LLDP_TLV_ORG, + sizeof(medloc[i].tlv_org_id) + + sizeof(medloc[i].tlv_org_subtype) + 1 + + global->g_lchassis.c_med_location[i].data_len); + memcpy(medloc[i].tlv_org_id, med, + sizeof(medloc[i].tlv_org_id)); + medloc[i].tlv_org_subtype = LLDP_TLV_MED_LOCATION; + IOV_NEW; + iov[c].iov_base = &medloc[i]; + iov[c].iov_len = sizeof(medloc[i]); + IOV_NEW; + iov[c].iov_base = + &global->g_lchassis.c_med_location[i].format; + iov[c].iov_len = 1; + IOV_NEW; + iov[c].iov_base = + global->g_lchassis.c_med_location[i].data; + iov[c].iov_len = + global->g_lchassis.c_med_location[i].data_len; + } + } } #endif diff --git a/src/lldpctl.c b/src/lldpctl.c index 7f9ea63c..cd94ad10 100644 --- a/src/lldpctl.c +++ b/src/lldpctl.c @@ -287,7 +287,7 @@ display_fixed_precision(u_int64_t value, int intpart, int floatpart, int display u_int64_t tmp = value; int negative = 0; u_int32_t integer = 0; - if (value & (1ULL<<(intpart + floatpart))) { + if (value & (1ULL<<(intpart + floatpart - 1))) { negative = 1; tmp = ~value; tmp += 1; diff --git a/src/lldpd.c b/src/lldpd.c index 5b7f10da..2cc3bd6c 100644 --- a/src/lldpd.c +++ b/src/lldpd.c @@ -1362,6 +1362,188 @@ lldpd_loop(struct lldpd *cfg) lldpd_recv_all(cfg); } +#ifdef ENABLE_LLDPMED +void +lldpd_parse_location(struct lldpd_chassis *chassis, const char *location) +{ + char *l, *e, *s, *data, *n; + double ll, altitude; + u_int32_t intpart, floatpart; + int type = 0, i; + + if ((l = strdup(location)) == NULL) + fatal(NULL); + s = l; + if ((e = index(s, ':')) == NULL) + goto invalid_location; + *e = '\0'; + type = atoi(s); + switch (type) { + case LLDPMED_LOCFORMAT_COORD: + /* Coordinates */ + if ((chassis->c_med_location[0].data = + (char *)malloc(16)) == NULL) + fatal(NULL); + chassis->c_med_location[0].data_len = 16; + chassis->c_med_location[0].format = LLDPMED_LOCFORMAT_COORD; + data = chassis->c_med_location[0].data; + + /* Latitude and longitude */ + for (i = 0; i < 2; i++) { + s = e+1; + if ((e = index(s, ':')) == NULL) + goto invalid_location; + *e = '\0'; + ll = atof(s); + s = e + 1; + if ((e = index(s, ':')) == NULL) + goto invalid_location; + *e = '\0'; + intpart = (int)ll; + floatpart = (ll - intpart) * (1 << 25); + if (((i == 0) && (*s == 'S')) || + ((i == 1) && (*s == 'W'))) { + intpart = ~intpart; + intpart += 1; + floatpart = ~floatpart; + floatpart += 1; + } else if (((i == 0) && (*s != 'N')) || + ((i == 1) && (*s != 'E'))) + goto invalid_location; + *(u_int8_t *)data = (6 << 2) | /* Precision */ + ((intpart & 0x180) >> 7); /* Int part 2 bits */ + data++; + *(u_int8_t *)data = (((intpart & 0x7f) << 1) | /* Int part 7 bits */ + ((floatpart & 0x1000000) >> 24)); /* Float part 1 bit */ + data++; + *(u_int8_t *)data = (floatpart & 0xff0000) >> 16; /* 8 bits */ + data++; + *(u_int8_t *)data = (floatpart & 0xff00) >> 8; /* 8 bits */ + data++; + *(u_int8_t *)data = (floatpart & 0xff); /* 8 bits */ + data++; + } + + /* Altitude */ + s = e+1; + if ((e = index(s, ':')) == NULL) + goto invalid_location; + *e = '\0'; + altitude = atof(s); + s = e+1; + if ((e = index(s, ':')) == NULL) + goto invalid_location; + *e = '\0'; + if (altitude < 0) { + intpart = -(int)altitude; + floatpart = (-(altitude + intpart)) * (1 << 8); + intpart = ~intpart; intpart += 1; + floatpart = ~floatpart; floatpart += 1; + } else { + intpart = (int)altitude; + floatpart = (altitude - intpart) * (1 << 8); + } + if ((*s != 'm') && (*s != 'f')) + goto invalid_location; + *(u_int8_t *)data = ((((*s == 'm')?1:2) << 4) | /* Type 4 bits */ + 0); /* Precision 4 bits */ + data++; + *(u_int8_t *)data = ((6 << 6) | /* Precision 2 bits */ + ((intpart & 0x3f0000) >> 16)); /* Int 6 bits */ + data++; + *(u_int8_t *)data = (intpart & 0xff00) >> 8; /* Int 8 bits */ + data++; + *(u_int8_t *)data = intpart & 0xff; /* Int 8 bits */ + data++; + *(u_int8_t *)data = floatpart & 0xff; /* Float 8 bits */ + data++; + + /* Datum */ + s = e + 1; + if (index(s, ':') != NULL) + goto invalid_location; + *(u_int8_t *)data = atoi(s); + break; + case LLDPMED_LOCFORMAT_CIVIC: + /* Civic address */ + chassis->c_med_location[1].data_len = 4; + s = e+1; + if ((s = index(s, ':')) == NULL) + goto invalid_location; + s = s+1; + do { + if ((s = index(s, ':')) == NULL) + break; + s = s+1; + /* s is the beginning of the word */ + if ((n = index(s, ':')) == NULL) + n = s + strlen(s); + /* n is the end of the word */ + chassis->c_med_location[1].data_len += (n - s) + 2; + if ((s = index(s, ':')) == NULL) + break; + s = s+1; + } while (1); + s = e+1; + if ((chassis->c_med_location[1].data = + (char *)malloc(chassis->c_med_location[1].data_len)) == + NULL) + fatal(NULL); + chassis->c_med_location[1].format = LLDPMED_LOCFORMAT_CIVIC; + data = chassis->c_med_location[1].data; + *(u_int8_t *)data = chassis->c_med_location[1].data_len - 1; + data++; + *(u_int8_t *)data = 2; /* Client location */ + data++; + if ((e = index(s, ':')) == NULL) + goto invalid_location; + if ((e - s) != 2) + goto invalid_location; + memcpy(data, s, 2); /* Country code */ + data += 2; + while (*e != '\0') { + s=e+1; + if ((e = index(s, ':')) == NULL) + goto invalid_location; + *e = '\0'; + *(u_int8_t *)data = atoi(s); + data++; + s=e+1; + if ((e = index(s, ':')) == NULL) + e = s + strlen(s); + *(u_int8_t *)data = e - s; + data++; + memcpy(data, s, e-s); + data += e-s; + } + break; + case LLDPMED_LOCFORMAT_ELIN: + s = e+1; + chassis->c_med_location[2].data_len = strlen(s); + if ((chassis->c_med_location[2].data = + (char *)malloc(strlen(s))) == NULL) + fatal(NULL); + chassis->c_med_location[2].format = LLDPMED_LOCFORMAT_ELIN; + strcpy(chassis->c_med_location[2].data, s); + break; + default: + goto invalid_location; + } + + chassis->c_med_cap_enabled |= LLDPMED_CAP_LOCATION; + return; +invalid_location: + LLOG_WARNX("the format of the location is invalid (%s)", + location); + if (type) { + free(chassis->c_med_location[type-1].data); + memset(&chassis->c_med_location[type-1], 0, + sizeof(struct lldpd_med_loc)); + } + free(l); +} +#endif + void lldpd_shutdown(int sig) { @@ -1402,7 +1584,7 @@ main(int argc, char *argv[]) int snmp = 0; #endif char *mgmtp = NULL; - char *popt, opts[] = "vdxm:p:M:i@ "; + char *popt, opts[] = "vdxm:p:M:iL:P:@ "; int probe = 0, i, found, vlan = 0; #ifdef ENABLE_LLDPMED int lldpmed = 0, noinventory = 0; @@ -1430,26 +1612,30 @@ main(int argc, char *argv[]) case 'm': mgmtp = optarg; break; - case 'M': #ifdef ENABLE_LLDPMED + case 'M': lldpmed = atoi(optarg); if ((lldpmed < 1) || (lldpmed > 4)) { fprintf(stderr, "-M requires an argument between 1 and 4\n"); usage(); } -#else - fprintf(stderr, "LLDP-MED support is not built-in\n"); - usage(); -#endif break; case 'i': -#ifdef ENABLE_LLDPMED noinventory = 1; + break; + case 'L': + case 'P': + /* Handled later */ + break; #else + case 'M': + case 'i': + case 'L': + case 'P': fprintf(stderr, "LLDP-MED support is not built-in\n"); usage(); -#endif break; +#endif case 'p': probe = atoi(optarg); break; @@ -1474,7 +1660,7 @@ main(int argc, char *argv[]) usage(); } } - + log_init(debug); if (!debug) { @@ -1518,10 +1704,20 @@ main(int argc, char *argv[]) cfg->g_lchassis.c_cap_available |= LLDP_CAP_TELEPHONE; cfg->g_lchassis.c_med_type = lldpmed; cfg->g_lchassis.c_med_cap_available = LLDPMED_CAP_CAP | - LLDPMED_CAP_IV; + LLDPMED_CAP_IV | LLDPMED_CAP_LOCATION; cfg->g_lchassis.c_med_cap_enabled = LLDPMED_CAP_CAP; if (!noinventory) cfg->g_lchassis.c_med_cap_enabled |= LLDPMED_CAP_IV; + optind = 1; + while ((ch = getopt(argc, argv, opts)) != -1) { + switch (ch) { + case 'L': + lldpd_parse_location(&cfg->g_lchassis, optarg); + break; + case 'P': + break; + } + } } #endif -- 2.39.5