Commit a9add108 authored by Grzegorz Daniluk's avatar Grzegorz Daniluk

Merge branch 'adam-lldp-rebased' into proposed_master

parents 0e488977 214464b3
......@@ -297,6 +297,15 @@ config ABSCAL
using a loop-back SFP adapter, according to the procedure
described and documented by Peter Jansweijer.
config LLDP
depends on WR_NODE
boolean "Include LLDP protocol transmit-only"
default n
help
This enable LLDP support. LLDP is a vendor-neutral link layer protocol
used by network devices for advertising their identity, capabilities,
and neighbors on local area network.
#
# This is a set of configuration options that should not be changed by
# normal users. If the "developer" menu is used, the binary is tainted.
......@@ -576,11 +585,3 @@ config PPSI
boolean
help
Select this option for the ppsi engine (now only option)
config LLDP
depends on DEVELOPER
boolean "Include LLDP protocol transmit-only"
help
This enable LLDP support. LLDP is a vendor-neutral link layer protocol
used by network devices for advertising their identity, capabilities,
and neighbors on local area network.
......@@ -42,6 +42,7 @@ CONFIG_GENSDBFS=y
CONFIG_WR_DIAG=y
# CONFIG_WR_NODE_SIM is not set
CONFIG_ABSCAL=y
CONFIG_LLDP=y
#
# wrpc-sw is tainted if you change the following options
......
......@@ -42,6 +42,7 @@ CONFIG_GENSDBFS=y
CONFIG_WR_DIAG=y
# CONFIG_WR_NODE_SIM is not set
CONFIG_ABSCAL=y
CONFIG_LLDP=y
#
# wrpc-sw is tainted if you change the following options
......
......@@ -2,7 +2,10 @@
* This work is part of the White Rabbit project
*
* Copyright (C) 2011 GSI (www.gsi.de)
* Copyright (C) 2017 CERN
*
* Author: Cesar Prados <c.prados@gsi.de>
* Author: Adam Wujek <adam.wujek@cern.ch>
*
* LLDP transmit-only station
*
......@@ -17,9 +20,10 @@
#include "endpoint.h"
#include "ipv4.h"
#include "shell.h"
#include "syscon.h"
#include <wrpc.h> /*needed for htons()*/
static char lldpdu[LLDP_PKT_LEN];
static uint8_t lldpdu[LLDP_MAX_PKT_LEN];
static uint16_t lldpdu_len;
/* tx-only socket */
......@@ -31,109 +35,194 @@ static struct wrpc_socket __static_lldp_socket = {
static struct wrpc_socket *lldp_socket;
static struct wr_sockaddr addr;
static void lldp_header_tlv(int tlv_type) {
extern char wrc_hw_name[HW_NAME_LENGTH];
lldpdu_len = tlv_offset[tlv_type];
lldpdu[lldpdu_len] = tlv_type * 2;
lldpdu[lldpdu_len + LLDP_SUBTYPE] = tlv_type_len[tlv_type];
static void lldp_header_tlv(uint8_t tlv_type, uint8_t tlv_len)
{
lldpdu[lldpdu_len] = tlv_type << 1;
lldpdu[lldpdu_len + LLDP_SUBTYPE] = tlv_len;
lldpdu_len += LLDP_HEADER;
}
static void fill_mac(uint8_t *tlv, uint8_t type)
{
*tlv = type;
/* write MAC after subtype byte */
get_mac_addr(tlv + LLDP_SUBTYPE);
}
static void lldp_add_tlv(int tlv_type) {
unsigned char mac[6];
uint8_t mac[6];
unsigned char ipWR[4];
switch(tlv_type) {
case END_LLDP:
/* header */
lldp_header_tlv(tlv_type);
/* End TLV */
memcpy(lldpdu + lldpdu_len, 0x0, tlv_type_len[tlv_type]);
break;
case CHASSIS_ID:
/* header */
lldp_header_tlv(tlv_type);
/* TLV Chassis Component */
lldpdu[lldpdu_len] = 4;
int tlv_len = 0;
switch (tlv_type) {
case END_LLDP:
/* End TLV */
/* header */
lldp_header_tlv(tlv_type, tlv_len);
break;
case CHASSIS_ID:
tlv_len = CHASSIS_ID_TLV_LEN;
/* header */
lldp_header_tlv(tlv_type, tlv_len);
/* TLV Chassis Component */
fill_mac(&lldpdu[lldpdu_len], CHASSIS_ID_TYPE_MAC);
break;
case PORT_ID:
tlv_len = PORT_ID_TLV_LEN;
/* header */
lldp_header_tlv(tlv_type, tlv_len);
/* TLV portID */
fill_mac(&lldpdu[lldpdu_len], PORT_ID_SUBTYPE_MAC);
break;
case TTL:
tlv_len = TTL_ID_TLV_LEN;
/* header */
lldp_header_tlv(tlv_type, tlv_len);
/* TLV Time to Live */
/* use LLDP_TX_TICK_INTERVAL in seconds times 4 */
lldpdu[lldpdu_len + TTL_BYTE_MSB] =
(((LLDP_TX_TICK_INTERVAL / 1000) * 4) >> 8) & 0xff;
lldpdu[lldpdu_len + TTL_BYTE_LSB] =
((LLDP_TX_TICK_INTERVAL / 1000) * 4) & 0xff;
break;
case PORT:
tlv_len = strlen(PORT_NAME) + 1;
/* header */
lldp_header_tlv(tlv_type, tlv_len);
strcpy((char *)lldpdu + lldpdu_len, PORT_NAME);
break;
case SYS_NAME:
{
/* TODO get host system name from wr-core outer world */
/* NOTE: according to 802.1AB-2005 9.5.6.2 and then
* IETF RFC 3418:
* "If the name is unknown, the value is the zero-length
* string."
* However, we put the IP, if not set MAC to be able to
* identify a system */
char buf[32];
getIP(ipWR);
if (HAS_IP && memcmp(ipWR, "\0\0\0\0", 4)) {
/* NOTE: no subtype */
format_ip(buf, ipWR);
tlv_len = strlen((char *)buf);
strcpy((char *)(lldpdu + lldpdu_len + LLDP_HEADER),
(char *)buf);
} else {
/* NOTE: no subtype */
get_mac_addr(mac);
memcpy(lldpdu + (lldpdu_len + LLDP_SUBTYPE), mac, 6);
break;
case PORT_ID:
/* header */
lldp_header_tlv(tlv_type);
/* TLV Interce Alias */
lldpdu[lldpdu_len] = 7;
strcpy(lldpdu + (lldpdu_len + LLDP_SUBTYPE), "WR Port");
break;
case TTL:
/* header */
lldp_header_tlv(tlv_type);
pp_sprintf(buf,
"%02x:%02x:%02x:%02x:%02x:%02x",
mac[0], mac[1], mac[2], mac[3],
mac[4], mac[5]);
tlv_len = 17;
strncpy((char *)(lldpdu + lldpdu_len + LLDP_HEADER),
(char *)buf, tlv_len);
}
/* header */
lldp_header_tlv(tlv_type, tlv_len);
/* TLV Time to Live */
lldpdu[lldpdu_len + LLDP_SUBTYPE] = 0x20; /* sec */
break;
case PORT:
/* header */
lldp_header_tlv(tlv_type);
/* TLV Info srting */
if (HAS_IP) {
getIP(ipWR);
char buf[32];
format_ip(buf, ipWR);
strcpy(lldpdu + lldpdu_len, buf);
}
break;
}
case SYS_DESCR:
tlv_len = 0; /* will be calculated later */
uint8_t *pdu_p;
/* set the pointer after TLV's header */
pdu_p = &lldpdu[lldpdu_len + LLDP_HEADER];
/* TLV Info srting */
strncpy((char *)(pdu_p), wrc_hw_name, HW_NAME_LENGTH - 1);
pdu_p += strnlen(wrc_hw_name, HW_NAME_LENGTH - 1);
strcpy((char *)(pdu_p), ": ");
pdu_p += 2; /* length of ": " */
strncpy((char *)(pdu_p), build_revision, 32);
pdu_p += strnlen(build_revision, 32);
tlv_len = (uint8_t)(pdu_p - &lldpdu[lldpdu_len + LLDP_HEADER]);
lldp_header_tlv(tlv_type, tlv_len);
break;
case SYS_CAPLTY:
/* don't implement this, this TLV is optional */
break;
tlv_len = 4;
/* header */
lldp_header_tlv(tlv_type, tlv_len);
/* TLV Info string */
memset(lldpdu + lldpdu_len, 0x0, tlv_len);
break;
case MNG_ADD:
/* TODO: fill with MAC if no IP present */
if (!HAS_IP || !memcmp(ipWR, "\0\0\0\0", 4)) {
/* if no IP present skip this field */
break;
case SYS_NAME:
/* header */
lldp_header_tlv(tlv_type);
/* TLV Info srting */
/* TODO get host system name from wr-core outer world */
strcpy(lldpdu + lldpdu_len, "WR PTP Core");
}
/* TODO: dynamic len */
tlv_len = 0xc;
/* header */
lldp_header_tlv(tlv_type, tlv_len);
/* TLV Info string */
lldpdu[lldpdu_len] = MNG_ADDR_LEN; /* len */
/* mngt add subtype */
lldpdu[lldpdu_len + LLDP_SUBTYPE] = MNG_ADDR_SUBTYPE_IPv4;
/* if subtype (ifIndex)*/
lldpdu[lldpdu_len + MNT_IF_SUBTYPE] =
MNG_IF_NUM_SUBTYPE_IFINDEX;
/* if number */
lldpdu[lldpdu_len + MNT_IF_NUM] = 0x1;
/* TLV Info srting */
if (HAS_IP) {
getIP(ipWR);
char buf[32];
format_ip(buf, ipWR);
memcpy(&lldpdu[lldpdu_len + LLDP_SUBTYPE + 1],
ipWR, 4);
}
/* TODO: add info about VLAN 9.5.9.9g */
break;
case USER_DEF:
/* TODO define WR TLV */
return;
break;
default:
return;
break;
}
lldpdu_len += tlv_len;
}
break;
case SYS_DESCR:
/* header */
lldp_header_tlv(tlv_type);
static void lldp_update(void)
{
int i;
/* add mandatory LLDP TLVs */
memset(lldpdu, 0x0, LLDP_MAX_PKT_LEN);
lldpdu_len = 0;
/* TLV Info srting */
strcpy(lldpdu + lldpdu_len, build_revision);
break;
case SYS_CAPLTY:
/* header */
lldp_header_tlv(tlv_type);
pp_printf("lldp update\n");
/* add all TLV's */
for (i = CHASSIS_ID; i <= MNG_ADD; i++)
lldp_add_tlv(i);
/* TLV Info string */
memset(lldpdu + lldpdu_len, 0x0, 4);
break;
case MNG_ADD:
/* header */
lldp_header_tlv(tlv_type);
/* TLV Info string */
/* TODO get host system name from wr-core outer world */
lldpdu[lldpdu_len] = 0x4; /* len */
lldpdu[lldpdu_len + LLDP_SUBTYPE] = 0x1; /* mngt add subtype */
lldpdu[lldpdu_len + IF_SUBTYPE] = 0x1; /* if subtype */
lldpdu[lldpdu_len + IF_NUM] = 0x1; /* if number */
break;
case USER_DEF:
/* TODO define WR TLV */
break;
default:
break;
}
/* end TLVs */
lldp_add_tlv(END_LLDP);
}
static void lldp_init(void)
{
struct wr_sockaddr saddr;
int i;
/* LLDP: raw ethernet*/
memset(&saddr, 0x0, sizeof(saddr));
......@@ -145,33 +234,38 @@ static void lldp_init(void)
memset(&addr, 0x0, sizeof(struct wr_sockaddr));
memcpy(addr.mac, LLDP_MCAST_MAC, 6);
/* add mandatory LLDP TLVs */
memset(lldpdu, 0x0, LLDP_PKT_LEN);
lldpdu_len = 0;
for (i=CHASSIS_ID; i <= SYS_CAPLTY; i++)
lldp_add_tlv(i);
/* add optional TLVs */
lldp_add_tlv(MNG_ADD);
/* end TLVs */
lldp_add_tlv(END_LLDP);
lldp_update();
}
static int lldp_poll(void)
{
static int ticks;
static int ticks;
unsigned char new_ipWR;
static unsigned char old_ipWR;
uint8_t new_mac[ETH_ALEN];
static uint8_t old_mac[ETH_ALEN];
/* periodic tasks */
if (ticks > LLDP_TX_FQ) {
if (ticks > LLDP_TX_TICK_INTERVAL) {
get_mac_addr(new_mac);
if (HAS_IP) {
getIP(&new_ipWR);
}
if (HAS_IP && (ip_status != IP_TRAINING)) {
lldp_add_tlv(PORT);
/* update other dynamic TLVs */
/* Update only when IP or MAC changed */
/* TODO: or VLAN changed */
if (memcmp(&new_mac, &old_mac, ETH_ALEN)
|| (HAS_IP && (ip_status != IP_TRAINING)
&& memcmp(&new_ipWR, &old_ipWR, IPLEN))
) {
/* update LLDP info */
lldp_update();
/* copy new MAC nad IP */
memcpy(&old_mac, &new_mac, ETH_ALEN);
memcpy(&old_ipWR, &new_ipWR, IPLEN);
}
ptpd_netif_sendto(lldp_socket, &addr, lldpdu, LLDP_PKT_LEN, 0);
ptpd_netif_sendto(lldp_socket, &addr, lldpdu, lldpdu_len, 0);
ticks = 0;
return 1;
......
......@@ -11,24 +11,41 @@
#ifndef __LLDP_H
#define __LLDP_H
#define LLDP_MCAST_MAC "\x01\x80\xC2\x00\x00\x0E"
#define LLDP_ETH_TYP 0x88CC
#define LLDP_PKT_LEN 0x9E /* 158 bytes */
#define TLV_MAX 0xA
#define LLDP_HEADER 0x2
#define LLDP_SUBTYPE 0x1
#define IF_SUBTYPE 0x6
#define IF_NUM 0x10
#define LLDP_TX_FQ 1000
enum TLV_TYPE { END_LLDP = 0, /* mandatory TLVs */
#include "minic.h"
#define LLDP_MCAST_MAC "\x01\x80\xC2\x00\x00\x0E" /* 802.1AB-2005,
Table 8-1 */
#define LLDP_ETH_TYP 0x88CC /* 802.1AB-2005, Table 8-2 */
#define LLDP_MAX_PKT_LEN 0x9E /* 158 bytes */
#define TLV_MAX 0xA
#define LLDP_HEADER 0x2
#define LLDP_SUBTYPE 0x1
#define MNT_IF_SUBTYPE 0x6
#define MNT_IF_NUM 10
#define LLDP_TX_TICK_INTERVAL 1000
#define CHASSIS_ID_TLV_LEN (1 + ETH_ALEN) /* chassis ID subtype byte
* + MAC Len */
#define CHASSIS_ID_TYPE_MAC 4 /* 802.1AB-2005, table 9-2 */
#define PORT_ID_TLV_LEN (1 + ETH_ALEN) /* port ID subtype byte
* + MAC Len */
#define PORT_ID_SUBTYPE_MAC 3 /* 802.1AB-2005, table 9-3 */
#define TTL_ID_TLV_LEN 2 /* 802.1AB-2005, Figure 9-6 */
#define TTL_BYTE_MSB 0
#define TTL_BYTE_LSB 1
#define PORT_NAME "wr0"
#define IPLEN 4 /* len of IP address in bytes */
#define MNG_ADDR_LEN (1 + IPLEN) /* MNT addr subtype + IPLEN */
#define MNG_ADDR_SUBTYPE_IPv4 1 /* ianaAddressFamilyNumbers MIB */
#define MNG_ADDR_SUBTYPE_MAC 6 /* ianaAddressFamilyNumbers MIB */
#define MNG_IF_NUM_SUBTYPE_IFINDEX 2 /* 802.1AB-2005, 9.5.9.5 */
enum TLV_TYPE {
END_LLDP = 0, /* mandatory TLVs */
CHASSIS_ID,
PORT_ID,
TTL,
PORT, /* optional TLVs */
PORT, /* optional TLVs */
SYS_NAME,
SYS_DESCR,
SYS_CAPLTY,
......@@ -36,26 +53,4 @@ enum TLV_TYPE { END_LLDP = 0, /* mandatory TLVs */
USER_DEF
};
uint16_t tlv_type_len[TLV_MAX] = { 0x0, /* LEN_LLDP_END */
0x7, /* LEN_CHASSIS_ID */
0x14, /* LEN_PORT_ID */
0x2, /* LEN_TTL */
0x14, /* LEN_PORT */
0x14, /* LEN_SYS_NAME */
0x14, /* LEN_SYS_DESCR */
0x4, /* LEN_SYS_CAPLTY */
0xC /* LEN_MNG_ADD */
};
uint16_t tlv_offset[TLV_MAX] = { 0x79, /* LEN_LLDP_END */
0x0, /* LEN_CHASSIS_ID */
0x9, /* LEN_PORT_ID */
0x1F, /* LEN_TTL */
0x23, /* LEN_PORT */
0x39, /* LEN_SYS_NAME */
0x4F, /* LEN_SYS_DESCR */
0x65, /* LEN_SYS_CAPLTY */
0x6B /* LEN_MNG_ADD */
};
#endif /* __LLDP_H */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment