int ctl_msg_recv(int fd, enum hmsg_type *type, void **t) { int n, flags = -1; struct hmsg_header hdr; *type = NONE; *t = NULL; /* First, we read the header to know the size of the message */ if ((n = read(fd, &hdr, sizeof(struct hmsg_header))) == -1) { LLOG_WARN("unable to read message header"); return -1; } if (n == 0) /* Remote closed the connection. */ return -1; if (n < sizeof(struct hmsg_header)) { LLOG_WARNX("message received too short (%d)", n); goto recv_error; } if (hdr.len > (1<<15)) { LLOG_WARNX("message received is too large"); goto recv_error; } if (hdr.len == 0) { /* No answer */ *type = hdr.type; return 0; } /* Now, we read the remaining message. We need to use non-blocking stuff * just in case the message was truncated. */ if ((*t = malloc(hdr.len)) == NULL) { LLOG_WARNX("not enough space available for incoming message"); goto recv_error; } if ((flags = fcntl(fd, F_GETFL, 0)) == -1 || fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) { LLOG_WARN("unable to set socket access mode to non blocking"); goto recv_error; } if ((n = read(fd, *t, hdr.len)) == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { LLOG_WARNX("message seems truncated"); goto recv_error; } LLOG_WARN("unable to read incoming request"); goto recv_error; } if (n != hdr.len) { LLOG_WARNX("received message is too short (%d < %zu)", n, hdr.len); goto recv_error; } fcntl(fd, F_SETFL, flags); /* No error check */ *type = hdr.type; return hdr.len; recv_error: free(*t); *t = NULL; if (flags != -1) fcntl(fd, F_SETFL, flags); return -1; }
int ctl_msg_send_recv(int fd, enum hmsg_type type, void *input, struct marshal_info *input_mi, void **output, struct marshal_info *output_mi) { int n, input_len = 0; void *input_buffer = NULL; void *serialized = NULL; enum hmsg_type received_type; /* Serialize */ if (input) { input_len = marshal_serialize_(input_mi, input, &input_buffer, 0, NULL, 0); if (input_len <= 0) { LLOG_WARNX("unable to serialize input data"); return -1; } } /* Send request */ if (ctl_msg_send(fd, type, input_buffer, input_len) == -1) { LLOG_WARN("unable to send request"); goto send_recv_error; } free(input_buffer); input_buffer = NULL; /* Receive answer */ if ((n = ctl_msg_recv(fd, &received_type, &serialized)) == -1) goto send_recv_error; /* Check type */ if (received_type != type) { LLOG_WARNX("incorrect received message type (expected: %d, received: %d)", type, received_type); goto send_recv_error; } /* Unserialize */ if (output == NULL) { free(serialized); return 0; } if (n == 0) { LLOG_WARNX("no payload available in answer"); goto send_recv_error; } if (marshal_unserialize_(output_mi, serialized, n, output, NULL, 0, 0) <= 0) { LLOG_WARNX("unable to deserialize received data"); goto send_recv_error; } /* All done. */ return 0; send_recv_error: free(serialized); free(input_buffer); return -1; }
int ctl_connect(char *name) { int s; struct sockaddr_un su; int rc; if ((s = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) return -1; su.sun_family = AF_UNIX; strlcpy(su.sun_path, name, UNIX_PATH_MAX); if (connect(s, (struct sockaddr *)&su, sizeof(struct sockaddr_un)) == -1) { rc = errno; LLOG_WARN("unable to connect to socket " LLDPD_CTL_SOCKET); errno = rc; return -1; } return s; }
static int cdp_send(struct lldpd *global, struct lldpd_hardware *hardware, int version) { struct lldpd_chassis *chassis; u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; u_int8_t llcorg[] = LLC_ORG_CISCO; #ifdef ENABLE_FDP char *capstr; #endif u_int16_t checksum; int length; u_int32_t cap; u_int8_t *packet; u_int8_t *pos, *pos_len_eh, *pos_llc, *pos_cdp, *pos_checksum, *tlv, *end; chassis = hardware->h_lport.p_chassis; #ifdef ENABLE_FDP if (version == 0) { /* With FDP, change multicast address and LLC PID */ const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR; const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY; memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr)); memcpy(llcorg, fdpllcorg, sizeof(llcorg)); } #endif length = hardware->h_mtu; if ((packet = (u_int8_t*)malloc(length)) == NULL) return ENOMEM; memset(packet, 0, length); pos = packet; /* Ethernet header */ if (!( POKE_BYTES(mcastaddr, sizeof(mcastaddr)) && POKE_BYTES(&hardware->h_lladdr, sizeof(hardware->h_lladdr)) && POKE_SAVE(pos_len_eh) && /* We compute the len later */ POKE_UINT16(0))) goto toobig; /* LLC */ if (!( POKE_SAVE(pos_llc) && POKE_UINT8(0xaa) && /* SSAP */ POKE_UINT8(0xaa) && /* DSAP */ POKE_UINT8(0x03) && /* Control field */ POKE_BYTES(llcorg, sizeof(llcorg)) && POKE_UINT16(LLC_PID_CDP))) goto toobig; /* CDP header */ if (!( POKE_SAVE(pos_cdp) && POKE_UINT8((version == 0)?1:version) && POKE_UINT8(chassis->c_ttl) && POKE_SAVE(pos_checksum) && /* Save checksum position */ POKE_UINT16(0))) goto toobig; /* Chassis ID */ if (!( POKE_START_CDP_TLV(CDP_TLV_CHASSIS) && POKE_BYTES(chassis->c_name, strlen(chassis->c_name)) && POKE_END_CDP_TLV)) goto toobig; /* Adresses */ if (!( POKE_START_CDP_TLV(CDP_TLV_ADDRESSES) && POKE_UINT32(1) && /* We ship only one address */ POKE_UINT8(1) && /* Type: NLPID */ POKE_UINT8(1) && /* Length: 1 */ POKE_UINT8(CDP_ADDRESS_PROTO_IP) && /* IP */ POKE_UINT16(sizeof(struct in_addr)) && /* Address length */ POKE_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)) && POKE_END_CDP_TLV)) goto toobig; /* Port ID */ if (!( POKE_START_CDP_TLV(CDP_TLV_PORT) && POKE_BYTES(hardware->h_lport.p_descr, strlen(hardware->h_lport.p_descr)) && POKE_END_CDP_TLV)) goto toobig; /* Capabilities */ if (version != 0) { cap = 0; if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) cap |= CDP_CAP_ROUTER; if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) cap |= CDP_CAP_SWITCH; cap |= CDP_CAP_HOST; if (!( POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) && POKE_UINT32(cap) && POKE_END_CDP_TLV)) goto toobig; #ifdef ENABLE_FDP } else { /* With FDP, it seems that a string is used in place of an int */ if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) capstr = "Router"; else if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) capstr = "Switch"; else if (chassis->c_cap_enabled & LLDP_CAP_REPEATER) capstr = "Bridge"; else capstr = "Host"; if (!( POKE_START_CDP_TLV(CDP_TLV_CAPABILITIES) && POKE_BYTES(capstr, strlen(capstr)) && POKE_END_CDP_TLV)) goto toobig; #endif } /* Software version */ if (!( POKE_START_CDP_TLV(CDP_TLV_SOFTWARE) && POKE_BYTES(chassis->c_descr, strlen(chassis->c_descr)) && POKE_END_CDP_TLV)) goto toobig; /* Platform */ if (!( POKE_START_CDP_TLV(CDP_TLV_PLATFORM) && POKE_BYTES("Linux", strlen("Linux")) && POKE_END_CDP_TLV)) goto toobig; POKE_SAVE(end); /* Compute len and checksum */ POKE_RESTORE(pos_len_eh); if (!(POKE_UINT16(end - pos_llc))) goto toobig; checksum = frame_checksum(pos_cdp, end - pos_cdp, (version != 0) ? 1 : 0); POKE_RESTORE(pos_checksum); if (!(POKE_UINT16(ntohs(checksum)))) goto toobig; if (hardware->h_ops->send(global, hardware, (char *)packet, end - packet) == -1) { LLOG_WARN("unable to send packet on real device for %s", hardware->h_ifname); free(packet); return ENETDOWN; } hardware->h_tx_cnt++; free(packet); return 0; toobig: free(packet); return -1; }
/* cdp_decode also decodes FDP */ int cdp_decode(struct lldpd *cfg, char *frame, int s, struct lldpd_hardware *hardware, struct lldpd_chassis **newchassis, struct lldpd_port **newport) { struct lldpd_chassis *chassis; struct lldpd_port *port; #if 0 u_int16_t cksum; #endif u_int8_t *software = NULL, *platform = NULL; int software_len = 0, platform_len = 0, proto, version, nb, caps; const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR; #ifdef ENABLE_FDP const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR; int fdp = 0; #endif u_int8_t *pos, *tlv, *pos_address, *pos_next_address; int length, len_eth, tlv_type, tlv_len, addresses_len, address_len; if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { LLOG_WARN("failed to allocate remote chassis"); return -1; } if ((port = calloc(1, sizeof(struct lldpd_port))) == NULL) { LLOG_WARN("failed to allocate remote port"); free(chassis); return -1; } #ifdef ENABLE_DOT1 TAILQ_INIT(&port->p_vlans); #endif length = s; pos = (u_int8_t*)frame; if (length < 2*ETH_ALEN + sizeof(u_int16_t) /* Ethernet */ + 8 /* LLC */ + 4 /* CDP header */) { LLOG_WARNX("too short CDP/FDP frame received on %s", hardware->h_ifname); goto malformed; } if (PEEK_CMP(cdpaddr, sizeof(cdpaddr)) != 0) { #ifdef ENABLE_FDP PEEK_RESTORE((u_int8_t*)frame); if (PEEK_CMP(fdpaddr, sizeof(fdpaddr)) != 0) fdp = 1; else { #endif LLOG_INFO("frame not targeted at CDP/FDP multicast address received on %s", hardware->h_ifname); goto malformed; #ifdef ENABLE_FDP } #endif } PEEK_DISCARD(ETH_ALEN); /* Don't care of source address */ len_eth = PEEK_UINT16; if (len_eth > length) { LLOG_WARNX("incorrect 802.3 frame size reported on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(6); /* Skip beginning of LLC */ proto = PEEK_UINT16; if (proto != LLC_PID_CDP) { if ((proto != LLC_PID_DRIP) && (proto != LLC_PID_PAGP) && (proto != LLC_PID_PVSTP) && (proto != LLC_PID_UDLD) && (proto != LLC_PID_VTP) && (proto != LLC_PID_DTP) && (proto != LLC_PID_STP)) LLOG_DEBUG("incorrect LLC protocol ID received on %s", hardware->h_ifname); goto malformed; } #if 0 /* Check checksum */ cksum = frame_checksum(pos, len_eth - 8, #ifdef ENABLE_FDP !fdp /* fdp = 0 -> cisco checksum */ #else 1 /* cisco checksum */ #endif ); /* An off-by-one error may happen. Just ignore it */ if ((cksum != 0) && (cksum != 0xfffe)) { LLOG_INFO("incorrect CDP/FDP checksum for frame received on %s (%d)", hardware->h_ifname, cksum); goto malformed; } #endif /* Check version */ version = PEEK_UINT8; if ((version != 1) && (version != 2)) { LLOG_WARNX("incorrect CDP/FDP version (%d) for frame received on %s", version, hardware->h_ifname); goto malformed; } chassis->c_ttl = PEEK_UINT8; /* TTL */ PEEK_DISCARD_UINT16; /* Checksum, already checked */ while (length) { if (length < 4) { LLOG_WARNX("CDP/FDP TLV header is too large for " "frame received on %s", hardware->h_ifname); goto malformed; } tlv_type = PEEK_UINT16; tlv_len = PEEK_UINT16 - 4; PEEK_SAVE(tlv); if ((tlv_len < 0) || (length < tlv_len)) { LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame " "received on %s", hardware->h_ifname); goto malformed; } switch (tlv_type) { case CDP_TLV_CHASSIS: if ((chassis->c_name = (char *)calloc(1, tlv_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis name"); goto malformed; } PEEK_BYTES(chassis->c_name, tlv_len); chassis->c_id_subtype = LLDP_CHASSISID_SUBTYPE_LOCAL; if ((chassis->c_id = (char *)malloc(tlv_len)) == NULL) { LLOG_WARN("unable to allocate memory for chassis ID"); goto malformed; } memcpy(chassis->c_id, chassis->c_name, tlv_len); chassis->c_id_len = tlv_len; break; case CDP_TLV_ADDRESSES: CHECK_TLV_SIZE(4, "Address"); addresses_len = tlv_len - 4; for (nb = PEEK_UINT32; nb > 0; nb--) { PEEK_SAVE(pos_address); /* We first try to get the real length of the packet */ if (addresses_len < 2) { LLOG_WARN("too short address subframe " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD_UINT8; addresses_len--; address_len = PEEK_UINT8; addresses_len--; if (addresses_len < address_len + 2) { LLOG_WARN("too short address subframe " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(address_len); addresses_len -= address_len; address_len = PEEK_UINT16; addresses_len -= 2; if (addresses_len < address_len) { LLOG_WARN("too short address subframe " "received on %s", hardware->h_ifname); goto malformed; } PEEK_DISCARD(address_len); PEEK_SAVE(pos_next_address); /* Next, we go back and try to extract IPv4 address */ PEEK_RESTORE(pos_address); if ((PEEK_UINT8 == 1) && (PEEK_UINT8 == 1) && (PEEK_UINT8 == CDP_ADDRESS_PROTO_IP) && (PEEK_UINT16 == sizeof(struct in_addr)) && (chassis->c_mgmt.s_addr == INADDR_ANY)) PEEK_BYTES(&chassis->c_mgmt, sizeof(struct in_addr)); /* Go to the end of the address */ PEEK_RESTORE(pos_next_address); } break; case CDP_TLV_PORT: if ((port->p_descr = (char *)calloc(1, tlv_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for port description"); goto malformed; } PEEK_BYTES(port->p_descr, tlv_len); port->p_id_subtype = LLDP_PORTID_SUBTYPE_IFNAME; if ((port->p_id = (char *)calloc(1, tlv_len)) == NULL) { LLOG_WARN("unable to allocate memory for port ID"); goto malformed; } memcpy(port->p_id, port->p_descr, tlv_len); port->p_id_len = tlv_len; break; case CDP_TLV_CAPABILITIES: #ifdef ENABLE_FDP if (fdp) { /* Capabilities are string with FDP */ if (!strncmp("Router", (char*)pos, tlv_len)) chassis->c_cap_enabled = LLDP_CAP_ROUTER; else if (!strncmp("Switch", (char*)pos, tlv_len)) chassis->c_cap_enabled = LLDP_CAP_BRIDGE; else if (!strncmp("Bridge", (char*)pos, tlv_len)) chassis->c_cap_enabled = LLDP_CAP_REPEATER; else chassis->c_cap_enabled = LLDP_CAP_STATION; chassis->c_cap_available = chassis->c_cap_enabled; break; } #endif CHECK_TLV_SIZE(4, "Capabilities"); caps = PEEK_UINT32; if (caps & CDP_CAP_ROUTER) chassis->c_cap_enabled |= LLDP_CAP_ROUTER; if (caps & 0x0e) chassis->c_cap_enabled |= LLDP_CAP_BRIDGE; if (chassis->c_cap_enabled == 0) chassis->c_cap_enabled = LLDP_CAP_STATION; chassis->c_cap_available = chassis->c_cap_enabled; break; case CDP_TLV_SOFTWARE: software_len = tlv_len; PEEK_SAVE(software); break; case CDP_TLV_PLATFORM: platform_len = tlv_len; PEEK_SAVE(platform); break; default: LLOG_DEBUG("unknown CDP/FDP TLV type (%d) received on %s", ntohs(tlv_type), hardware->h_ifname); hardware->h_rx_unrecognized_cnt++; } PEEK_DISCARD(tlv + tlv_len - pos); } if (!software && platform) { if ((chassis->c_descr = (char *)calloc(1, platform_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis description"); goto malformed; } memcpy(chassis->c_descr, platform, platform_len); } else if (software && !platform) { if ((chassis->c_descr = (char *)calloc(1, software_len + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis description"); goto malformed; } memcpy(chassis->c_descr, software, software_len); } else if (software && platform) { #define CONCAT_PLATFORM " running on\n" if ((chassis->c_descr = (char *)calloc(1, software_len + platform_len + strlen(CONCAT_PLATFORM) + 1)) == NULL) { LLOG_WARN("unable to allocate memory for chassis description"); goto malformed; } memcpy(chassis->c_descr, platform, platform_len); memcpy(chassis->c_descr + platform_len, CONCAT_PLATFORM, strlen(CONCAT_PLATFORM)); memcpy(chassis->c_descr + platform_len + strlen(CONCAT_PLATFORM), software, software_len); } if ((chassis->c_id == NULL) || (port->p_id == NULL) || (chassis->c_name == NULL) || (chassis->c_descr == NULL) || (port->p_descr == NULL) || (chassis->c_ttl == 0) || (chassis->c_cap_enabled == 0)) { LLOG_WARNX("some mandatory CDP/FDP tlv are missing for frame received on %s", hardware->h_ifname); goto malformed; } *newchassis = chassis; *newport = port; return 1; malformed: lldpd_chassis_cleanup(chassis, 1); lldpd_port_cleanup(cfg, port, 1); free(port); return -1; }
void ctl_cleanup(char *name) { if (unlink(name) == -1) LLOG_WARN("unable to unlink %s", name); }