LCOV - code coverage report
Current view: top level - network - networkd-lldp-tx.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 15 202 7.4 %
Date: 2019-08-23 13:36:53 Functions: 2 9 22.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 8 164 4.9 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <endian.h>
       4                 :            : #include <inttypes.h>
       5                 :            : #include <net/if.h>
       6                 :            : #include <net/if_arp.h>
       7                 :            : #include <string.h>
       8                 :            : 
       9                 :            : #include "alloc-util.h"
      10                 :            : #include "env-file.h"
      11                 :            : #include "fd-util.h"
      12                 :            : #include "hostname-util.h"
      13                 :            : #include "missing_network.h"
      14                 :            : #include "networkd-link.h"
      15                 :            : #include "networkd-lldp-tx.h"
      16                 :            : #include "networkd-manager.h"
      17                 :            : #include "parse-util.h"
      18                 :            : #include "random-util.h"
      19                 :            : #include "socket-util.h"
      20                 :            : #include "string-util.h"
      21                 :            : #include "unaligned.h"
      22                 :            : 
      23                 :            : /* The LLDP spec calls this "txFastInit", see 9.2.5.19 */
      24                 :            : #define LLDP_TX_FAST_INIT 4U
      25                 :            : 
      26                 :            : /* The LLDP spec calls this "msgTxHold", see 9.2.5.6 */
      27                 :            : #define LLDP_TX_HOLD 4U
      28                 :            : 
      29                 :            : /* The jitter range to add, see 9.2.2. */
      30                 :            : #define LLDP_JITTER_USEC (400U * USEC_PER_MSEC)
      31                 :            : 
      32                 :            : /* The LLDP spec calls this msgTxInterval, but we subtract half the jitter off it. */
      33                 :            : #define LLDP_TX_INTERVAL_USEC (30U * USEC_PER_SEC - LLDP_JITTER_USEC / 2)
      34                 :            : 
      35                 :            : /* The LLDP spec calls this msgFastTx, but we subtract half the jitter off it. */
      36                 :            : #define LLDP_FAST_TX_USEC (1U * USEC_PER_SEC - LLDP_JITTER_USEC / 2)
      37                 :            : 
      38                 :            : static const struct ether_addr lldp_multicast_addr[_LLDP_EMIT_MAX] = {
      39                 :            :         [LLDP_EMIT_NEAREST_BRIDGE]  = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x0e }},
      40                 :            :         [LLDP_EMIT_NON_TPMR_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }},
      41                 :            :         [LLDP_EMIT_CUSTOMER_BRIDGE] = {{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 }},
      42                 :            : };
      43                 :            : 
      44                 :          0 : bool link_lldp_emit_enabled(Link *link) {
      45         [ #  # ]:          0 :         assert(link);
      46                 :            : 
      47         [ #  # ]:          0 :         if (link->flags & IFF_LOOPBACK)
      48                 :          0 :                 return false;
      49                 :            : 
      50         [ #  # ]:          0 :         if (link->iftype != ARPHRD_ETHER)
      51                 :          0 :                 return false;
      52                 :            : 
      53         [ #  # ]:          0 :         if (!link->network)
      54                 :          0 :                 return false;
      55                 :            : 
      56                 :          0 :         return link->network->lldp_emit != LLDP_EMIT_NO;
      57                 :            : }
      58                 :            : 
      59                 :          0 : static int lldp_write_tlv_header(uint8_t **p, uint8_t id, size_t sz) {
      60         [ #  # ]:          0 :         assert(p);
      61                 :            : 
      62         [ #  # ]:          0 :         if (id > 127)
      63                 :          0 :                 return -EBADMSG;
      64         [ #  # ]:          0 :         if (sz > 511)
      65                 :          0 :                 return -ENOBUFS;
      66                 :            : 
      67                 :          0 :         (*p)[0] = (id << 1) | !!(sz & 256);
      68                 :          0 :         (*p)[1] = sz & 255;
      69                 :            : 
      70                 :          0 :         *p = *p + 2;
      71                 :          0 :         return 0;
      72                 :            : }
      73                 :            : 
      74                 :          0 : static int lldp_make_packet(
      75                 :            :                 LLDPEmit mode,
      76                 :            :                 const struct ether_addr *hwaddr,
      77                 :            :                 const char *machine_id,
      78                 :            :                 const char *ifname,
      79                 :            :                 uint16_t ttl,
      80                 :            :                 const char *port_description,
      81                 :            :                 const char *hostname,
      82                 :            :                 const char *pretty_hostname,
      83                 :            :                 uint16_t system_capabilities,
      84                 :            :                 uint16_t enabled_capabilities,
      85                 :            :                 void **ret, size_t *sz) {
      86                 :            : 
      87                 :          0 :         size_t machine_id_length, ifname_length, port_description_length = 0, hostname_length = 0, pretty_hostname_length = 0;
      88                 :          0 :         _cleanup_free_ void *packet = NULL;
      89                 :            :         struct ether_header *h;
      90                 :            :         uint8_t *p;
      91                 :            :         size_t l;
      92                 :            :         int r;
      93                 :            : 
      94         [ #  # ]:          0 :         assert(mode > LLDP_EMIT_NO);
      95         [ #  # ]:          0 :         assert(mode < _LLDP_EMIT_MAX);
      96         [ #  # ]:          0 :         assert(hwaddr);
      97         [ #  # ]:          0 :         assert(machine_id);
      98         [ #  # ]:          0 :         assert(ifname);
      99         [ #  # ]:          0 :         assert(ret);
     100         [ #  # ]:          0 :         assert(sz);
     101                 :            : 
     102                 :          0 :         machine_id_length = strlen(machine_id);
     103                 :          0 :         ifname_length = strlen(ifname);
     104                 :            : 
     105         [ #  # ]:          0 :         if (port_description)
     106                 :          0 :                 port_description_length = strlen(port_description);
     107                 :            : 
     108         [ #  # ]:          0 :         if (hostname)
     109                 :          0 :                 hostname_length = strlen(hostname);
     110                 :            : 
     111         [ #  # ]:          0 :         if (pretty_hostname)
     112                 :          0 :                 pretty_hostname_length = strlen(pretty_hostname);
     113                 :            : 
     114                 :          0 :         l = sizeof(struct ether_header) +
     115                 :            :                 /* Chassis ID */
     116                 :            :                 2 + 1 + machine_id_length +
     117                 :            :                 /* Port ID */
     118                 :          0 :                 2 + 1 + ifname_length +
     119                 :            :                 /* TTL */
     120                 :            :                 2 + 2 +
     121                 :            :                 /* System Capabilities */
     122                 :            :                 2 + 4 +
     123                 :            :                 /* End */
     124                 :            :                 2;
     125                 :            : 
     126                 :            :         /* Port Description */
     127         [ #  # ]:          0 :         if (port_description)
     128                 :          0 :                 l += 2 + port_description_length;
     129                 :            : 
     130                 :            :         /* System Name */
     131         [ #  # ]:          0 :         if (hostname)
     132                 :          0 :                 l += 2 + hostname_length;
     133                 :            : 
     134                 :            :         /* System Description */
     135         [ #  # ]:          0 :         if (pretty_hostname)
     136                 :          0 :                 l += 2 + pretty_hostname_length;
     137                 :            : 
     138                 :          0 :         packet = malloc(l);
     139         [ #  # ]:          0 :         if (!packet)
     140                 :          0 :                 return -ENOMEM;
     141                 :            : 
     142                 :          0 :         h = (struct ether_header*) packet;
     143                 :          0 :         h->ether_type = htobe16(ETHERTYPE_LLDP);
     144                 :          0 :         memcpy(h->ether_dhost, lldp_multicast_addr + mode, ETH_ALEN);
     145                 :          0 :         memcpy(h->ether_shost, hwaddr, ETH_ALEN);
     146                 :            : 
     147                 :          0 :         p = (uint8_t*) packet + sizeof(struct ether_header);
     148                 :            : 
     149                 :          0 :         r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_CHASSIS_ID, 1 + machine_id_length);
     150         [ #  # ]:          0 :         if (r < 0)
     151                 :          0 :                 return r;
     152                 :          0 :         *(p++) = SD_LLDP_CHASSIS_SUBTYPE_LOCALLY_ASSIGNED;
     153                 :          0 :         p = mempcpy(p, machine_id, machine_id_length);
     154                 :            : 
     155                 :          0 :         r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_ID, 1 + ifname_length);
     156         [ #  # ]:          0 :         if (r < 0)
     157                 :          0 :                 return r;
     158                 :          0 :         *(p++) = SD_LLDP_PORT_SUBTYPE_INTERFACE_NAME;
     159                 :          0 :         p = mempcpy(p, ifname, ifname_length);
     160                 :            : 
     161                 :          0 :         r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_TTL, 2);
     162         [ #  # ]:          0 :         if (r < 0)
     163                 :          0 :                 return r;
     164                 :          0 :         unaligned_write_be16(p, ttl);
     165                 :          0 :         p += 2;
     166                 :            : 
     167         [ #  # ]:          0 :         if (port_description) {
     168                 :          0 :                 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_PORT_DESCRIPTION, port_description_length);
     169         [ #  # ]:          0 :                 if (r < 0)
     170                 :          0 :                         return r;
     171                 :          0 :                 p = mempcpy(p, port_description, port_description_length);
     172                 :            :         }
     173                 :            : 
     174         [ #  # ]:          0 :         if (hostname) {
     175                 :          0 :                 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_NAME, hostname_length);
     176         [ #  # ]:          0 :                 if (r < 0)
     177                 :          0 :                         return r;
     178                 :          0 :                 p = mempcpy(p, hostname, hostname_length);
     179                 :            :         }
     180                 :            : 
     181         [ #  # ]:          0 :         if (pretty_hostname) {
     182                 :          0 :                 r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_DESCRIPTION, pretty_hostname_length);
     183         [ #  # ]:          0 :                 if (r < 0)
     184                 :          0 :                         return r;
     185                 :          0 :                 p = mempcpy(p, pretty_hostname, pretty_hostname_length);
     186                 :            :         }
     187                 :            : 
     188                 :          0 :         r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_SYSTEM_CAPABILITIES, 4);
     189         [ #  # ]:          0 :         if (r < 0)
     190                 :          0 :                 return r;
     191                 :          0 :         unaligned_write_be16(p, system_capabilities);
     192                 :          0 :         p += 2;
     193                 :          0 :         unaligned_write_be16(p, enabled_capabilities);
     194                 :          0 :         p += 2;
     195                 :            : 
     196                 :          0 :         r = lldp_write_tlv_header(&p, SD_LLDP_TYPE_END, 0);
     197         [ #  # ]:          0 :         if (r < 0)
     198                 :          0 :                 return r;
     199                 :            : 
     200         [ #  # ]:          0 :         assert(p == (uint8_t*) packet + l);
     201                 :            : 
     202                 :          0 :         *ret = TAKE_PTR(packet);
     203                 :          0 :         *sz = l;
     204                 :            : 
     205                 :          0 :         return 0;
     206                 :            : }
     207                 :            : 
     208                 :          0 : static int lldp_send_packet(
     209                 :            :                 int ifindex,
     210                 :            :                 const struct ether_addr *address,
     211                 :            :                 const void *packet,
     212                 :            :                 size_t packet_size) {
     213                 :            : 
     214                 :          0 :         union sockaddr_union sa = {
     215                 :            :                 .ll.sll_family = AF_PACKET,
     216                 :          0 :                 .ll.sll_protocol = htobe16(ETHERTYPE_LLDP),
     217                 :            :                 .ll.sll_ifindex = ifindex,
     218                 :            :                 .ll.sll_halen = ETH_ALEN,
     219                 :            :         };
     220                 :            : 
     221                 :          0 :         _cleanup_close_ int fd = -1;
     222                 :            :         ssize_t l;
     223                 :            : 
     224         [ #  # ]:          0 :         assert(ifindex > 0);
     225         [ #  # ]:          0 :         assert(address);
     226   [ #  #  #  # ]:          0 :         assert(packet || packet_size <= 0);
     227                 :            : 
     228                 :          0 :         memcpy(sa.ll.sll_addr, address, ETH_ALEN);
     229                 :            : 
     230                 :          0 :         fd = socket(PF_PACKET, SOCK_RAW|SOCK_CLOEXEC, IPPROTO_RAW);
     231         [ #  # ]:          0 :         if (fd < 0)
     232                 :          0 :                 return -errno;
     233                 :            : 
     234                 :          0 :         l = sendto(fd, packet, packet_size, MSG_NOSIGNAL, &sa.sa, sizeof(sa.ll));
     235         [ #  # ]:          0 :         if (l < 0)
     236                 :          0 :                 return -errno;
     237                 :            : 
     238         [ #  # ]:          0 :         if ((size_t) l != packet_size)
     239                 :          0 :                 return -EIO;
     240                 :            : 
     241                 :          0 :         return 0;
     242                 :            : }
     243                 :            : 
     244                 :          0 : static int link_send_lldp(Link *link) {
     245                 :            :         char machine_id_string[SD_ID128_STRING_MAX];
     246                 :          0 :         _cleanup_free_ char *hostname = NULL, *pretty_hostname = NULL;
     247                 :          0 :         _cleanup_free_ void *packet = NULL;
     248                 :          0 :         size_t packet_size = 0;
     249                 :            :         sd_id128_t machine_id;
     250                 :            :         uint16_t caps;
     251                 :            :         usec_t ttl;
     252                 :            :         int r;
     253                 :            : 
     254         [ #  # ]:          0 :         assert(link);
     255                 :            : 
     256   [ #  #  #  # ]:          0 :         if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO)
     257                 :          0 :                 return 0;
     258                 :            : 
     259         [ #  # ]:          0 :         assert(link->network->lldp_emit < _LLDP_EMIT_MAX);
     260                 :            : 
     261                 :          0 :         r = sd_id128_get_machine(&machine_id);
     262         [ #  # ]:          0 :         if (r < 0)
     263                 :          0 :                 return r;
     264                 :            : 
     265                 :          0 :         (void) gethostname_strict(&hostname);
     266                 :          0 :         (void) parse_env_file(NULL, "/etc/machine-info", "PRETTY_HOSTNAME", &pretty_hostname);
     267                 :            : 
     268                 :            :         assert_cc(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1 <= (UINT16_MAX - 1) * USEC_PER_SEC);
     269                 :          0 :         ttl = DIV_ROUND_UP(LLDP_TX_INTERVAL_USEC * LLDP_TX_HOLD + 1, USEC_PER_SEC);
     270                 :            : 
     271   [ #  #  #  # ]:          0 :         caps = (link->network && link->network->ip_forward != ADDRESS_FAMILY_NO) ?
     272                 :            :                 SD_LLDP_SYSTEM_CAPABILITIES_ROUTER :
     273                 :            :                 SD_LLDP_SYSTEM_CAPABILITIES_STATION;
     274                 :            : 
     275                 :          0 :         r = lldp_make_packet(link->network->lldp_emit,
     276                 :          0 :                              &link->mac,
     277                 :          0 :                              sd_id128_to_string(machine_id, machine_id_string),
     278                 :          0 :                              link->ifname,
     279                 :          0 :                              (uint16_t) ttl,
     280         [ #  # ]:          0 :                              link->network ? link->network->description : NULL,
     281                 :            :                              hostname,
     282                 :            :                              pretty_hostname,
     283                 :            :                              SD_LLDP_SYSTEM_CAPABILITIES_STATION|SD_LLDP_SYSTEM_CAPABILITIES_BRIDGE|SD_LLDP_SYSTEM_CAPABILITIES_ROUTER,
     284                 :            :                              caps,
     285                 :            :                              &packet, &packet_size);
     286         [ #  # ]:          0 :         if (r < 0)
     287                 :          0 :                 return r;
     288                 :            : 
     289                 :          0 :         return lldp_send_packet(link->ifindex, lldp_multicast_addr + link->network->lldp_emit, packet, packet_size);
     290                 :            : }
     291                 :            : 
     292                 :          0 : static int on_lldp_timer(sd_event_source *s, usec_t t, void *userdata) {
     293                 :          0 :         Link *link = userdata;
     294                 :            :         usec_t current, delay, next;
     295                 :            :         int r;
     296                 :            : 
     297         [ #  # ]:          0 :         assert(s);
     298         [ #  # ]:          0 :         assert(userdata);
     299                 :            : 
     300   [ #  #  #  # ]:          0 :         log_link_debug(link, "Sending LLDP packet...");
     301                 :            : 
     302                 :          0 :         r = link_send_lldp(link);
     303         [ #  # ]:          0 :         if (r < 0)
     304   [ #  #  #  # ]:          0 :                 log_link_debug_errno(link, r, "Failed to send LLDP packet, ignoring: %m");
     305                 :            : 
     306         [ #  # ]:          0 :         if (link->lldp_tx_fast > 0)
     307                 :          0 :                 link->lldp_tx_fast--;
     308                 :            : 
     309         [ #  # ]:          0 :         assert_se(sd_event_now(sd_event_source_get_event(s), clock_boottime_or_monotonic(), &current) >= 0);
     310                 :            : 
     311         [ #  # ]:          0 :         delay = link->lldp_tx_fast > 0 ? LLDP_FAST_TX_USEC : LLDP_TX_INTERVAL_USEC;
     312                 :          0 :         next = usec_add(usec_add(current, delay), (usec_t) random_u64() % LLDP_JITTER_USEC);
     313                 :            : 
     314                 :          0 :         r = sd_event_source_set_time(s, next);
     315         [ #  # ]:          0 :         if (r < 0)
     316   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "Failed to restart LLDP timer: %m");
     317                 :            : 
     318                 :          0 :         r = sd_event_source_set_enabled(s, SD_EVENT_ONESHOT);
     319         [ #  # ]:          0 :         if (r < 0)
     320   [ #  #  #  # ]:          0 :                 return log_link_error_errno(link, r, "Failed to enable LLDP timer: %m");
     321                 :            : 
     322                 :          0 :         return 0;
     323                 :            : }
     324                 :            : 
     325                 :          0 : int link_lldp_emit_start(Link *link) {
     326                 :            :         usec_t next;
     327                 :            :         int r;
     328                 :            : 
     329         [ #  # ]:          0 :         assert(link);
     330                 :            : 
     331   [ #  #  #  # ]:          0 :         if (!link->network || link->network->lldp_emit == LLDP_EMIT_NO) {
     332                 :          0 :                 link_lldp_emit_stop(link);
     333                 :          0 :                 return 0;
     334                 :            :         }
     335                 :            : 
     336                 :            :         /* Starts the LLDP transmission in "fast" mode. If it is already started, turns "fast" mode back on again. */
     337                 :            : 
     338                 :          0 :         link->lldp_tx_fast = LLDP_TX_FAST_INIT;
     339                 :            : 
     340                 :          0 :         next = usec_add(usec_add(now(clock_boottime_or_monotonic()), LLDP_FAST_TX_USEC),
     341                 :          0 :                      (usec_t) random_u64() % LLDP_JITTER_USEC);
     342                 :            : 
     343         [ #  # ]:          0 :         if (link->lldp_emit_event_source) {
     344                 :            :                 usec_t old;
     345                 :            : 
     346                 :            :                 /* Lower the timeout, maybe */
     347                 :          0 :                 r = sd_event_source_get_time(link->lldp_emit_event_source, &old);
     348         [ #  # ]:          0 :                 if (r < 0)
     349                 :          0 :                         return r;
     350                 :            : 
     351         [ #  # ]:          0 :                 if (old <= next)
     352                 :          0 :                         return 0;
     353                 :            : 
     354                 :          0 :                 return sd_event_source_set_time(link->lldp_emit_event_source, next);
     355                 :            :         } else {
     356                 :          0 :                 r = sd_event_add_time(
     357                 :          0 :                                 link->manager->event,
     358                 :            :                                 &link->lldp_emit_event_source,
     359                 :            :                                 clock_boottime_or_monotonic(),
     360                 :            :                                 next,
     361                 :            :                                 0,
     362                 :            :                                 on_lldp_timer,
     363                 :            :                                 link);
     364         [ #  # ]:          0 :                 if (r < 0)
     365                 :          0 :                         return r;
     366                 :            : 
     367                 :          0 :                 (void) sd_event_source_set_description(link->lldp_emit_event_source, "lldp-tx");
     368                 :            :         }
     369                 :            : 
     370                 :          0 :         return 0;
     371                 :            : }
     372                 :            : 
     373                 :         56 : void link_lldp_emit_stop(Link *link) {
     374         [ -  + ]:         56 :         assert(link);
     375                 :            : 
     376                 :         56 :         link->lldp_emit_event_source = sd_event_source_unref(link->lldp_emit_event_source);
     377                 :         56 : }
     378                 :            : 
     379                 :         12 : int config_parse_lldp_emit(
     380                 :            :                 const char *unit,
     381                 :            :                 const char *filename,
     382                 :            :                 unsigned line,
     383                 :            :                 const char *section,
     384                 :            :                 unsigned section_line,
     385                 :            :                 const char *lvalue,
     386                 :            :                 int ltype,
     387                 :            :                 const char *rvalue,
     388                 :            :                 void *data,
     389                 :            :                 void *userdata) {
     390                 :            : 
     391                 :         12 :         LLDPEmit *emit = data;
     392                 :            :         int r;
     393                 :            : 
     394         [ -  + ]:         12 :         assert(filename);
     395         [ -  + ]:         12 :         assert(lvalue);
     396         [ -  + ]:         12 :         assert(rvalue);
     397                 :            : 
     398         [ -  + ]:         12 :         if (isempty(rvalue))
     399                 :          0 :                 *emit = LLDP_EMIT_NO;
     400         [ -  + ]:         12 :         else if (streq(rvalue, "nearest-bridge"))
     401                 :          0 :                 *emit = LLDP_EMIT_NEAREST_BRIDGE;
     402         [ -  + ]:         12 :         else if (streq(rvalue, "non-tpmr-bridge"))
     403                 :          0 :                 *emit = LLDP_EMIT_NON_TPMR_BRIDGE;
     404         [ +  - ]:         12 :         else if (streq(rvalue, "customer-bridge"))
     405                 :         12 :                 *emit = LLDP_EMIT_CUSTOMER_BRIDGE;
     406                 :            :         else {
     407                 :          0 :                 r = parse_boolean(rvalue);
     408         [ #  # ]:          0 :                 if (r < 0) {
     409         [ #  # ]:          0 :                         log_syntax(unit, LOG_ERR, filename, line, 0, "Failed to parse LLDP emission setting, ignoring: %s", rvalue);
     410                 :          0 :                         return 0;
     411                 :            :                 }
     412                 :            : 
     413                 :          0 :                 *emit = r ? LLDP_EMIT_NEAREST_BRIDGE : LLDP_EMIT_NO;
     414                 :            :         }
     415                 :            : 
     416                 :         12 :         return 0;
     417                 :            : }

Generated by: LCOV version 1.14