Files
radvd/device-linux.c
Robin H. Johnson 56f31f469e Fix -Wstringop-truncation warning: s/strncpy/strlcpy/
GCC8 added a warning for string truncation that fires on the interface
name handling.

Use the same fix as found in iproute2 to solve it, replacing `strncpy(dst, src, sz-1)`
with `memset(dst, NULL, size)` and then `strlcpy(dst, src, sizeof(dst))`.

`strlcpy` ensures NULL terminated strings in all cases where `strncpy`
does not.

Reference: https://patchwork.ozlabs.org/project/netdev/patch/20180319165638.30166-3-stephen@networkplumber.org/
Reference: https://developers.redhat.com/blog/2018/05/24/detecting-string-truncation-with-gcc-8
Signed-off-by: Robin H. Johnson <robbat2@gentoo.org>
2023-01-03 15:16:45 -08:00

497 lines
13 KiB
C

/*
*
* Authors:
* Lars Fenneberg <lf@elemental.net>
*
* This software is Copyright 1996,1997 by the above mentioned author(s),
* All Rights Reserved.
*
* The license which is distributed with this software in the file COPYRIGHT
* applies to this software. If your distribution is missing this file, you
* may request it from <reubenhwk@gmail.com>.
*
*/
#include "config.h"
#include "defaults.h"
#include "includes.h"
#include "netlink.h"
#include "pathnames.h"
#include "radvd.h"
#ifndef IPV6_ADDR_LINKLOCAL
#define IPV6_ADDR_LINKLOCAL 0x0020U
#endif
#ifndef ARPHRD_6LOWPAN
#define ARPHRD_6LOWPAN 825 /* IPv6 over LoWPAN */
#endif
static char const *hwstr(unsigned short sa_family);
uint32_t get_interface_linkmtu(const char *);
/*
* this function gets the hardware type and address of an interface,
* determines the link layer token length and checks it against
* the defined prefixes
*/
int update_device_info(int sock, struct Interface *iface)
{
struct ifreq ifr;
memset(&ifr, 0, sizeof(ifr));
strlcpy(ifr.ifr_name, iface->props.name, sizeof(ifr.ifr_name));
if (ioctl(sock, SIOCGIFMTU, &ifr) < 0) {
flog(LOG_ERR, "ioctl(SIOCGIFMTU) failed on %s: %s", iface->props.name, strerror(errno));
return -1;
}
iface->sllao.if_maxmtu = ifr.ifr_mtu;
dlog(LOG_DEBUG, 3, "%s mtu: %d", iface->props.name, ifr.ifr_mtu);
/* RFC 2460: 5. Packet Size Issues */
/* Get the smallest MTU between the SIOCGIFMTU value and the protocol MTU
* /proc/sys/net/ipv6/conf/eth0/mtu
* Because the protocol MTU _may_ be different than the physical link MTU
*
* If Link-layer MTU <= 1280: use 1280 (enforced by iface->AdvRAMTU)
* If Link-layer MTU > 1280: use the lower of:
* - RA MTU
* - link-layer MTU
* - per-protocol MTU
*/
iface->props.max_ra_option_size = iface->AdvRAMTU;
iface->props.max_ra_option_size = MIN(iface->props.max_ra_option_size, MAX(iface->sllao.if_maxmtu, RFC2460_MIN_MTU));
iface->props.max_ra_option_size =
MIN(iface->props.max_ra_option_size, MAX(get_interface_linkmtu(iface->props.name), RFC2460_MIN_MTU));
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) {
flog(LOG_ERR, "ioctl(SIOCGIFHWADDR) failed on %s: %s", iface->props.name, strerror(errno));
return -1;
}
dlog(LOG_DEBUG, 3, "%s hardware type: %s", iface->props.name, hwstr(ifr.ifr_hwaddr.sa_family));
switch (ifr.ifr_hwaddr.sa_family) {
case ARPHRD_ETHER:
iface->sllao.if_hwaddr_len = 48;
iface->sllao.if_prefix_len = 64;
char hwaddr[3 * 6];
sprintf(hwaddr, "%02x:%02x:%02x:%02x:%02x:%02x", (unsigned char)ifr.ifr_hwaddr.sa_data[0],
(unsigned char)ifr.ifr_hwaddr.sa_data[1], (unsigned char)ifr.ifr_hwaddr.sa_data[2],
(unsigned char)ifr.ifr_hwaddr.sa_data[3], (unsigned char)ifr.ifr_hwaddr.sa_data[4],
(unsigned char)ifr.ifr_hwaddr.sa_data[5]);
dlog(LOG_DEBUG, 3, "%s hardware address: %s", iface->props.name, hwaddr);
iface->props.max_ra_option_size -= 14; /* RFC 2464 */
break;
#ifdef ARPHRD_FDDI
case ARPHRD_FDDI:
iface->sllao.if_hwaddr_len = 48;
iface->sllao.if_prefix_len = 64;
iface->props.max_ra_option_size -= 22; /* RFC 2109 */
break;
#endif /* ARPHDR_FDDI */
#ifdef ARPHRD_ARCNET
case ARPHRD_ARCNET:
iface->sllao.if_hwaddr_len = 8;
iface->sllao.if_prefix_len = -1;
iface->sllao.if_maxmtu = -1;
/* RFC1201: fragmentation handled at a lower layer.
* native packet size is 256-512 bytes */
iface->props.max_ra_option_size -= 0;
break;
#endif /* ARPHDR_ARCNET */
case ARPHRD_6LOWPAN:
#ifdef HAVE_NETLINK
/* hwaddr length differs on some L2 type lets detect them */
iface->sllao.if_hwaddr_len = netlink_get_device_addr_len(iface);
if (iface->sllao.if_hwaddr_len != -1) {
iface->sllao.if_hwaddr_len *= 8;
iface->sllao.if_prefix_len = 64;
} else {
iface->sllao.if_prefix_len = -1;
}
#else
iface->sllao.if_hwaddr_len = -1;
iface->sllao.if_prefix_len = -1;
#endif
/* RFC4944: fragmentation handled at a lower layer.
* native packet size maxes at 127 bytes */
iface->props.max_ra_option_size -= 0;
break;
default:
iface->sllao.if_hwaddr_len = -1;
iface->sllao.if_prefix_len = -1;
iface->sllao.if_maxmtu = -1;
/* Assume fragmentation handled at a lower layer. */
iface->props.max_ra_option_size -= 0;
break;
}
dlog(LOG_DEBUG, 3, "%s link layer token length: %d", iface->props.name, iface->sllao.if_hwaddr_len);
dlog(LOG_DEBUG, 3, "%s prefix length: %d", iface->props.name, iface->sllao.if_prefix_len);
if (iface->sllao.if_hwaddr_len != -1) {
unsigned int if_hwaddr_len_bytes = (iface->sllao.if_hwaddr_len + 7) >> 3;
if (if_hwaddr_len_bytes > sizeof(iface->sllao.if_hwaddr)) {
flog(LOG_ERR, "%s address length too big: %d", iface->props.name, if_hwaddr_len_bytes);
return -2;
}
memcpy(iface->sllao.if_hwaddr, ifr.ifr_hwaddr.sa_data, if_hwaddr_len_bytes);
char zero[sizeof(iface->props.if_addr)];
memset(zero, 0, sizeof(zero));
if (!memcmp(iface->sllao.if_hwaddr, zero, if_hwaddr_len_bytes))
flog(LOG_WARNING, "WARNING, MAC address on %s is all zero!", iface->props.name);
}
struct AdvPrefix *prefix = iface->AdvPrefixList;
while (prefix) {
if ((iface->sllao.if_prefix_len != -1) && (iface->sllao.if_prefix_len != prefix->PrefixLen)) {
flog(LOG_WARNING, "%s prefix length should be: %d", iface->props.name, iface->sllao.if_prefix_len);
}
prefix = prefix->next;
}
// Regardless of link-layer, every RA message will have an IPV6 header & RA header
iface->props.max_ra_option_size -= sizeof(struct ip6_hdr);
iface->props.max_ra_option_size -= sizeof(struct nd_router_advert);
return 0;
}
int setup_allrouters_membership(int sock, struct Interface *iface)
{
struct ipv6_mreq mreq;
memset(&mreq, 0, sizeof(mreq));
mreq.ipv6mr_interface = iface->props.if_index;
/* ipv6-allrouters: ff02::2 */
mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000);
mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2);
if (setsockopt(sock, SOL_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
/* linux-2.6.12-bk4 returns error with HUP signal but keep listening */
if (errno != EADDRINUSE) {
flog(LOG_ERR, "can't join ipv6-allrouters on %s", iface->props.name);
return -1;
}
}
return 0;
}
int cleanup_allrouters_membership(int sock, struct Interface *iface)
{
struct ipv6_mreq mreq;
memset(&mreq, 0, sizeof(mreq));
mreq.ipv6mr_interface = iface->props.if_index;
/* ipv6-allrouters: ff02::2 */
mreq.ipv6mr_multiaddr.s6_addr32[0] = htonl(0xFF020000);
mreq.ipv6mr_multiaddr.s6_addr32[3] = htonl(0x2);
setsockopt(sock, SOL_IPV6, IPV6_DROP_MEMBERSHIP, &mreq, sizeof(mreq));
return 0;
}
uint32_t get_interface_linkmtu(const char *iface)
{
int value;
FILE *fp = NULL;
char proc_path[sizeof(PROC_SYS_IP6_LINKMTU) + IFNAMSIZ];
snprintf(proc_path, sizeof(PROC_SYS_IP6_LINKMTU) + IFNAMSIZ, PROC_SYS_IP6_LINKMTU, iface);
fp = fopen(proc_path, "r");
if (fp) {
int rc = fscanf(fp, "%d", &value);
if (rc != 1) {
flog(LOG_ERR, "cannot read value from %s: %s", proc_path, strerror(errno));
exit(1);
}
fclose(fp);
} else {
flog(LOG_DEBUG, "Correct IPv6 MTU entry not found, "
"perhaps the procfs is disabled, "
"or the kernel interface has changed?");
value = 1280; /* RFC2460: section 5 */
}
return value;
}
int set_interface_linkmtu(const char *iface, uint32_t mtu) { return privsep_interface_linkmtu(iface, mtu); }
int set_interface_curhlim(const char *iface, uint8_t hlim) { return privsep_interface_curhlim(iface, hlim); }
int set_interface_reachtime(const char *iface, uint32_t rtime) { return privsep_interface_reachtime(iface, rtime); }
int set_interface_retranstimer(const char *iface, uint32_t rettimer) { return privsep_interface_retranstimer(iface, rettimer); }
int check_ip6_iface_forwarding(const char *iface)
{
int value = -1;
FILE *fp = NULL;
char path[sizeof(PROC_SYS_IP6_IFACE_FORWARDING) + IFNAMSIZ];
snprintf(path, sizeof(PROC_SYS_IP6_IFACE_FORWARDING) + IFNAMSIZ, PROC_SYS_IP6_IFACE_FORWARDING, iface);
fp = fopen(path, "r");
if (fp) {
int rc = fscanf(fp, "%d", &value);
if (rc != 1) {
flog(LOG_ERR, "cannot read value from %s: %s", path, strerror(errno));
exit(1);
}
fclose(fp);
} else {
flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry for interface "
"not found, perhaps the procfs is disabled, "
"or the kernel interface has changed?");
value = -1;
}
return value;
}
int check_ip6_forwarding(void)
{
int value;
FILE *fp = NULL;
fp = fopen(PROC_SYS_IP6_FORWARDING, "r");
if (fp) {
int rc = fscanf(fp, "%d", &value);
if (rc != 1) {
flog(LOG_ERR, "cannot read value from %s: %s", PROC_SYS_IP6_FORWARDING, strerror(errno));
exit(1);
}
fclose(fp);
} else {
flog(LOG_DEBUG, "Correct IPv6 forwarding procfs entry not found, "
"perhaps the procfs is disabled, "
"or the kernel interface has changed?");
value = -1;
}
#ifdef HAVE_SYSCTL
int forw_sysctl[] = {SYSCTL_IP6_FORWARDING};
size_t size = sizeof(value);
if (!fp && sysctl(forw_sysctl, sizeof(forw_sysctl) / sizeof(forw_sysctl[0]), &value, &size, NULL, 0) < 0) {
flog(LOG_DEBUG, "Correct IPv6 forwarding sysctl branch not found, "
"perhaps the kernel interface has changed?");
return 0; /* this is of advisory value only */
}
#endif
/* Linux allows the forwarding value to be either 1 or 2.
* https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/Documentation/networking/ip-sysctl.txt?id=ae8abfa00efb8ec550f772cbd1e1854977d06212#n1078
*
* The value 2 indicates forwarding is enabled and that *AS* *WELL* router solicitations are being done.
*
* Which is sometimes used on routers performing RS on their WAN (ppp, etc.) links
*/
static int warned = 0;
if (!warned && value != 1 && value != 2) {
warned = 1;
flog(LOG_DEBUG, "IPv6 forwarding setting is: %u, should be 1 or 2", value);
return -1;
}
return 0;
}
static char const *hwstr(unsigned short sa_family)
{
char const *rc = 0;
switch (sa_family) {
case ARPHRD_NETROM:
rc = "ARPHRD_NETROM";
break;
case ARPHRD_ETHER:
rc = "ARPHRD_ETHER";
break;
case ARPHRD_EETHER:
rc = "ARPHRD_EETHER";
break;
case ARPHRD_AX25:
rc = "ARPHRD_AX25";
break;
case ARPHRD_PRONET:
rc = "ARPHRD_PRONET";
break;
case ARPHRD_CHAOS:
rc = "ARPHRD_CHAOS";
break;
case ARPHRD_IEEE802:
rc = "ARPHRD_IEEE802";
break;
case ARPHRD_APPLETLK:
rc = "ARPHRD_APPLETLK";
break;
case ARPHRD_DLCI:
rc = "ARPHRD_DLCI";
break;
case ARPHRD_ATM:
rc = "ARPHRD_ATM";
break;
case ARPHRD_METRICOM:
rc = "ARPHRD_METRICOM";
break;
case ARPHRD_IEEE1394:
rc = "ARPHRD_IEEE1394";
break;
case ARPHRD_EUI64:
rc = "ARPHRD_EUI64";
break;
case ARPHRD_INFINIBAND:
rc = "ARPHRD_INFINIBAND";
break;
case ARPHRD_SLIP:
rc = "ARPHRD_SLIP";
break;
case ARPHRD_CSLIP:
rc = "ARPHRD_CSLIP";
break;
case ARPHRD_SLIP6:
rc = "ARPHRD_SLIP6";
break;
case ARPHRD_CSLIP6:
rc = "ARPHRD_CSLIP6";
break;
case ARPHRD_RSRVD:
rc = "ARPHRD_RSRVD";
break;
case ARPHRD_ADAPT:
return "ARPHRD_ADAPT";
break;
case ARPHRD_ROSE:
rc = "ARPHRD_ROSE";
break;
case ARPHRD_X25:
rc = "ARPHRD_X25";
break;
case ARPHRD_HWX25:
rc = "ARPHRD_HWX25";
break;
case ARPHRD_PPP:
rc = "ARPHRD_PPP";
break;
case ARPHRD_CISCO:
rc = "ARPHRD_CISCO";
break;
case ARPHRD_LAPB:
rc = "ARPHRD_LAPB";
break;
case ARPHRD_DDCMP:
rc = "ARPHRD_DDCMP";
break;
case ARPHRD_RAWHDLC:
rc = "ARPHRD_RAWHDLC";
break;
case ARPHRD_TUNNEL:
rc = "ARPHRD_TUNNEL";
break;
case ARPHRD_TUNNEL6:
rc = "ARPHRD_TUNNEL6";
break;
case ARPHRD_FRAD:
rc = "ARPHRD_FRAD";
break;
case ARPHRD_SKIP:
rc = "ARPHRD_SKIP";
break;
case ARPHRD_LOOPBACK:
rc = "ARPHRD_LOOPBACK";
break;
case ARPHRD_LOCALTLK:
rc = "ARPHRD_LOCALTLK";
break;
case ARPHRD_BIF:
rc = "ARPHRD_BIF";
break;
case ARPHRD_SIT:
rc = "ARPHRD_SIT";
break;
case ARPHRD_IPDDP:
rc = "ARPHRD_IPDDP";
break;
case ARPHRD_IPGRE:
rc = "ARPHRD_IPGRE";
break;
case ARPHRD_PIMREG:
rc = "ARPHRD_PIMREG";
break;
case ARPHRD_HIPPI:
rc = "ARPHRD_HIPPI";
break;
case ARPHRD_ASH:
rc = "ARPHRD_ASH";
break;
case ARPHRD_ECONET:
rc = "ARPHRD_ECONET";
break;
case ARPHRD_IRDA:
rc = "ARPHRD_IRDA";
break;
case ARPHRD_FCPP:
rc = "ARPHRD_FCPP";
break;
case ARPHRD_FCAL:
rc = "ARPHRD_FCAL";
break;
case ARPHRD_FCPL:
rc = "ARPHRD_FCPL";
break;
case ARPHRD_FCFABRIC:
rc = "ARPHRD_FCFABRIC";
break;
case ARPHRD_IEEE802_TR:
rc = "ARPHRD_IEEE802_TR";
break;
case ARPHRD_IEEE80211:
rc = "ARPHRD_IEEE80211";
break;
case ARPHRD_IEEE80211_PRISM:
rc = "ARPHRD_IEEE80211_PRISM";
break;
case ARPHRD_IEEE80211_RADIOTAP:
rc = "ARPHRD_IEEE80211_RADIOTAP";
break;
case ARPHRD_IEEE802154:
rc = "ARPHRD_IEEE802154";
break;
#if ARPHRD_IEEE802154_MONITOR
case ARPHRD_IEEE802154_MONITOR:
rc = "ARPHRD_IEEE802154_MONITOR";
break;
#elif ARPHRD_IEEE802154_PHY
case ARPHRD_IEEE802154_PHY:
rc = "ARPHRD_IEEE802154_PHY";
break;
#endif
case ARPHRD_6LOWPAN:
rc = "ARPHRD_6LOWPAN";
break;
case ARPHRD_VOID:
rc = "ARPHRD_VOID";
break;
case ARPHRD_NONE:
rc = "ARPHRD_NONE";
break;
default:
rc = "unknown";
break;
}
return rc;
}