.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
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
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
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;
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
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)
{
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;
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;
usage();
}
}
-
+
log_init(debug);
if (!debug) {
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