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