1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <netinet/in.h>
4 #include <linux/if.h>
5
6 #include "netlink-util.h"
7 #include "networkd-ipv6-proxy-ndp.h"
8 #include "networkd-link.h"
9 #include "networkd-manager.h"
10 #include "networkd-network.h"
11 #include "networkd-queue.h"
12 #include "socket-util.h"
13 #include "string-util.h"
14
network_adjust_ipv6_proxy_ndp(Network * network)15 void network_adjust_ipv6_proxy_ndp(Network *network) {
16 assert(network);
17
18 if (set_isempty(network->ipv6_proxy_ndp_addresses))
19 return;
20
21 if (!socket_ipv6_is_supported()) {
22 log_once(LOG_WARNING,
23 "%s: IPv6 proxy NDP addresses are set, but IPv6 is not supported by kernel, "
24 "Ignoring IPv6 proxy NDP addresses.", network->filename);
25 network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses);
26 }
27 }
28
ipv6_proxy_ndp_address_configure_handler(sd_netlink * rtnl,sd_netlink_message * m,Request * req,Link * link,struct in6_addr * address)29 static int ipv6_proxy_ndp_address_configure_handler(
30 sd_netlink *rtnl,
31 sd_netlink_message *m,
32 Request *req,
33 Link *link,
34 struct in6_addr *address) {
35
36 int r;
37
38 assert(m);
39 assert(link);
40
41 r = sd_netlink_message_get_errno(m);
42 if (r < 0)
43 log_link_message_warning_errno(link, m, r, "Could not add IPv6 proxy ndp address entry, ignoring");
44
45 if (link->static_ipv6_proxy_ndp_messages == 0) {
46 log_link_debug(link, "IPv6 proxy NDP addresses set.");
47 link->static_ipv6_proxy_ndp_configured = true;
48 link_check_ready(link);
49 }
50
51 return 1;
52 }
53
54 /* send a request to the kernel to add an IPv6 Proxy entry to the neighbour table */
ipv6_proxy_ndp_address_configure(const struct in6_addr * address,Link * link,Request * req)55 static int ipv6_proxy_ndp_address_configure(const struct in6_addr *address, Link *link, Request *req) {
56 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
57 int r;
58
59 assert(address);
60 assert(link);
61 assert(link->manager);
62 assert(link->manager->rtnl);
63 assert(req);
64
65 /* create new netlink message */
66 r = sd_rtnl_message_new_neigh(link->manager->rtnl, &m, RTM_NEWNEIGH, link->ifindex, AF_INET6);
67 if (r < 0)
68 return r;
69
70 r = sd_rtnl_message_neigh_set_flags(m, NTF_PROXY);
71 if (r < 0)
72 return r;
73
74 r = sd_netlink_message_append_in6_addr(m, NDA_DST, address);
75 if (r < 0)
76 return r;
77
78 return request_call_netlink_async(link->manager->rtnl, m, req);
79 }
80
ipv6_proxy_ndp_address_process_request(Request * req,Link * link,struct in6_addr * address)81 static int ipv6_proxy_ndp_address_process_request(Request *req, Link *link, struct in6_addr *address) {
82 int r;
83
84 assert(req);
85 assert(link);
86 assert(address);
87
88 if (!link_is_ready_to_configure(link, false))
89 return 0;
90
91 r = ipv6_proxy_ndp_address_configure(address, link, req);
92 if (r < 0)
93 return log_link_warning_errno(link, r, "Failed to configure IPv6 proxy NDP address: %m");
94
95 return 1;
96 }
97
link_request_static_ipv6_proxy_ndp_addresses(Link * link)98 int link_request_static_ipv6_proxy_ndp_addresses(Link *link) {
99 struct in6_addr *address;
100 int r;
101
102 assert(link);
103 assert(link->network);
104
105 link->static_ipv6_proxy_ndp_configured = false;
106
107 SET_FOREACH(address, link->network->ipv6_proxy_ndp_addresses) {
108 r = link_queue_request_safe(link, REQUEST_TYPE_IPV6_PROXY_NDP,
109 address, NULL,
110 in6_addr_hash_func,
111 in6_addr_compare_func,
112 ipv6_proxy_ndp_address_process_request,
113 &link->static_ipv6_proxy_ndp_messages,
114 ipv6_proxy_ndp_address_configure_handler,
115 NULL);
116 if (r < 0)
117 return log_link_warning_errno(link, r, "Failed to request IPv6 proxy NDP address: %m");
118 }
119
120 if (link->static_ipv6_proxy_ndp_messages == 0) {
121 link->static_ipv6_proxy_ndp_configured = true;
122 link_check_ready(link);
123 } else {
124 log_link_debug(link, "Setting IPv6 proxy NDP addresses.");
125 link_set_state(link, LINK_STATE_CONFIGURING);
126 }
127
128 return 0;
129 }
130
config_parse_ipv6_proxy_ndp_address(const char * unit,const char * filename,unsigned line,const char * section,unsigned section_line,const char * lvalue,int ltype,const char * rvalue,void * data,void * userdata)131 int config_parse_ipv6_proxy_ndp_address(
132 const char *unit,
133 const char *filename,
134 unsigned line,
135 const char *section,
136 unsigned section_line,
137 const char *lvalue,
138 int ltype,
139 const char *rvalue,
140 void *data,
141 void *userdata) {
142
143 _cleanup_free_ struct in6_addr *address = NULL;
144 Network *network = userdata;
145 union in_addr_union buffer;
146 int r;
147
148 assert(filename);
149 assert(rvalue);
150 assert(network);
151
152 if (isempty(rvalue)) {
153 network->ipv6_proxy_ndp_addresses = set_free_free(network->ipv6_proxy_ndp_addresses);
154 return 0;
155 }
156
157 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
158 if (r < 0) {
159 log_syntax(unit, LOG_WARNING, filename, line, r,
160 "Failed to parse IPv6 proxy NDP address, ignoring: %s", rvalue);
161 return 0;
162 }
163
164 if (in_addr_is_null(AF_INET6, &buffer)) {
165 log_syntax(unit, LOG_WARNING, filename, line, 0,
166 "IPv6 proxy NDP address cannot be the ANY address, ignoring: %s", rvalue);
167 return 0;
168 }
169
170 address = newdup(struct in6_addr, &buffer.in6, 1);
171 if (!address)
172 return log_oom();
173
174 r = set_ensure_put(&network->ipv6_proxy_ndp_addresses, &in6_addr_hash_ops, address);
175 if (r < 0)
176 return log_oom();
177 if (r > 0)
178 TAKE_PTR(address);
179
180 return 0;
181 }
182