]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-ipv6-proxy-ndp.c
526db69d5beb03e87a26cd4d2cf3e808306ae670
[thirdparty/systemd.git] / src / network / networkd-ipv6-proxy-ndp.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2017 Florian Klink <flokli@flokli.de>
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <netinet/ether.h>
22 #include <linux/if.h>
23 #include <unistd.h>
24
25 #include "fileio.h"
26 #include "netlink-util.h"
27 #include "networkd-ipv6-proxy-ndp.h"
28 #include "networkd-link.h"
29 #include "networkd-manager.h"
30 #include "networkd-network.h"
31 #include "string-util.h"
32
33 static bool ipv6_proxy_ndp_is_needed(Link *link) {
34 assert(link);
35
36 if (link->flags & IFF_LOOPBACK)
37 return false;
38
39 if (!link->network)
40 return false;
41
42 if (link->network->ipv6_proxy_ndp != -1)
43 return link->network->ipv6_proxy_ndp;
44
45 if (link->network->n_ipv6_proxy_ndp_addresses == 0)
46 return false;
47
48 return true;
49 }
50
51 static int ipv6_proxy_ndp_set(Link *link) {
52 const char *p = NULL;
53 int r, v;
54
55 assert(link);
56
57 v = ipv6_proxy_ndp_is_needed(link);
58 p = strjoina("/proc/sys/net/ipv6/conf/", link->ifname, "/proxy_ndp");
59
60 r = write_string_file(p, one_zero(v), WRITE_STRING_FILE_VERIFY_ON_FAILURE);
61 if (r < 0)
62 log_link_warning_errno(link, r, "Cannot configure proxy NDP for interface: %m");
63
64 return 0;
65 }
66
67 int ipv6_proxy_ndp_address_new_static(Network *network, IPv6ProxyNDPAddress **ret) {
68 _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
69
70 assert(network);
71 assert(ret);
72
73 /* allocate space for IPv6ProxyNDPAddress entry */
74 ipv6_proxy_ndp_address = new0(IPv6ProxyNDPAddress, 1);
75 if (!ipv6_proxy_ndp_address)
76 return -ENOMEM;
77
78 ipv6_proxy_ndp_address->network = network;
79
80 LIST_PREPEND(ipv6_proxy_ndp_addresses, network->ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address);
81 network->n_ipv6_proxy_ndp_addresses++;
82
83 *ret = ipv6_proxy_ndp_address;
84 ipv6_proxy_ndp_address = NULL;
85
86 return 0;
87 }
88
89 void ipv6_proxy_ndp_address_free(IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
90 if (!ipv6_proxy_ndp_address)
91 return;
92
93 if (ipv6_proxy_ndp_address->network) {
94 LIST_REMOVE(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address->network->ipv6_proxy_ndp_addresses,
95 ipv6_proxy_ndp_address);
96
97 assert(ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses > 0);
98 ipv6_proxy_ndp_address->network->n_ipv6_proxy_ndp_addresses--;
99 }
100
101 free(ipv6_proxy_ndp_address);
102 }
103
104 int config_parse_ipv6_proxy_ndp_address(
105 const char *unit,
106 const char *filename,
107 unsigned line,
108 const char *section,
109 unsigned section_line,
110 const char *lvalue,
111 int ltype,
112 const char *rvalue,
113 void *data,
114 void *userdata) {
115
116 Network *network = userdata;
117 _cleanup_(ipv6_proxy_ndp_address_freep) IPv6ProxyNDPAddress *ipv6_proxy_ndp_address = NULL;
118 int r;
119 union in_addr_union buffer;
120
121 assert(filename);
122 assert(section);
123 assert(lvalue);
124 assert(rvalue);
125 assert(data);
126
127 r = ipv6_proxy_ndp_address_new_static(network, &ipv6_proxy_ndp_address);
128 if (r < 0)
129 return r;
130
131 r = in_addr_from_string(AF_INET6, rvalue, &buffer);
132 if (r < 0) {
133 log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse IPv6 proxy NDP address, ignoring: %s",
134 rvalue);
135 return 0;
136 }
137
138 r = in_addr_is_null(AF_INET6, &buffer);
139 if (r != 0) {
140 log_syntax(unit, LOG_ERR, filename, line, r,
141 "IPv6 proxy NDP address can not be the ANY address, ignoring: %s", rvalue);
142 return 0;
143 }
144
145 ipv6_proxy_ndp_address->in_addr = buffer.in6;
146 ipv6_proxy_ndp_address = NULL;
147
148 return 0;
149 }
150
151 static int set_ipv6_proxy_ndp_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
152 Link *link = userdata;
153 int r;
154
155 assert(link);
156
157 r = sd_netlink_message_get_errno(m);
158 if (r < 0 && r != -EEXIST)
159 log_link_error_errno(link, r, "Could not add IPv6 proxy ndp address entry: %m");
160
161 return 1;
162 }
163
164 /* send a request to the kernel to add a IPv6 Proxy entry to the neighbour table */
165 int ipv6_proxy_ndp_address_configure(Link *link, IPv6ProxyNDPAddress *ipv6_proxy_ndp_address) {
166 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
167 sd_netlink *rtnl;
168 int r;
169
170 assert(link);
171 assert(link->network);
172 assert(link->manager);
173 assert(ipv6_proxy_ndp_address);
174
175 rtnl = link->manager->rtnl;
176
177 /* create new netlink message */
178 r = sd_rtnl_message_new_neigh(rtnl, &req, RTM_NEWNEIGH, link->ifindex, AF_INET6);
179 if (r < 0)
180 return rtnl_log_create_error(r);
181
182 r = sd_rtnl_message_neigh_set_flags(req, NLM_F_REQUEST | NTF_PROXY);
183 if (r < 0)
184 return rtnl_log_create_error(r);
185
186 r = sd_netlink_message_append_in6_addr(req, NDA_DST, &ipv6_proxy_ndp_address->in_addr);
187 if (r < 0)
188 return rtnl_log_create_error(r);
189
190 r = sd_netlink_call_async(rtnl, req, set_ipv6_proxy_ndp_address_handler, link, 0, NULL);
191 if (r < 0)
192 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
193
194 return 0;
195 }
196
197 /* configure all ipv6 proxy ndp addresses */
198 int ipv6_proxy_ndp_addresses_configure(Link *link) {
199 IPv6ProxyNDPAddress *ipv6_proxy_ndp_address;
200 int r;
201
202 /* enable or disable proxy_ndp itself depending on whether ipv6_proxy_ndp_addresses are set or not */
203 r = ipv6_proxy_ndp_set(link);
204 if (r != 0)
205 return r;
206
207 LIST_FOREACH(ipv6_proxy_ndp_addresses, ipv6_proxy_ndp_address, link->network->ipv6_proxy_ndp_addresses) {
208 r = ipv6_proxy_ndp_address_configure(link, ipv6_proxy_ndp_address);
209 if (r != 0)
210 return r;
211 }
212 return 0;
213 }