/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 | } |