diff options
Diffstat (limited to 'src/libsystemd-network')
-rw-r--r-- | src/libsystemd-network/dhcp-internal.h | 3 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-option.c | 109 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp-protocol.h | 1 | ||||
-rw-r--r-- | src/libsystemd-network/dhcp6-option.c | 18 | ||||
-rw-r--r-- | src/libsystemd-network/icmp6-util.c | 44 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-client.c | 56 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-lease.c | 2 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp-server.c | 19 | ||||
-rw-r--r-- | src/libsystemd-network/sd-dhcp6-client.c | 22 | ||||
-rw-r--r-- | src/libsystemd-network/sd-ndisc.c | 75 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp-client.c | 6 | ||||
-rw-r--r-- | src/libsystemd-network/test-dhcp-option.c | 22 |
12 files changed, 216 insertions, 161 deletions
diff --git a/src/libsystemd-network/dhcp-internal.h b/src/libsystemd-network/dhcp-internal.h index a5daaa543a..7038212bcf 100644 --- a/src/libsystemd-network/dhcp-internal.h +++ b/src/libsystemd-network/dhcp-internal.h @@ -47,8 +47,7 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, uint8_ typedef int (*dhcp_option_cb_t)(uint8_t code, uint8_t len, const void *option, void *userdata); -int dhcp_option_parse(DHCPMessage *message, size_t len, - dhcp_option_cb_t cb, void *userdata); +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **error_message); int dhcp_message_init(DHCPMessage *message, uint8_t op, uint32_t xid, uint8_t type, uint16_t arp_type, size_t optlen, diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index a6c410ba91..1de7f3639c 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -24,6 +24,9 @@ #include <stdio.h> #include <string.h> +#include "alloc-util.h" +#include "utf8.h" + #include "dhcp-internal.h" static int option_append(uint8_t options[], size_t size, size_t *offset, @@ -139,72 +142,84 @@ int dhcp_option_append(DHCPMessage *message, size_t size, size_t *offset, } static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overload, - uint8_t *message_type, dhcp_option_cb_t cb, + uint8_t *message_type, char **error_message, dhcp_option_cb_t cb, void *userdata) { uint8_t code, len; + const uint8_t *option; size_t offset = 0; while (offset < buflen) { - switch (options[offset]) { - case DHCP_OPTION_PAD: - offset++; + code = options[offset ++]; - break; + switch (code) { + case DHCP_OPTION_PAD: + continue; case DHCP_OPTION_END: return 0; + } - case DHCP_OPTION_MESSAGE_TYPE: - if (buflen < offset + 3) - return -ENOBUFS; + if (buflen < offset + 1) + return -ENOBUFS; + + len = options[offset ++]; - len = options[++offset]; + if (buflen < offset + len) + return -EINVAL; + + option = &options[offset]; + + switch (code) { + case DHCP_OPTION_MESSAGE_TYPE: if (len != 1) return -EINVAL; if (message_type) - *message_type = options[++offset]; - else - offset++; - - offset++; + *message_type = *option; break; - case DHCP_OPTION_OVERLOAD: - if (buflen < offset + 3) - return -ENOBUFS; - - len = options[++offset]; - if (len != 1) + case DHCP_OPTION_ERROR_MESSAGE: + if (len == 0) return -EINVAL; - if (overload) - *overload = options[++offset]; - else - offset++; + if (error_message) { + _cleanup_free_ char *string = NULL; - offset++; + /* Accept a trailing NUL byte */ + if (memchr(option, 0, len - 1)) + return -EINVAL; - break; + string = strndup((const char *) option, len); + if (!string) + return -ENOMEM; - default: - if (buflen < offset + 3) - return -ENOBUFS; + if (!ascii_is_valid(string)) + return -EINVAL; - code = options[offset]; - len = options[++offset]; + free(*error_message); + *error_message = string; + string = NULL; + } - if (buflen < ++offset + len) + break; + case DHCP_OPTION_OVERLOAD: + if (len != 1) return -EINVAL; - if (cb) - cb(code, len, &options[offset], userdata); + if (overload) + *overload = *option; - offset += len; + break; + + default: + if (cb) + cb(code, len, option, userdata); break; } + + offset += len; } if (offset < buflen) @@ -213,8 +228,8 @@ static int parse_options(const uint8_t options[], size_t buflen, uint8_t *overlo return 0; } -int dhcp_option_parse(DHCPMessage *message, size_t len, - dhcp_option_cb_t cb, void *userdata) { +int dhcp_option_parse(DHCPMessage *message, size_t len, dhcp_option_cb_t cb, void *userdata, char **_error_message) { + _cleanup_free_ char *error_message = NULL; uint8_t overload = 0; uint8_t message_type = 0; int r; @@ -227,27 +242,29 @@ int dhcp_option_parse(DHCPMessage *message, size_t len, len -= sizeof(DHCPMessage); - r = parse_options(message->options, len, &overload, &message_type, - cb, userdata); + r = parse_options(message->options, len, &overload, &message_type, &error_message, cb, userdata); if (r < 0) return r; if (overload & DHCP_OVERLOAD_FILE) { - r = parse_options(message->file, sizeof(message->file), - NULL, &message_type, cb, userdata); + r = parse_options(message->file, sizeof(message->file), NULL, &message_type, &error_message, cb, userdata); if (r < 0) return r; } if (overload & DHCP_OVERLOAD_SNAME) { - r = parse_options(message->sname, sizeof(message->sname), - NULL, &message_type, cb, userdata); + r = parse_options(message->sname, sizeof(message->sname), NULL, &message_type, &error_message, cb, userdata); if (r < 0) return r; } - if (message_type) - return message_type; + if (message_type == 0) + return -ENOMSG; + + if (_error_message && IN_SET(message_type, DHCP_NAK, DHCP_DECLINE)) { + *_error_message = error_message; + error_message = NULL; + } - return -ENOMSG; + return message_type; } diff --git a/src/libsystemd-network/dhcp-protocol.h b/src/libsystemd-network/dhcp-protocol.h index 05bb5ae493..f65529a00e 100644 --- a/src/libsystemd-network/dhcp-protocol.h +++ b/src/libsystemd-network/dhcp-protocol.h @@ -132,6 +132,7 @@ enum { DHCP_OPTION_MESSAGE_TYPE = 53, DHCP_OPTION_SERVER_IDENTIFIER = 54, DHCP_OPTION_PARAMETER_REQUEST_LIST = 55, + DHCP_OPTION_ERROR_MESSAGE = 56, DHCP_OPTION_MAXIMUM_MESSAGE_SIZE = 57, DHCP_OPTION_RENEWAL_T1_TIME = 58, DHCP_OPTION_REBINDING_T2_TIME = 59, diff --git a/src/libsystemd-network/dhcp6-option.c b/src/libsystemd-network/dhcp6-option.c index 62023a9e49..850212aea1 100644 --- a/src/libsystemd-network/dhcp6-option.c +++ b/src/libsystemd-network/dhcp6-option.c @@ -360,7 +360,6 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * /* End of name */ break; else if (c <= 63) { - _cleanup_free_ char *t = NULL; const char *label; /* Literal label */ @@ -369,21 +368,20 @@ int dhcp6_option_parse_domainname(const uint8_t *optval, uint16_t optlen, char * if (pos > optlen) return -EMSGSIZE; - r = dns_label_escape(label, c, &t); - if (r < 0) - goto fail; - - if (!GREEDY_REALLOC0(ret, allocated, n + !first + strlen(t) + 1)) { + if (!GREEDY_REALLOC(ret, allocated, n + !first + DNS_LABEL_ESCAPED_MAX)) { r = -ENOMEM; goto fail; } - if (!first) - ret[n++] = '.'; - else + if (first) first = false; + else + ret[n++] = '.'; + + r = dns_label_escape(label, c, ret + n, DNS_LABEL_ESCAPED_MAX); + if (r < 0) + goto fail; - memcpy(ret + n, t, r); n += r; continue; } else { diff --git a/src/libsystemd-network/icmp6-util.c b/src/libsystemd-network/icmp6-util.c index 91308bf6c3..acad9d7d6a 100644 --- a/src/libsystemd-network/icmp6-util.c +++ b/src/libsystemd-network/icmp6-util.c @@ -47,17 +47,15 @@ int icmp6_bind_router_solicitation(int index) { .ipv6mr_interface = index, }; _cleanup_close_ int s = -1; - int r, zero = 0, hops = 255; + int r, zero = 0, one = 1, hops = 255; - s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, - IPPROTO_ICMPV6); + s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, IPPROTO_ICMPV6); if (s < 0) return -errno; ICMP6_FILTER_SETBLOCKALL(&filter); ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter); - r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, - sizeof(filter)); + r = setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filter, sizeof(filter)); if (r < 0) return -errno; @@ -65,23 +63,23 @@ int icmp6_bind_router_solicitation(int index) { IPV6_PKTINFO socket option also applies for ICMPv6 multicast. Empirical experiments indicates otherwise and therefore an IPV6_MULTICAST_IF socket option is used here instead */ - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, - sizeof(index)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, - sizeof(zero)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &zero, sizeof(zero)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, - sizeof(hops)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hops, sizeof(hops)); if (r < 0) return -errno; - r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, - sizeof(mreq)); + r = setsockopt(s, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); + if (r < 0) + return -errno; + + r = setsockopt(s, SOL_IPV6, IPV6_RECVHOPLIMIT, &one, sizeof(one)); if (r < 0) return -errno; @@ -101,25 +99,25 @@ int icmp6_send_router_solicitation(int s, const struct ether_addr *ether_addr) { struct ether_addr rs_opt_mac; } _packed_ rs = { .rs.nd_rs_type = ND_ROUTER_SOLICIT, + .rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR, + .rs_opt.nd_opt_len = 1, }; - struct iovec iov[1] = { - { &rs, }, + struct iovec iov = { + .iov_base = &rs, + .iov_len = sizeof(rs), }; struct msghdr msg = { .msg_name = &dst, .msg_namelen = sizeof(dst), - .msg_iov = iov, + .msg_iov = &iov, .msg_iovlen = 1, }; int r; - if (ether_addr) { - memcpy(&rs.rs_opt_mac, ether_addr, ETH_ALEN); - rs.rs_opt.nd_opt_type = ND_OPT_SOURCE_LINKADDR; - rs.rs_opt.nd_opt_len = 1; - iov[0].iov_len = sizeof(rs); - } else - iov[0].iov_len = sizeof(rs.rs); + assert(s >= 0); + assert(ether_addr); + + rs.rs_opt_mac = *ether_addr; r = sendmsg(s, &msg, 0); if (r < 0) diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 5ec0e661f7..7deb00af9c 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -604,7 +604,7 @@ static int client_send_discover(sd_dhcp_client *client) { their messages MUST NOT also send the Host Name option". Just send one of the two depending on the hostname type. */ - if (dns_name_single_label(client->hostname)) { + if (dns_name_is_single_label(client->hostname)) { /* it is unclear from RFC 2131 if client should send hostname in DHCPDISCOVER but dhclient does and so we do as well */ @@ -719,7 +719,7 @@ static int client_send_request(sd_dhcp_client *client) { } if (client->hostname) { - if (dns_name_single_label(client->hostname)) + if (dns_name_is_single_label(client->hostname)) r = dhcp_option_append(&request->dhcp, optlen, &optoffset, 0, DHCP_OPTION_HOST_NAME, strlen(client->hostname), client->hostname); @@ -1082,7 +1082,7 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, return r; } - r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease); + r = dhcp_option_parse(offer, len, dhcp_lease_parse_options, lease, NULL); if (r != DHCP_OFFER) { log_dhcp_client(client, "received message was not an OFFER, ignoring"); return -ENOMSG; @@ -1121,7 +1121,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, size_t len) { int r; - r = dhcp_option_parse(force, len, NULL, NULL); + r = dhcp_option_parse(force, len, NULL, NULL, NULL); if (r != DHCP_FORCERENEW) return -ENOMSG; @@ -1133,6 +1133,7 @@ static int client_handle_forcerenew(sd_dhcp_client *client, DHCPMessage *force, static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, size_t len) { _cleanup_dhcp_lease_unref_ sd_dhcp_lease *lease = NULL; + _cleanup_free_ char *error_message = NULL; int r; r = dhcp_lease_new(&lease); @@ -1147,9 +1148,9 @@ static int client_handle_ack(sd_dhcp_client *client, DHCPMessage *ack, return r; } - r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease); + r = dhcp_option_parse(ack, len, dhcp_lease_parse_options, lease, &error_message); if (r == DHCP_NAK) { - log_dhcp_client(client, "NAK"); + log_dhcp_client(client, "NAK: %s", strna(error_message)); return -EADDRNOTAVAIL; } @@ -1513,9 +1514,8 @@ static int client_receive_message_udp(sd_event_source *s, int fd, r = ioctl(fd, FIONREAD, &buflen); if (r < 0) - return r; - - if (buflen < 0) + return -errno; + else if (buflen < 0) /* this can't be right */ return -EIO; @@ -1525,26 +1525,28 @@ static int client_receive_message_udp(sd_event_source *s, int fd, len = read(fd, message, buflen); if (len < 0) { - log_dhcp_client(client, "could not receive message from UDP " - "socket: %m"); - return 0; + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_dhcp_client(client, "Could not receive message from UDP socket: %m"); + return -errno; } else if ((size_t)len < sizeof(DHCPMessage)) { - log_dhcp_client(client, "too small to be a DHCP message: ignoring"); + log_dhcp_client(client, "Too small to be a DHCP message: ignoring"); return 0; } if (be32toh(message->magic) != DHCP_MAGIC_COOKIE) { - log_dhcp_client(client, "not a DHCP message: ignoring"); + log_dhcp_client(client, "Not a DHCP message: ignoring"); return 0; } if (message->op != BOOTREPLY) { - log_dhcp_client(client, "not a BOOTREPLY message: ignoring"); + log_dhcp_client(client, "Not a BOOTREPLY message: ignoring"); return 0; } if (message->htype != client->arp_type) { - log_dhcp_client(client, "packet type does not match client type"); + log_dhcp_client(client, "Packet type does not match client type"); return 0; } @@ -1558,13 +1560,12 @@ static int client_receive_message_udp(sd_event_source *s, int fd, } if (message->hlen != expected_hlen) { - log_dhcp_client(client, "unexpected packet hlen %d", message->hlen); + log_dhcp_client(client, "Unexpected packet hlen %d", message->hlen); return 0; } if (memcmp(&message->chaddr[0], expected_chaddr, ETH_ALEN)) { - log_dhcp_client(client, "received chaddr does not match " - "expected: ignoring"); + log_dhcp_client(client, "Received chaddr does not match expected: ignoring"); return 0; } @@ -1572,8 +1573,7 @@ static int client_receive_message_udp(sd_event_source *s, int fd, be32toh(message->xid) != client->xid) { /* in BOUND state, we may receive FORCERENEW with xid set by server, so ignore the xid in this case */ - log_dhcp_client(client, "received xid (%u) does not match " - "expected (%u): ignoring", + log_dhcp_client(client, "Received xid (%u) does not match expected (%u): ignoring", be32toh(message->xid), client->xid); return 0; } @@ -1602,9 +1602,8 @@ static int client_receive_message_raw(sd_event_source *s, int fd, r = ioctl(fd, FIONREAD, &buflen); if (r < 0) - return r; - - if (buflen < 0) + return -errno; + else if (buflen < 0) /* this can't be right */ return -EIO; @@ -1617,9 +1616,12 @@ static int client_receive_message_raw(sd_event_source *s, int fd, len = recvmsg(fd, &msg, 0); if (len < 0) { - log_dhcp_client(client, "could not receive message from raw " - "socket: %m"); - return 0; + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_dhcp_client(client, "Could not receive message from raw socket: %m"); + + return -errno; } else if ((size_t)len < sizeof(DHCPPacket)) return 0; diff --git a/src/libsystemd-network/sd-dhcp-lease.c b/src/libsystemd-network/sd-dhcp-lease.c index 8befedc500..fccdc01bc3 100644 --- a/src/libsystemd-network/sd-dhcp-lease.c +++ b/src/libsystemd-network/sd-dhcp-lease.c @@ -661,7 +661,7 @@ int dhcp_lease_parse_options(uint8_t code, uint8_t len, const void *option, void break; default: - log_debug("Ignoring option DHCP option %i while parsing.", code); + log_debug("Ignoring option DHCP option %"PRIu8" while parsing.", code); break; } diff --git a/src/libsystemd-network/sd-dhcp-server.c b/src/libsystemd-network/sd-dhcp-server.c index 277c88e2b9..587ff936ba 100644 --- a/src/libsystemd-network/sd-dhcp-server.c +++ b/src/libsystemd-network/sd-dhcp-server.c @@ -699,6 +699,7 @@ static int get_pool_offset(sd_dhcp_server *server, be32_t requested_ip) { int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, size_t length) { _cleanup_dhcp_request_free_ DHCPRequest *req = NULL; + _cleanup_free_ char *error_message = NULL; DHCPLease *existing_lease; int type, r; @@ -714,7 +715,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, if (!req) return -ENOMEM; - type = dhcp_option_parse(message, length, parse_request, req); + type = dhcp_option_parse(message, length, parse_request, req, &error_message); if (type < 0) return 0; @@ -784,8 +785,7 @@ int dhcp_server_handle_message(sd_dhcp_server *server, DHCPMessage *message, break; } case DHCP_DECLINE: - log_dhcp_server(server, "DECLINE (0x%x)", - be32toh(req->message->xid)); + log_dhcp_server(server, "DECLINE (0x%x): %s", be32toh(req->message->xid), strna(error_message)); /* TODO: make sure we don't offer this address again */ @@ -963,10 +963,10 @@ static int server_receive_message(sd_event_source *s, int fd, if (ioctl(fd, FIONREAD, &buflen) < 0) return -errno; - if (buflen < 0) + else if (buflen < 0) return -EIO; - message = malloc0(buflen); + message = malloc(buflen); if (!message) return -ENOMEM; @@ -974,9 +974,12 @@ static int server_receive_message(sd_event_source *s, int fd, iov.iov_len = buflen; len = recvmsg(fd, &msg, 0); - if (len < buflen) - return 0; - else if ((size_t)len < sizeof(DHCPMessage)) + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + return -errno; + } else if ((size_t)len < sizeof(DHCPMessage)) return 0; CMSG_FOREACH(cmsg, &msg) { diff --git a/src/libsystemd-network/sd-dhcp6-client.c b/src/libsystemd-network/sd-dhcp6-client.c index 801331d270..36d909a4c5 100644 --- a/src/libsystemd-network/sd-dhcp6-client.c +++ b/src/libsystemd-network/sd-dhcp6-client.c @@ -895,7 +895,7 @@ static int client_receive_advertise(sd_dhcp6_client *client, DHCP6Message *adver static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, void *userdata) { sd_dhcp6_client *client = userdata; DHCP6_CLIENT_DONT_DESTROY(client); - _cleanup_free_ DHCP6Message *message; + _cleanup_free_ DHCP6Message *message = NULL; int r, buflen, len; assert(s); @@ -903,18 +903,26 @@ static int client_receive_message(sd_event_source *s, int fd, uint32_t revents, assert(client->event); r = ioctl(fd, FIONREAD, &buflen); - if (r < 0 || buflen <= 0) - buflen = DHCP6_MIN_OPTIONS_SIZE; + if (r < 0) + return -errno; + else if (buflen < 0) + /* This really should not happen */ + return -EIO; - message = malloc0(buflen); + message = malloc(buflen); if (!message) return -ENOMEM; len = read(fd, message, buflen); - if ((size_t)len < sizeof(DHCP6Message)) { - log_dhcp6_client(client, "could not receive message from UDP socket: %m"); + if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + + log_dhcp6_client(client, "Could not receive message from UDP socket: %m"); + + return -errno; + } else if ((size_t)len < sizeof(DHCP6Message)) return 0; - } switch(message->type) { case DHCP6_SOLICIT: diff --git a/src/libsystemd-network/sd-ndisc.c b/src/libsystemd-network/sd-ndisc.c index 713438f212..f2bce3b99f 100644 --- a/src/libsystemd-network/sd-ndisc.c +++ b/src/libsystemd-network/sd-ndisc.c @@ -418,8 +418,7 @@ static int ndisc_prefix_update(sd_ndisc *nd, ssize_t len, return 0; } -static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, - ssize_t len) { +static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, ssize_t len) { void *opt; struct nd_opt_hdr *opt_hdr; @@ -482,36 +481,79 @@ static int ndisc_ra_parse(sd_ndisc *nd, struct nd_router_advert *ra, static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) { _cleanup_free_ struct nd_router_advert *ra = NULL; sd_ndisc *nd = userdata; - int r, buflen = 0, pref, stateful; - union sockaddr_union router = {}; - socklen_t router_len = sizeof(router); + union { + struct cmsghdr cmsghdr; + uint8_t buf[CMSG_LEN(sizeof(int))]; + } control = {}; + struct iovec iov = {}; + union sockaddr_union sa = {}; + struct msghdr msg = { + .msg_name = &sa.sa, + .msg_namelen = sizeof(sa), + .msg_iov = &iov, + .msg_iovlen = 1, + .msg_control = &control, + .msg_controllen = sizeof(control), + }; + struct cmsghdr *cmsg; struct in6_addr *gw; unsigned lifetime; ssize_t len; + int r, pref, stateful, buflen = 0; assert(s); assert(nd); assert(nd->event); r = ioctl(fd, FIONREAD, &buflen); - if (r < 0 || buflen <= 0) - buflen = ICMP6_RECV_SIZE; + if (r < 0) + return -errno; + else if (buflen < 0) + /* This really should not happen */ + return -EIO; + + iov.iov_len = buflen; - ra = malloc(buflen); + ra = malloc(iov.iov_len); if (!ra) return -ENOMEM; - len = recvfrom(fd, ra, buflen, 0, &router.sa, &router_len); + iov.iov_base = ra; + + len = recvmsg(fd, &msg, 0); if (len < 0) { + if (errno == EAGAIN || errno == EINTR) + return 0; + log_ndisc(nd, "Could not receive message from ICMPv6 socket: %m"); + return -errno; + } else if ((size_t)len < sizeof(struct nd_router_advert)) { return 0; - } else if (router_len == 0) + } else if (msg.msg_namelen == 0) gw = NULL; /* only happens when running the test-suite over a socketpair */ - else if (router_len != sizeof(router.in6)) { - log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)router_len); + else if (msg.msg_namelen != sizeof(sa.in6)) { + log_ndisc(nd, "Received invalid source address size from ICMPv6 socket: %zu bytes", (size_t)msg.msg_namelen); return 0; } else - gw = &router.in6.sin6_addr; + gw = &sa.in6.sin6_addr; + + assert(!(msg.msg_flags & MSG_CTRUNC)); + assert(!(msg.msg_flags & MSG_TRUNC)); + + CMSG_FOREACH(cmsg, &msg) { + if (cmsg->cmsg_level == SOL_IPV6 && + cmsg->cmsg_type == IPV6_HOPLIMIT && + cmsg->cmsg_len == CMSG_LEN(sizeof(int))) { + int hops = *(int*)CMSG_DATA(cmsg); + + if (hops != 255) { + log_ndisc(nd, "Received RA with invalid hop limit %d. Ignoring.", hops); + return 0; + } + + break; + } + } if (gw && !in_addr_is_link_local(AF_INET6, (const union in_addr_union*) gw)) { _cleanup_free_ char *addr = NULL; @@ -566,8 +608,6 @@ static int ndisc_router_advertisment_recv(sd_event_source *s, int fd, uint32_t r static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, void *userdata) { sd_ndisc *nd = userdata; uint64_t time_now, next_timeout; - struct ether_addr unset = { }; - struct ether_addr *addr = NULL; int r; assert(s); @@ -581,10 +621,7 @@ static int ndisc_router_solicitation_timeout(sd_event_source *s, uint64_t usec, nd->callback(nd, SD_NDISC_EVENT_TIMEOUT, nd->userdata); nd->state = NDISC_STATE_ADVERTISMENT_LISTEN; } else { - if (memcmp(&nd->mac_addr, &unset, sizeof(struct ether_addr))) - addr = &nd->mac_addr; - - r = icmp6_send_router_solicitation(nd->fd, addr); + r = icmp6_send_router_solicitation(nd->fd, &nd->mac_addr); if (r < 0) log_ndisc(nd, "Error sending Router Solicitation"); else { diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 1200a7c251..4478147a83 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -223,7 +223,7 @@ int dhcp_network_send_udp_socket(int s, be32_t address, uint16_t port, const voi static int test_discover_message_verify(size_t size, struct DHCPMessage *dhcp) { int res; - res = dhcp_option_parse(dhcp, size, check_options, NULL); + res = dhcp_option_parse(dhcp, size, check_options, NULL, NULL); assert_se(res == DHCP_DISCOVER); if (verbose) @@ -390,7 +390,7 @@ static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { uint8_t *msg_bytes = (uint8_t *)request; int res; - res = dhcp_option_parse(request, size, check_options, NULL); + res = dhcp_option_parse(request, size, check_options, NULL, NULL); assert_se(res == DHCP_REQUEST); assert_se(xid == request->xid); @@ -420,7 +420,7 @@ static int test_addr_acq_recv_discover(size_t size, DHCPMessage *discover) { uint8_t *msg_bytes = (uint8_t *)discover; int res; - res = dhcp_option_parse(discover, size, check_options, NULL); + res = dhcp_option_parse(discover, size, check_options, NULL, NULL); assert_se(res == DHCP_DISCOVER); assert_se(msg_bytes[size - 1] == DHCP_OPTION_END); diff --git a/src/libsystemd-network/test-dhcp-option.c b/src/libsystemd-network/test-dhcp-option.c index 3607df63af..75d22c4df3 100644 --- a/src/libsystemd-network/test-dhcp-option.c +++ b/src/libsystemd-network/test-dhcp-option.c @@ -75,9 +75,8 @@ static const char *dhcp_type(int type) { static void test_invalid_buffer_length(void) { DHCPMessage message; - assert_se(dhcp_option_parse(&message, 0, NULL, NULL) == -EINVAL); - assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL) - == -EINVAL); + assert_se(dhcp_option_parse(&message, 0, NULL, NULL, NULL) == -EINVAL); + assert_se(dhcp_option_parse(&message, sizeof(DHCPMessage) - 1, NULL, NULL, NULL) == -EINVAL); } static void test_message_init(void) { @@ -101,7 +100,7 @@ static void test_message_init(void) { assert_se(magic[2] == 83); assert_se(magic[3] == 99); - assert_se(dhcp_option_parse(message, len, NULL, NULL) >= 0); + assert_se(dhcp_option_parse(message, len, NULL, NULL, NULL) >= 0); } static DHCPMessage *create_message(uint8_t *options, uint16_t optlen, @@ -264,19 +263,12 @@ static void test_options(struct option_desc *desc) { buflen = sizeof(DHCPMessage) + optlen; if (!desc) { - assert_se((res = dhcp_option_parse(message, buflen, - test_options_cb, - NULL)) == -ENOMSG); + assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, NULL, NULL)) == -ENOMSG); } else if (desc->success) { - assert_se((res = dhcp_option_parse(message, buflen, - test_options_cb, - desc)) >= 0); - assert_se(desc->pos == -1 && desc->filepos == -1 && - desc->snamepos == -1); + assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) >= 0); + assert_se(desc->pos == -1 && desc->filepos == -1 && desc->snamepos == -1); } else - assert_se((res = dhcp_option_parse(message, buflen, - test_options_cb, - desc)) < 0); + assert_se((res = dhcp_option_parse(message, buflen, test_options_cb, desc, NULL)) < 0); if (verbose) printf("DHCP type %s\n", dhcp_type(res)); |