]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-route.c
network: add support for dropping address
[thirdparty/systemd.git] / src / network / networkd-route.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <net/if.h>
23
24 #include "networkd.h"
25
26 #include "utf8.h"
27 #include "util.h"
28 #include "conf-parser.h"
29 #include "net-util.h"
30
31 int route_new(Network *network, unsigned section, Route **ret) {
32 _cleanup_route_free_ Route *route = NULL;
33
34 if (section) {
35 uint64_t key = section;
36
37 route = hashmap_get(network->routes_by_section, &key);
38 if (route) {
39 *ret = route;
40 route = NULL;
41
42 return 0;
43 }
44 }
45
46 route = new0(Route, 1);
47 if (!route)
48 return -ENOMEM;
49
50 route->network = network;
51
52 LIST_PREPEND(routes, network->routes, route);
53
54 if (section) {
55 route->section = section;
56 hashmap_put(network->routes_by_section, &route->section, route);
57 }
58
59 *ret = route;
60 route = NULL;
61
62 return 0;
63 }
64
65 void route_free(Route *route) {
66 if (!route)
67 return;
68
69 LIST_REMOVE(routes, route->network->routes, route);
70
71 if (route->section)
72 hashmap_remove(route->network->routes_by_section,
73 &route->section);
74
75 free(route);
76 }
77
78 int route_configure(Route *route, Link *link,
79 sd_rtnl_message_handler_t callback) {
80 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
81 int r;
82
83 assert(link);
84 assert(link->manager);
85 assert(link->manager->rtnl);
86 assert(link->ifindex > 0);
87 assert(route->family == AF_INET || route->family == AF_INET6);
88
89 r = sd_rtnl_message_route_new(RTM_NEWROUTE, route->family, &req);
90 if (r < 0) {
91 log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r));
92 return r;
93 }
94
95 if (route->family == AF_INET)
96 r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
97 else if (route->family == AF_INET6)
98 r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
99 if (r < 0) {
100 log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
101 return r;
102 }
103
104 if (route->dst_prefixlen) {
105 if (route->family == AF_INET)
106 r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
107 else if (route->family == AF_INET6)
108 r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
109 if (r < 0) {
110 log_error("Could not append RTA_DST attribute: %s", strerror(-r));
111 return r;
112 }
113
114 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
115 if (r < 0) {
116 log_error("Could not set destination prefix length: %s", strerror(-r));
117 return r;
118 }
119 }
120
121 r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
122 if (r < 0) {
123 log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
124 return r;
125 }
126
127 r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
128 if (r < 0) {
129 log_error("Could not send rtnetlink message: %s", strerror(-r));
130 return r;
131 }
132
133 return 0;
134 }
135
136 int config_parse_gateway(const char *unit,
137 const char *filename,
138 unsigned line,
139 const char *section,
140 unsigned section_line,
141 const char *lvalue,
142 int ltype,
143 const char *rvalue,
144 void *data,
145 void *userdata) {
146 Network *network = userdata;
147 _cleanup_route_free_ Route *n = NULL;
148 _cleanup_free_ char *route = NULL;
149 int r;
150
151 assert(filename);
152 assert(section);
153 assert(lvalue);
154 assert(rvalue);
155 assert(data);
156
157 if (streq(section, "Network")) {
158 /* we are not in an Route section, so treat
159 * this as the special '0' section */
160 section_line = 0;
161 }
162
163 r = route_new(network, section_line, &n);
164 if (r < 0)
165 return r;
166
167 r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
168 if (r < 0) {
169 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
170 "Route is invalid, ignoring assignment: %s", route);
171 return 0;
172 }
173
174 n = NULL;
175
176 return 0;
177 }
178
179 int config_parse_destination(const char *unit,
180 const char *filename,
181 unsigned line,
182 const char *section,
183 unsigned section_line,
184 const char *lvalue,
185 int ltype,
186 const char *rvalue,
187 void *data,
188 void *userdata) {
189 Network *network = userdata;
190 _cleanup_route_free_ Route *n = NULL;
191 _cleanup_free_ char *address = NULL;
192 const char *e;
193 int r;
194
195 assert(filename);
196 assert(section);
197 assert(lvalue);
198 assert(rvalue);
199 assert(data);
200
201 r = route_new(network, section_line, &n);
202 if (r < 0)
203 return r;
204
205 /* Destination=address/prefixlen */
206
207 /* address */
208 e = strchr(rvalue, '/');
209 if (e) {
210 address = strndup(rvalue, e - rvalue);
211 if (!address)
212 return log_oom();
213 } else {
214 address = strdup(rvalue);
215 if (!address)
216 return log_oom();
217 }
218
219 r = net_parse_inaddr(address, &n->family, &n->dst_addr);
220 if (r < 0) {
221 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
222 "Destination is invalid, ignoring assignment: %s", address);
223 return 0;
224 }
225
226 /* prefixlen */
227 if (e) {
228 unsigned i;
229
230 r = safe_atou(e + 1, &i);
231 if (r < 0) {
232 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
233 "Route destination prefix length is invalid, "
234 "ignoring assignment: %s", e + 1);
235 return 0;
236 }
237
238 n->dst_prefixlen = (unsigned char) i;
239 } else {
240 switch (n->family) {
241 case AF_INET:
242 n->dst_prefixlen = 32;
243 break;
244 case AF_INET6:
245 n->dst_prefixlen = 128;
246 break;
247 }
248 }
249
250 n = NULL;
251
252 return 0;
253 }