Merge pull request #31492 from yuwata/icmp6-packet
sd-ndisc: introduce ICMP6Packet and relevant functions, and use them
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include "icmp6-packet.h"
|
||||
#include "icmp6-util.h"
|
||||
#include "in-addr-util.h"
|
||||
#include "iovec-util.h"
|
||||
#include "network-common.h"
|
||||
#include "socket-util.h"
|
||||
|
||||
DEFINE_TRIVIAL_REF_UNREF_FUNC(ICMP6Packet, icmp6_packet, mfree);
|
||||
|
||||
static ICMP6Packet* icmp6_packet_new(size_t size) {
|
||||
ICMP6Packet *p;
|
||||
|
||||
if (size > SIZE_MAX - offsetof(ICMP6Packet, raw_packet))
|
||||
return NULL;
|
||||
|
||||
p = malloc0(offsetof(ICMP6Packet, raw_packet) + size);
|
||||
if (!p)
|
||||
return NULL;
|
||||
|
||||
p->raw_size = size;
|
||||
p->n_ref = 1;
|
||||
|
||||
return p;
|
||||
}
|
||||
|
||||
int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret) {
|
||||
assert(p);
|
||||
|
||||
if (in6_addr_is_null(&p->sender_address))
|
||||
return -ENODATA;
|
||||
|
||||
if (ret)
|
||||
*ret = p->sender_address;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret) {
|
||||
assert(p);
|
||||
assert(ret);
|
||||
|
||||
if (!TRIPLE_TIMESTAMP_HAS_CLOCK(clock) || !clock_supported(clock))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (!triple_timestamp_is_set(&p->timestamp))
|
||||
return -ENODATA;
|
||||
|
||||
*ret = triple_timestamp_by_clock(&p->timestamp, clock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct icmp6_hdr* icmp6_packet_get_header(ICMP6Packet *p) {
|
||||
assert(p);
|
||||
|
||||
if (p->raw_size < sizeof(struct icmp6_hdr))
|
||||
return NULL;
|
||||
|
||||
return (const struct icmp6_hdr*) p->raw_packet;
|
||||
}
|
||||
|
||||
int icmp6_packet_get_type(ICMP6Packet *p) {
|
||||
const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
|
||||
if (!hdr)
|
||||
return -EBADMSG;
|
||||
|
||||
return hdr->icmp6_type;
|
||||
}
|
||||
|
||||
static int icmp6_packet_verify(ICMP6Packet *p) {
|
||||
const struct icmp6_hdr *hdr = icmp6_packet_get_header(p);
|
||||
if (!hdr)
|
||||
return -EBADMSG;
|
||||
|
||||
if (hdr->icmp6_code != 0)
|
||||
return -EBADMSG;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int icmp6_packet_receive(int fd, ICMP6Packet **ret) {
|
||||
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *p = NULL;
|
||||
ssize_t len;
|
||||
int r;
|
||||
|
||||
assert(fd >= 0);
|
||||
assert(ret);
|
||||
|
||||
len = next_datagram_size_fd(fd);
|
||||
if (len < 0)
|
||||
return (int) len;
|
||||
|
||||
p = icmp6_packet_new(len);
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
r = icmp6_receive(fd, p->raw_packet, p->raw_size, &p->sender_address, &p->timestamp);
|
||||
if (r < 0)
|
||||
switch (r) {
|
||||
case -EADDRNOTAVAIL:
|
||||
return log_debug_errno(r, "ICMPv6: Received a packet from neither link-local nor null address.");
|
||||
|
||||
case -EMULTIHOP:
|
||||
return log_debug_errno(r, "ICMPv6: Received a packet with an invalid hop limit.");
|
||||
|
||||
case -EPFNOSUPPORT:
|
||||
return log_debug_errno(r, "ICMPv6: Received a packet with an invalid source address.");
|
||||
|
||||
default:
|
||||
return log_debug_errno(r, "ICMPv6: Unexpected error while receiving a packet: %m");
|
||||
}
|
||||
|
||||
r = icmp6_packet_verify(p);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
*ret = TAKE_PTR(p);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "macro.h"
|
||||
#include "time-util.h"
|
||||
|
||||
typedef struct ICMP6Pakcet {
|
||||
unsigned n_ref;
|
||||
|
||||
struct in6_addr sender_address;
|
||||
struct triple_timestamp timestamp;
|
||||
|
||||
size_t raw_size;
|
||||
uint8_t raw_packet[];
|
||||
} ICMP6Packet;
|
||||
|
||||
ICMP6Packet* icmp6_packet_ref(ICMP6Packet *p);
|
||||
ICMP6Packet* icmp6_packet_unref(ICMP6Packet *p);
|
||||
DEFINE_TRIVIAL_CLEANUP_FUNC(ICMP6Packet*, icmp6_packet_unref);
|
||||
|
||||
int icmp6_packet_get_sender_address(ICMP6Packet *p, struct in6_addr *ret);
|
||||
int icmp6_packet_get_timestamp(ICMP6Packet *p, clockid_t clock, usec_t *ret);
|
||||
int icmp6_packet_get_type(ICMP6Packet *p);
|
||||
|
||||
int icmp6_packet_receive(int fd, ICMP6Packet **ret);
|
||||
@@ -8,6 +8,7 @@ sources = files(
|
||||
'dhcp6-network.c',
|
||||
'dhcp6-option.c',
|
||||
'dhcp6-protocol.c',
|
||||
'icmp6-packet.c',
|
||||
'icmp6-util.c',
|
||||
'lldp-neighbor.c',
|
||||
'lldp-network.c',
|
||||
|
||||
@@ -24,6 +24,7 @@ struct sd_ndisc {
|
||||
sd_event *event;
|
||||
int event_priority;
|
||||
|
||||
struct in6_addr link_local_addr;
|
||||
struct ether_addr mac_addr;
|
||||
|
||||
sd_event_source *recv_event_source;
|
||||
|
||||
@@ -1,7 +1,45 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include <netinet/icmp6.h>
|
||||
|
||||
#include "ndisc-protocol.h"
|
||||
|
||||
int ndisc_option_parse(
|
||||
ICMP6Packet *p,
|
||||
size_t offset,
|
||||
uint8_t *ret_type,
|
||||
size_t *ret_len,
|
||||
const uint8_t **ret_opt) {
|
||||
|
||||
assert(p);
|
||||
|
||||
if (offset == p->raw_size)
|
||||
return -ESPIPE; /* end of the packet */
|
||||
|
||||
if (offset > p->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
if (p->raw_size - offset < sizeof(struct nd_opt_hdr))
|
||||
return -EBADMSG;
|
||||
|
||||
const struct nd_opt_hdr *hdr = (const struct nd_opt_hdr*) (p->raw_packet + offset);
|
||||
if (hdr->nd_opt_len == 0)
|
||||
return -EBADMSG;
|
||||
|
||||
size_t len = hdr->nd_opt_len * 8;
|
||||
if (p->raw_size - offset < len)
|
||||
return -EBADMSG;
|
||||
|
||||
if (ret_type)
|
||||
*ret_type = hdr->nd_opt_type;
|
||||
if (ret_len)
|
||||
*ret_len = len;
|
||||
if (ret_opt)
|
||||
*ret_opt = p->raw_packet + offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const uint8_t prefix_length_code_to_prefix_length[_PREFIX_LENGTH_CODE_MAX] = {
|
||||
[PREFIX_LENGTH_CODE_96] = 96,
|
||||
[PREFIX_LENGTH_CODE_64] = 64,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "icmp6-packet.h"
|
||||
#include "time-util.h"
|
||||
|
||||
/* RFC 8781: PREF64 or (NAT64 prefix) */
|
||||
@@ -29,3 +30,10 @@ struct nd_opt_prefix64_info {
|
||||
|
||||
int pref64_plc_to_prefix_length(uint16_t plc, uint8_t *ret);
|
||||
int pref64_prefix_length_to_plc(uint8_t prefixlen, uint8_t *ret);
|
||||
|
||||
int ndisc_option_parse(
|
||||
ICMP6Packet *p,
|
||||
size_t offset,
|
||||
uint8_t *ret_type,
|
||||
size_t *ret_len,
|
||||
const uint8_t **ret_opt);
|
||||
|
||||
@@ -7,16 +7,13 @@
|
||||
|
||||
#include "sd-ndisc.h"
|
||||
|
||||
#include "icmp6-packet.h"
|
||||
#include "time-util.h"
|
||||
|
||||
struct sd_ndisc_router {
|
||||
unsigned n_ref;
|
||||
|
||||
triple_timestamp timestamp;
|
||||
struct in6_addr address;
|
||||
|
||||
/* The raw packet size. The data is appended to the object, accessible via NDIS_ROUTER_RAW() */
|
||||
size_t raw_size;
|
||||
ICMP6Packet *packet;
|
||||
|
||||
/* The current read index for the iterative option interface */
|
||||
size_t rindex;
|
||||
@@ -32,7 +29,7 @@ struct sd_ndisc_router {
|
||||
};
|
||||
|
||||
static inline void* NDISC_ROUTER_RAW(const sd_ndisc_router *rt) {
|
||||
return (uint8_t*) rt + ALIGN(sizeof(sd_ndisc_router));
|
||||
return ASSERT_PTR(ASSERT_PTR(rt)->packet)->raw_packet;
|
||||
}
|
||||
|
||||
static inline void *NDISC_ROUTER_OPTION_DATA(const sd_ndisc_router *rt) {
|
||||
@@ -46,5 +43,5 @@ static inline size_t NDISC_ROUTER_OPTION_LENGTH(const sd_ndisc_router *rt) {
|
||||
return ((uint8_t*) NDISC_ROUTER_OPTION_DATA(rt))[1] * 8;
|
||||
}
|
||||
|
||||
sd_ndisc_router *ndisc_router_new(size_t raw_size);
|
||||
sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet);
|
||||
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt);
|
||||
|
||||
@@ -18,46 +18,44 @@
|
||||
#include "ndisc-router-internal.h"
|
||||
#include "strv.h"
|
||||
|
||||
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, mfree);
|
||||
|
||||
sd_ndisc_router *ndisc_router_new(size_t raw_size) {
|
||||
sd_ndisc_router *rt;
|
||||
|
||||
if (raw_size > SIZE_MAX - ALIGN(sizeof(sd_ndisc_router)))
|
||||
return NULL;
|
||||
|
||||
rt = malloc0(ALIGN(sizeof(sd_ndisc_router)) + raw_size);
|
||||
static sd_ndisc_router* ndisc_router_free(sd_ndisc_router *rt) {
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
rt->raw_size = raw_size;
|
||||
rt->n_ref = 1;
|
||||
icmp6_packet_unref(rt->packet);
|
||||
return mfree(rt);
|
||||
}
|
||||
|
||||
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_router, sd_ndisc_router, ndisc_router_free);
|
||||
|
||||
sd_ndisc_router* ndisc_router_new(ICMP6Packet *packet) {
|
||||
sd_ndisc_router *rt;
|
||||
|
||||
assert(packet);
|
||||
|
||||
rt = new(sd_ndisc_router, 1);
|
||||
if (!rt)
|
||||
return NULL;
|
||||
|
||||
*rt = (sd_ndisc_router) {
|
||||
.n_ref = 1,
|
||||
.packet = icmp6_packet_ref(packet),
|
||||
};
|
||||
|
||||
return rt;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_get_address(sd_ndisc_router *rt, struct in6_addr *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (in6_addr_is_null(&rt->address))
|
||||
return -ENODATA;
|
||||
|
||||
*ret = rt->address;
|
||||
return 0;
|
||||
return icmp6_packet_get_sender_address(rt->packet, ret);
|
||||
}
|
||||
|
||||
int sd_ndisc_router_get_timestamp(sd_ndisc_router *rt, clockid_t clock, uint64_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(TRIPLE_TIMESTAMP_HAS_CLOCK(clock), -EOPNOTSUPP);
|
||||
assert_return(clock_supported(clock), -EOPNOTSUPP);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (!triple_timestamp_is_set(&rt->timestamp))
|
||||
return -ENODATA;
|
||||
|
||||
*ret = triple_timestamp_by_clock(&rt->timestamp, clock);
|
||||
return 0;
|
||||
return icmp6_packet_get_timestamp(rt->packet, clock, ret);
|
||||
}
|
||||
|
||||
#define DEFINE_GET_TIMESTAMP(name) \
|
||||
@@ -98,7 +96,7 @@ int sd_ndisc_router_get_raw(sd_ndisc_router *rt, const void **ret, size_t *ret_s
|
||||
assert_return(ret_size, -EINVAL);
|
||||
|
||||
*ret = NDISC_ROUTER_RAW(rt);
|
||||
*ret_size = rt->raw_size;
|
||||
*ret_size = rt->packet->raw_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -119,27 +117,20 @@ static bool pref64_option_verify(const struct nd_opt_prefix64_info *p, size_t le
|
||||
}
|
||||
|
||||
int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
struct nd_router_advert *a;
|
||||
const uint8_t *p;
|
||||
const struct nd_router_advert *a;
|
||||
bool has_mtu = false, has_flag_extension = false;
|
||||
size_t left;
|
||||
int r;
|
||||
|
||||
assert(rt);
|
||||
assert(rt->packet);
|
||||
|
||||
if (rt->raw_size < sizeof(struct nd_router_advert))
|
||||
if (rt->packet->raw_size < sizeof(struct nd_router_advert))
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Too small to be a router advertisement, ignoring.");
|
||||
|
||||
/* Router advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
|
||||
a = NDISC_ROUTER_RAW(rt);
|
||||
|
||||
if (a->nd_ra_type != ND_ROUTER_ADVERT)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received ND packet that is not a router advertisement, ignoring.");
|
||||
|
||||
if (a->nd_ra_code != 0)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Received ND packet with wrong RA code, ignoring.");
|
||||
a = (const struct nd_router_advert*) rt->packet->raw_packet;
|
||||
assert(a->nd_ra_type == ND_ROUTER_ADVERT);
|
||||
assert(a->nd_ra_code == 0);
|
||||
|
||||
rt->hop_limit = a->nd_ra_curhoplimit;
|
||||
rt->flags = a->nd_ra_flags_reserved; /* the first 8 bits */
|
||||
@@ -151,29 +142,13 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
if (!IN_SET(rt->preference, SD_NDISC_PREFERENCE_LOW, SD_NDISC_PREFERENCE_HIGH))
|
||||
rt->preference = SD_NDISC_PREFERENCE_MEDIUM;
|
||||
|
||||
p = (const uint8_t*) NDISC_ROUTER_RAW(rt) + sizeof(struct nd_router_advert);
|
||||
left = rt->raw_size - sizeof(struct nd_router_advert);
|
||||
|
||||
for (;;) {
|
||||
for (size_t offset = sizeof(struct nd_router_advert), length; offset < rt->packet->raw_size; offset += length) {
|
||||
uint8_t type;
|
||||
size_t length;
|
||||
const uint8_t *p;
|
||||
|
||||
if (left == 0)
|
||||
break;
|
||||
|
||||
if (left < 2)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Option lacks header, ignoring datagram.");
|
||||
|
||||
type = p[0];
|
||||
length = p[1] * 8;
|
||||
|
||||
if (length == 0)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Zero-length option, ignoring datagram.");
|
||||
if (left < length)
|
||||
return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
|
||||
"Option truncated, ignoring datagram.");
|
||||
r = ndisc_option_parse(rt->packet, offset, &type, &length, &p);
|
||||
if (r < 0)
|
||||
return log_ndisc_errno(nd, r, "Failed to parse NDisc option header, ignoring: %m");
|
||||
|
||||
switch (type) {
|
||||
|
||||
@@ -261,8 +236,6 @@ int ndisc_router_parse(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
"PREF64 prefix has invalid prefix length.");
|
||||
break;
|
||||
}}
|
||||
|
||||
p += length, left -= length;
|
||||
}
|
||||
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
@@ -332,43 +305,30 @@ int sd_ndisc_router_get_mtu(sd_ndisc_router *rt, uint32_t *ret) {
|
||||
int sd_ndisc_router_option_rewind(sd_ndisc_router *rt) {
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
assert(rt->raw_size >= sizeof(struct nd_router_advert));
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
assert(rt->packet);
|
||||
assert(rt->packet->raw_size >= sizeof(struct nd_router_advert));
|
||||
|
||||
return rt->rindex < rt->raw_size;
|
||||
rt->rindex = sizeof(struct nd_router_advert);
|
||||
return rt->rindex < rt->packet->raw_size;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_next(sd_ndisc_router *rt) {
|
||||
size_t length;
|
||||
int r;
|
||||
|
||||
assert_return(rt, -EINVAL);
|
||||
|
||||
if (rt->rindex == rt->raw_size) /* EOF */
|
||||
return -ESPIPE;
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
|
||||
return -EBADMSG;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (rt->rindex + length > rt->raw_size)
|
||||
return -EBADMSG;
|
||||
r = ndisc_option_parse(rt->packet, rt->rindex, NULL, &length, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
rt->rindex += length;
|
||||
return rt->rindex < rt->raw_size;
|
||||
return rt->rindex < rt->packet->raw_size;
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_get_type(sd_ndisc_router *rt, uint8_t *ret) {
|
||||
assert_return(rt, -EINVAL);
|
||||
assert_return(ret, -EINVAL);
|
||||
|
||||
if (rt->rindex == rt->raw_size) /* EOF */
|
||||
return -ESPIPE;
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size) /* Truncated message */
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = NDISC_ROUTER_OPTION_TYPE(rt);
|
||||
return 0;
|
||||
return ndisc_option_parse(rt->packet, rt->rindex, ret, NULL, NULL);
|
||||
}
|
||||
|
||||
int sd_ndisc_router_option_is_type(sd_ndisc_router *rt, uint8_t type) {
|
||||
@@ -393,11 +353,11 @@ int sd_ndisc_router_option_get_raw(sd_ndisc_router *rt, const void **ret, size_t
|
||||
|
||||
/* Note that this returns the full option, including the option header */
|
||||
|
||||
if (rt->rindex + 2 > rt->raw_size)
|
||||
if (rt->rindex + 2 > rt->packet->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
length = NDISC_ROUTER_OPTION_LENGTH(rt);
|
||||
if (rt->rindex + length > rt->raw_size)
|
||||
if (rt->rindex + length > rt->packet->raw_size)
|
||||
return -EBADMSG;
|
||||
|
||||
*ret = (uint8_t*) NDISC_ROUTER_RAW(rt) + rt->rindex;
|
||||
|
||||
@@ -96,6 +96,18 @@ int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_set_link_local_address(sd_ndisc *nd, const struct in6_addr *addr) {
|
||||
assert_return(nd, -EINVAL);
|
||||
assert_return(!addr || in6_addr_is_link_local(addr), -EINVAL);
|
||||
|
||||
if (addr)
|
||||
nd->link_local_addr = *addr;
|
||||
else
|
||||
zero(nd->link_local_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr) {
|
||||
assert_return(nd, -EINVAL);
|
||||
|
||||
@@ -186,11 +198,16 @@ int sd_ndisc_new(sd_ndisc **ret) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
static int ndisc_handle_router(sd_ndisc *nd, ICMP6Packet *packet) {
|
||||
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
|
||||
int r;
|
||||
|
||||
assert(nd);
|
||||
assert(rt);
|
||||
assert(packet);
|
||||
|
||||
rt = ndisc_router_new(packet);
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
r = ndisc_router_parse(nd, rt);
|
||||
if (r < 0)
|
||||
@@ -208,56 +225,46 @@ static int ndisc_handle_datagram(sd_ndisc *nd, sd_ndisc_router *rt) {
|
||||
}
|
||||
|
||||
static int ndisc_recv(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
_cleanup_(sd_ndisc_router_unrefp) sd_ndisc_router *rt = NULL;
|
||||
_cleanup_(icmp6_packet_unrefp) ICMP6Packet *packet = NULL;
|
||||
sd_ndisc *nd = ASSERT_PTR(userdata);
|
||||
ssize_t buflen;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(nd->event);
|
||||
|
||||
buflen = next_datagram_size_fd(fd);
|
||||
if (ERRNO_IS_NEG_TRANSIENT(buflen) || ERRNO_IS_NEG_DISCONNECT(buflen))
|
||||
return 0;
|
||||
if (buflen < 0) {
|
||||
log_ndisc_errno(nd, buflen, "Failed to determine datagram size to read, ignoring: %m");
|
||||
r = icmp6_packet_receive(fd, &packet);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Failed to receive ICMPv6 packet, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
rt = ndisc_router_new(buflen);
|
||||
if (!rt)
|
||||
return -ENOMEM;
|
||||
|
||||
r = icmp6_receive(fd, NDISC_ROUTER_RAW(rt), rt->raw_size, &rt->address, &rt->timestamp);
|
||||
if (ERRNO_IS_NEG_TRANSIENT(r) || ERRNO_IS_NEG_DISCONNECT(r))
|
||||
return 0;
|
||||
if (r < 0)
|
||||
switch (r) {
|
||||
case -EADDRNOTAVAIL:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet from neither link-local nor null address, ignoring.");
|
||||
return 0;
|
||||
|
||||
case -EMULTIHOP:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet with an invalid hop limit, ignoring.");
|
||||
return 0;
|
||||
|
||||
case -EPFNOSUPPORT:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet with an invalid source address, ignoring.");
|
||||
return 0;
|
||||
|
||||
default:
|
||||
log_ndisc_errno(nd, r, "Unexpected error while receiving an ICMPv6 packet, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The function icmp6_receive() accepts the null source address, but RFC 4861 Section 6.1.2 states
|
||||
* that hosts MUST discard messages with the null source address. */
|
||||
if (in6_addr_is_null(&rt->address)) {
|
||||
if (in6_addr_is_null(&packet->sender_address)) {
|
||||
log_ndisc(nd, "Received an ICMPv6 packet from null address, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) ndisc_handle_datagram(nd, rt);
|
||||
if (in6_addr_equal(&packet->sender_address, &nd->link_local_addr)) {
|
||||
log_ndisc(nd, "Received an ICMPv6 packet sent by the same interface, ignoring.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = icmp6_packet_get_type(packet);
|
||||
if (r < 0) {
|
||||
log_ndisc_errno(nd, r, "Received an invalid ICMPv6 packet, ignoring: %m");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (r) {
|
||||
case ND_ROUTER_ADVERT:
|
||||
(void) ndisc_handle_router(nd, packet);
|
||||
break;
|
||||
|
||||
default:
|
||||
log_ndisc(nd, "Received an ICMPv6 packet with unexpected type %i, ignoring.", r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -456,13 +456,6 @@ static int ndisc_router_process_default(Link *link, sd_ndisc_router *rt) {
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to get gateway address from RA: %m");
|
||||
|
||||
if (link_get_ipv6_address(link, &gateway, 0, NULL) >= 0) {
|
||||
if (DEBUG_LOGGING)
|
||||
log_link_debug(link, "No NDisc route added, gateway %s matches local address",
|
||||
IN6_ADDR_TO_STRING(&gateway));
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sd_ndisc_router_get_preference(rt, &preference);
|
||||
if (r < 0)
|
||||
return log_link_warning_errno(link, r, "Failed to get router preference from RA: %m");
|
||||
@@ -1774,6 +1767,10 @@ int ndisc_start(Link *link) {
|
||||
if (in6_addr_is_null(&link->ipv6ll_address))
|
||||
return 0;
|
||||
|
||||
r = sd_ndisc_set_link_local_address(link->ndisc, &link->ipv6ll_address);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_link_debug(link, "Discovering IPv6 routers");
|
||||
|
||||
r = sd_ndisc_start(link->ndisc);
|
||||
|
||||
@@ -62,6 +62,7 @@ int sd_ndisc_set_callback(sd_ndisc *nd, sd_ndisc_callback_t cb, void *userdata);
|
||||
int sd_ndisc_set_ifindex(sd_ndisc *nd, int interface_index);
|
||||
int sd_ndisc_set_ifname(sd_ndisc *nd, const char *interface_name);
|
||||
int sd_ndisc_get_ifname(sd_ndisc *nd, const char **ret);
|
||||
int sd_ndisc_set_link_local_address(sd_ndisc *nd, const struct in6_addr *addr);
|
||||
int sd_ndisc_set_mac(sd_ndisc *nd, const struct ether_addr *mac_addr);
|
||||
|
||||
_SD_END_DECLARATIONS;
|
||||
|
||||
Reference in New Issue
Block a user