]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
a0e5c15d | 2 | |
9aa5d8ba | 3 | #include <netinet/in.h> |
a0e5c15d FK |
4 | #include <linux/if.h> |
5 | #include <unistd.h> | |
6 | ||
7 | #include "fileio.h" | |
8 | #include "netlink-util.h" | |
9 | #include "networkd-ipv6-proxy-ndp.h" | |
10 | #include "networkd-link.h" | |
11 | #include "networkd-manager.h" | |
12 | #include "networkd-network.h" | |
18a121f9 | 13 | #include "socket-util.h" |
62e021a9 YW |
14 | #include "string-util.h" |
15 | #include "sysctl-util.h" | |
a0e5c15d FK |
16 | |
17 | static bool ipv6_proxy_ndp_is_needed(Link *link) { | |
18 | assert(link); | |
19 | ||
20 | if (link->flags & IFF_LOOPBACK) | |
21 | return false; | |
22 | ||
23 | if (!link->network) | |
24 | return false; | |
25 | ||
18a121f9 | 26 | if (link->network->ipv6_proxy_ndp >= 0) |
465dfe59 HV |
27 | return link->network->ipv6_proxy_ndp; |
28 | ||
a0e5c15d FK |
29 | if (link->network->n_ipv6_proxy_ndp_addresses == 0) |
30 | return false; | |
31 | ||
32 | return true; | |
33 | } | |
34 | ||
35 | static int ipv6_proxy_ndp_set(Link *link) { | |
62e021a9 YW |
36 | bool v; |
37 | int r; | |
a0e5c15d FK |
38 | |
39 | assert(link); | |
40 | ||
18a121f9 LP |
41 | if (!socket_ipv6_is_supported()) |
42 | return 0; | |
43 | ||
a0e5c15d | 44 | v = ipv6_proxy_ndp_is_needed(link); |
a0e5c15d | 45 | |
62e021a9 | 46 | r = sysctl_write_ip_property_boolean(AF_INET6, link->ifname, "proxy_ndp", v); |
a0e5c15d FK |
47 | if (r < 0) |
48 | log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m"); | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
9560e5b3 | 53 | static int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) { |
a0e5c15d FK |
54 | _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL; |
55 | ||
56 | assert(network); | |
57 | assert(ret); | |
58 | ||
59 | /* allocate space for IPv6ProxyNDPAddress entry */ | |
17f9c355 | 60 | ipv6_proxy_ndp_address = new(IPv6ProxyNDPAddress, 1); |
a0e5c15d FK |
61 | if (!ipv6_proxy_ndp_address) |
62 | return -ENOMEM; | |
63 | ||
17f9c355 YW |
64 | *ipv6_proxy_ndp_address = (IPv6ProxyNDPAddress) { |
65 | .network = network, | |
66 | }; | |
a0e5c15d FK |
67 | |
68 | LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address); | |
69 | network->n_ipv6_proxy_ndp_addresses++; | |
70 | ||
17f9c355 | 71 | *ret = TAKE_PTR(ipv6_proxy_ndp_address); |
a0e5c15d FK |
72 | |
73 | return 0; | |
74 | } | |
75 | ||
76 | void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) { | |
77 | if (!ipv6_proxy_ndp_address) | |
78 | return; | |
79 | ||
80 | if (ipv6_proxy_ndp_address->network) { | |
81 | LIST_REMOVE(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address->network->ipv6_proxy_ndp_addresses, | |
82 | ipv6_proxy_ndp_address); | |
83 | ||
84 | assert(ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses > 0); | |
85 | ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses--; | |
86 | } | |
87 | ||
88 | free(ipv6_proxy_ndp_address); | |
89 | } | |
90 | ||
91 | int config_parse_ipv6_proxy_ndp_address( | |
8a4871c7 YW |
92 | const char *unit, |
93 | const char *filename, | |
94 | unsigned line, | |
95 | const char *section, | |
96 | unsigned section_line, | |
97 | const char *lvalue, | |
98 | int ltype, | |
99 | const char *rvalue, | |
100 | void *data, | |
101 | void *userdata) { | |
a0e5c15d FK |
102 | |
103 | Network *network = userdata; | |
104 | _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL; | |
105 | int r; | |
106 | union in_addr_union buffer; | |
107 | ||
108 | assert(filename); | |
109 | assert(section); | |
110 | assert(lvalue); | |
111 | assert(rvalue); | |
112 | assert(data); | |
113 | ||
114 | r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address); | |
115 | if (r < 0) | |
116 | return r; | |
117 | ||
118 | r = in_addr_from_string(AF_INET6, rvalue, &buffer); | |
119 | if (r < 0) { | |
120 | log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 proxy NDP address, ignoring: %s", | |
121 | rvalue); | |
122 | return 0; | |
123 | } | |
124 | ||
c606db69 YW |
125 | if (in_addr_is_null(AF_INET6, &buffer)) { |
126 | log_syntax(unit, LOG_ERR, filename, line, 0, | |
87ac8d99 | 127 | "IPv6 proxy NDP address cannot be the ANY address, ignoring: %s", rvalue); |
a0e5c15d FK |
128 | return 0; |
129 | } | |
130 | ||
131 | ipv6_proxy_ndp_address->in_addr = buffer.in6; | |
132 | ipv6_proxy_ndp_address = NULL; | |
133 | ||
134 | return 0; | |
135 | } | |
136 | ||
302a796f | 137 | static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
a0e5c15d FK |
138 | int r; |
139 | ||
140 | assert(link); | |
141 | ||
142 | r = sd_netlink_message_get_errno(m); | |
143 | if (r < 0 && r != -EEXIST) | |
5ecb131d | 144 | log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring"); |
a0e5c15d FK |
145 | |
146 | return 1; | |
147 | } | |
148 | ||
149 | /* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */ | |
150 | int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) { | |
151 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
152 | sd_netlink *rtnl; | |
153 | int r; | |
154 | ||
155 | assert(link); | |
156 | assert(link->network); | |
157 | assert(link->manager); | |
158 | assert(ipv6_proxy_ndp_address); | |
159 | ||
160 | rtnl = link->manager->rtnl; | |
161 | ||
162 | /* create new netlink message */ | |
163 | r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6); | |
164 | if (r < 0) | |
98b02994 | 165 | return log_link_error_errno(link, r, "Could not create RTM_NEWNEIGH message: %m"); |
a0e5c15d FK |
166 | |
167 | r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY); | |
168 | if (r < 0) | |
98b02994 | 169 | return log_link_error_errno(link, r, "Could not set neighbor flags: %m"); |
a0e5c15d FK |
170 | |
171 | r = sd_netlink_message_append_in6_addr(req, NDA_DST, &ipv6_proxy_ndp_address->in_addr); | |
172 | if (r < 0) | |
98b02994 | 173 | return log_link_error_errno(link, r, "Could not append NDA_DST attribute: %m"); |
a0e5c15d | 174 | |
302a796f YW |
175 | r = netlink_call_async(rtnl, NULL, req, set_ipv6_proxy_ndp_address_handler, |
176 | link_netlink_destroy_callback, link); | |
a0e5c15d FK |
177 | if (r < 0) |
178 | return log_link_error_errno(link, r, "Could not send rtnetlink message: %m"); | |
179 | ||
1046bf9b YW |
180 | link_ref(link); |
181 | ||
a0e5c15d FK |
182 | return 0; |
183 | } | |
184 | ||
185 | /* configure all ipv6 proxy ndp addresses */ | |
186 | int ipv6_proxy_ndp_addresses_configure(Link *link) { | |
187 | IPv6ProxyNDPAddress *ipv6_proxy_ndp_address; | |
188 | int r; | |
189 | ||
18a121f9 LP |
190 | assert(link); |
191 | ||
a0e5c15d FK |
192 | /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */ |
193 | r = ipv6_proxy_ndp_set(link); | |
194 | if (r != 0) | |
195 | return r; | |
196 | ||
197 | LIST_FOREACH(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address, link->network->ipv6_proxy_ndp_addresses) { | |
198 | r = ipv6_proxy_ndp_address_configure(link, ipv6_proxy_ndp_address); | |
199 | if (r != 0) | |
200 | return r; | |
201 | } | |
202 | return 0; | |
203 | } |