Coverage Report

Created: 2026-10-28 06:53

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/systemd/src/libsystemd-network/sd-ndisc-neighbor.c
Line
Count
Source
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3
#include <netinet/icmp6.h>
4
5
#include "sd-ndisc.h"
6
7
#include "alloc-util.h"
8
#include "icmp6-packet.h"
9
#include "in-addr-util.h"
10
#include "ndisc-internal.h"
11
#include "ndisc-neighbor-internal.h"
12
#include "ndisc-option.h"
13
#include "set.h"
14
15
133
static sd_ndisc_neighbor* ndisc_neighbor_free(sd_ndisc_neighbor *na) {
16
133
        if (!na)
17
0
                return NULL;
18
19
133
        icmp6_packet_unref(na->packet);
20
133
        set_free(na->options);
21
133
        return mfree(na);
22
133
}
23
24
665
DEFINE_PUBLIC_TRIVIAL_REF_UNREF_FUNC(sd_ndisc_neighbor, sd_ndisc_neighbor, ndisc_neighbor_free);
Unexecuted instantiation: sd_ndisc_neighbor_ref
Unexecuted instantiation: sd_ndisc_neighbor_unref
25
665
26
665
sd_ndisc_neighbor* ndisc_neighbor_new(ICMP6Packet *packet) {
27
133
        sd_ndisc_neighbor *na;
28
29
133
        assert(packet);
30
31
133
        na = new(sd_ndisc_neighbor, 1);
32
133
        if (!na)
33
0
                return NULL;
34
35
133
        *na = (sd_ndisc_neighbor) {
36
133
                .n_ref = 1,
37
133
                .packet = icmp6_packet_ref(packet),
38
133
        };
39
40
133
        return na;
41
133
}
42
43
133
int ndisc_neighbor_parse(sd_ndisc *nd, sd_ndisc_neighbor *na) {
44
133
        int r;
45
46
133
        assert(na);
47
48
133
        if (na->packet->raw_size < sizeof(struct nd_neighbor_advert))
49
133
                return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
50
125
                                       "Too small to be a neighbor advertisement, ignoring datagram.");
51
52
        /* Neighbor advertisement packets are neatly aligned to 64-bit boundaries, hence we can access them directly */
53
125
        const struct nd_neighbor_advert *a = (const struct nd_neighbor_advert*) na->packet->raw_packet;
54
125
        assert(a->nd_na_type == ND_NEIGHBOR_ADVERT);
55
125
        if (a->nd_na_code != 0)
56
125
                return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
57
125
                                       "Received Neighbor Advertisement with non-zero code, ignoring datagram.");
58
59
125
        na->flags = a->nd_na_flags_reserved; /* the first 3 bits */
60
125
        na->target_address = a->nd_na_target;
61
62
        /* RFC 4861 section 4.4:
63
         * For solicited advertisements, the Target Address field in the Neighbor Solicitation message that
64
         * prompted this advertisement. For an unsolicited advertisement, the address whose link-layer
65
         * address has changed. The Target Address MUST NOT be a multicast address.
66
         *
67
         * Here, we only check if the target address is a link-layer address (or a null address, for safety)
68
         * when the message is an unsolicited neighbor advertisement. */
69
125
        if (!FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED))
70
121
                if (!in6_addr_is_link_local(&na->target_address) && !in6_addr_is_null(&na->target_address))
71
125
                        return log_ndisc_errno(nd, SYNTHETIC_ERRNO(EBADMSG),
72
9
                                               "Received ND packet with an invalid target address (%s), ignoring datagram.",
73
9
                                               IN6_ADDR_TO_STRING(&na->target_address));
74
75
9
        r = ndisc_parse_options(na->packet, &na->options);
76
9
        if (r < 0)
77
9
                return log_ndisc_errno(nd, r, "Failed to parse NDisc options in neighbor advertisement message, ignoring: %m");
78
79
7
        return 0;
80
9
}
81
82
0
int sd_ndisc_neighbor_get_sender_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
83
0
        assert_return(na, -EINVAL);
84
85
0
        return icmp6_packet_get_sender_address(na->packet, ret);
86
0
}
87
88
0
int sd_ndisc_neighbor_get_target_address(sd_ndisc_neighbor *na, struct in6_addr *ret) {
89
0
        assert_return(na, -EINVAL);
90
91
0
        if (in6_addr_is_null(&na->target_address))
92
                /* fall back to the sender address, for safety. */
93
0
                return sd_ndisc_neighbor_get_sender_address(na, ret);
94
95
0
        if (ret)
96
0
                *ret = na->target_address;
97
0
        return 0;
98
0
}
99
100
0
int sd_ndisc_neighbor_get_target_mac(sd_ndisc_neighbor *na, struct ether_addr *ret) {
101
0
        assert_return(na, -EINVAL);
102
103
0
        return ndisc_option_get_mac(na->options, SD_NDISC_OPTION_TARGET_LL_ADDRESS, ret);
104
0
}
105
106
0
int sd_ndisc_neighbor_get_flags(sd_ndisc_neighbor *na, uint32_t *ret) {
107
0
        assert_return(na, -EINVAL);
108
109
0
        if (ret)
110
0
                *ret = na->flags;
111
0
        return 0;
112
0
}
113
114
0
int sd_ndisc_neighbor_is_router(sd_ndisc_neighbor *na) {
115
0
        assert_return(na, -EINVAL);
116
117
0
        return FLAGS_SET(na->flags, ND_NA_FLAG_ROUTER);
118
0
}
119
120
0
int sd_ndisc_neighbor_is_solicited(sd_ndisc_neighbor *na) {
121
0
        assert_return(na, -EINVAL);
122
123
0
        return FLAGS_SET(na->flags, ND_NA_FLAG_SOLICITED);
124
0
}
125
126
0
int sd_ndisc_neighbor_is_override(sd_ndisc_neighbor *na) {
127
0
        assert_return(na, -EINVAL);
128
129
0
        return FLAGS_SET(na->flags, ND_NA_FLAG_OVERRIDE);
130
0
}