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 ...@@ -297,6 +297,15 @@ config ABSCAL
using a loop-back SFP adapter, according to the procedure using a loop-back SFP adapter, according to the procedure
described and documented by Peter Jansweijer. 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 # 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. # normal users. If the "developer" menu is used, the binary is tainted.
...@@ -576,11 +585,3 @@ config PPSI ...@@ -576,11 +585,3 @@ config PPSI
boolean boolean
help help
Select this option for the ppsi engine (now only option) 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 ...@@ -42,6 +42,7 @@ CONFIG_GENSDBFS=y
CONFIG_WR_DIAG=y CONFIG_WR_DIAG=y
# CONFIG_WR_NODE_SIM is not set # CONFIG_WR_NODE_SIM is not set
CONFIG_ABSCAL=y CONFIG_ABSCAL=y
CONFIG_LLDP=y
# #
# wrpc-sw is tainted if you change the following options # wrpc-sw is tainted if you change the following options
......
...@@ -42,6 +42,7 @@ CONFIG_GENSDBFS=y ...@@ -42,6 +42,7 @@ CONFIG_GENSDBFS=y
CONFIG_WR_DIAG=y CONFIG_WR_DIAG=y
# CONFIG_WR_NODE_SIM is not set # CONFIG_WR_NODE_SIM is not set
CONFIG_ABSCAL=y CONFIG_ABSCAL=y
CONFIG_LLDP=y
# #
# wrpc-sw is tainted if you change the following options # wrpc-sw is tainted if you change the following options
......
...@@ -2,7 +2,10 @@ ...@@ -2,7 +2,10 @@
* This work is part of the White Rabbit project * This work is part of the White Rabbit project
* *
* Copyright (C) 2011 GSI (www.gsi.de) * Copyright (C) 2011 GSI (www.gsi.de)
* Copyright (C) 2017 CERN
*
* Author: Cesar Prados <c.prados@gsi.de> * Author: Cesar Prados <c.prados@gsi.de>
* Author: Adam Wujek <adam.wujek@cern.ch>
* *
* LLDP transmit-only station * LLDP transmit-only station
* *
...@@ -17,9 +20,10 @@ ...@@ -17,9 +20,10 @@
#include "endpoint.h" #include "endpoint.h"
#include "ipv4.h" #include "ipv4.h"
#include "shell.h" #include "shell.h"
#include "syscon.h"
#include <wrpc.h> /*needed for htons()*/ #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; static uint16_t lldpdu_len;
/* tx-only socket */ /* tx-only socket */
...@@ -31,109 +35,194 @@ static struct wrpc_socket __static_lldp_socket = { ...@@ -31,109 +35,194 @@ static struct wrpc_socket __static_lldp_socket = {
static struct wrpc_socket *lldp_socket; static struct wrpc_socket *lldp_socket;
static struct wr_sockaddr addr; 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]; static void lldp_header_tlv(uint8_t tlv_type, uint8_t tlv_len)
lldpdu[lldpdu_len] = tlv_type * 2; {
lldpdu[lldpdu_len + LLDP_SUBTYPE] = tlv_type_len[tlv_type]; lldpdu[lldpdu_len] = tlv_type << 1;
lldpdu[lldpdu_len + LLDP_SUBTYPE] = tlv_len;
lldpdu_len += LLDP_HEADER; 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) { static void lldp_add_tlv(int tlv_type) {
unsigned char mac[6]; uint8_t mac[6];
unsigned char ipWR[4]; unsigned char ipWR[4];
int tlv_len = 0;
switch(tlv_type) {
case END_LLDP: switch (tlv_type) {
/* header */ case END_LLDP:
lldp_header_tlv(tlv_type); /* End TLV */
/* header */
/* End TLV */ lldp_header_tlv(tlv_type, tlv_len);
memcpy(lldpdu + lldpdu_len, 0x0, tlv_type_len[tlv_type]);
break; break;
case CHASSIS_ID: case CHASSIS_ID:
/* header */ tlv_len = CHASSIS_ID_TLV_LEN;
lldp_header_tlv(tlv_type); /* header */
lldp_header_tlv(tlv_type, tlv_len);
/* TLV Chassis Component */
lldpdu[lldpdu_len] = 4; /* 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); get_mac_addr(mac);
memcpy(lldpdu + (lldpdu_len + LLDP_SUBTYPE), mac, 6); pp_sprintf(buf,
break; "%02x:%02x:%02x:%02x:%02x:%02x",
case PORT_ID: mac[0], mac[1], mac[2], mac[3],
/* header */ mac[4], mac[5]);
lldp_header_tlv(tlv_type); tlv_len = 17;
strncpy((char *)(lldpdu + lldpdu_len + LLDP_HEADER),
/* TLV Interce Alias */ (char *)buf, tlv_len);
lldpdu[lldpdu_len] = 7; }
strcpy(lldpdu + (lldpdu_len + LLDP_SUBTYPE), "WR Port"); /* header */
break; lldp_header_tlv(tlv_type, tlv_len);
case TTL:
/* header */
lldp_header_tlv(tlv_type);
/* TLV Time to Live */ break;
lldpdu[lldpdu_len + LLDP_SUBTYPE] = 0x20; /* sec */ }
break; case SYS_DESCR:
case PORT: tlv_len = 0; /* will be calculated later */
/* header */ uint8_t *pdu_p;
lldp_header_tlv(tlv_type);
/* set the pointer after TLV's header */
/* TLV Info srting */ pdu_p = &lldpdu[lldpdu_len + LLDP_HEADER];
if (HAS_IP) {
getIP(ipWR); /* TLV Info srting */
char buf[32]; strncpy((char *)(pdu_p), wrc_hw_name, HW_NAME_LENGTH - 1);
format_ip(buf, ipWR); pdu_p += strnlen(wrc_hw_name, HW_NAME_LENGTH - 1);
strcpy(lldpdu + lldpdu_len, buf); 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; break;
case SYS_NAME: }
/* header */ /* TODO: dynamic len */
lldp_header_tlv(tlv_type); tlv_len = 0xc;
/* header */
/* TLV Info srting */ lldp_header_tlv(tlv_type, tlv_len);
/* TODO get host system name from wr-core outer world */
strcpy(lldpdu + lldpdu_len, "WR PTP Core"); /* 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; static void lldp_update(void)
case SYS_DESCR: {
/* header */ int i;
lldp_header_tlv(tlv_type); /* add mandatory LLDP TLVs */
memset(lldpdu, 0x0, LLDP_MAX_PKT_LEN);
lldpdu_len = 0;
/* TLV Info srting */ pp_printf("lldp update\n");
strcpy(lldpdu + lldpdu_len, build_revision); /* add all TLV's */
break; for (i = CHASSIS_ID; i <= MNG_ADD; i++)
case SYS_CAPLTY: lldp_add_tlv(i);
/* header */
lldp_header_tlv(tlv_type);
/* TLV Info string */ /* end TLVs */
memset(lldpdu + lldpdu_len, 0x0, 4); lldp_add_tlv(END_LLDP);
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;
}
} }
static void lldp_init(void) static void lldp_init(void)
{ {
struct wr_sockaddr saddr; struct wr_sockaddr saddr;
int i;
/* LLDP: raw ethernet*/ /* LLDP: raw ethernet*/
memset(&saddr, 0x0, sizeof(saddr)); memset(&saddr, 0x0, sizeof(saddr));
...@@ -145,33 +234,38 @@ static void lldp_init(void) ...@@ -145,33 +234,38 @@ static void lldp_init(void)
memset(&addr, 0x0, sizeof(struct wr_sockaddr)); memset(&addr, 0x0, sizeof(struct wr_sockaddr));
memcpy(addr.mac, LLDP_MCAST_MAC, 6); memcpy(addr.mac, LLDP_MCAST_MAC, 6);
/* add mandatory LLDP TLVs */ lldp_update();
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);
} }
static int lldp_poll(void) 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 */ /* 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)) { /* Update only when IP or MAC changed */
lldp_add_tlv(PORT); /* TODO: or VLAN changed */
/* update other dynamic TLVs */ 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; ticks = 0;
return 1; return 1;
......
...@@ -11,24 +11,41 @@ ...@@ -11,24 +11,41 @@
#ifndef __LLDP_H #ifndef __LLDP_H
#define __LLDP_H #define __LLDP_H
#include "minic.h"
#define LLDP_MCAST_MAC "\x01\x80\xC2\x00\x00\x0E" #define LLDP_MCAST_MAC "\x01\x80\xC2\x00\x00\x0E" /* 802.1AB-2005,
#define LLDP_ETH_TYP 0x88CC Table 8-1 */
#define LLDP_ETH_TYP 0x88CC /* 802.1AB-2005, Table 8-2 */
#define LLDP_PKT_LEN 0x9E /* 158 bytes */
#define TLV_MAX 0xA #define LLDP_MAX_PKT_LEN 0x9E /* 158 bytes */
#define LLDP_HEADER 0x2 #define TLV_MAX 0xA
#define LLDP_SUBTYPE 0x1 #define LLDP_HEADER 0x2
#define IF_SUBTYPE 0x6 #define LLDP_SUBTYPE 0x1
#define IF_NUM 0x10 #define MNT_IF_SUBTYPE 0x6
#define MNT_IF_NUM 10
#define LLDP_TX_FQ 1000
#define LLDP_TX_TICK_INTERVAL 1000
enum TLV_TYPE { END_LLDP = 0, /* mandatory TLVs */
#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, CHASSIS_ID,
PORT_ID, PORT_ID,
TTL, TTL,
PORT, /* optional TLVs */ PORT, /* optional TLVs */
SYS_NAME, SYS_NAME,
SYS_DESCR, SYS_DESCR,
SYS_CAPLTY, SYS_CAPLTY,
...@@ -36,26 +53,4 @@ enum TLV_TYPE { END_LLDP = 0, /* mandatory TLVs */ ...@@ -36,26 +53,4 @@ enum TLV_TYPE { END_LLDP = 0, /* mandatory TLVs */
USER_DEF 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 */ #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