chiark / gitweb /
networkd: distinguish between static and dynamic addresses/routes
[elogind.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_static(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(static_routes, network->static_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 int route_new_dynamic(Route **ret) {
66         _cleanup_route_free_ Route *route = NULL;
67
68         route = new0(Route, 1);
69         if (!route)
70                 return -ENOMEM;
71
72         *ret = route;
73         route = NULL;
74
75         return 0;
76 }
77
78 void route_free(Route *route) {
79         if (!route)
80                 return;
81
82         if (route->network) {
83                 LIST_REMOVE(static_routes, route->network->static_routes, route);
84
85                 if (route->section)
86                         hashmap_remove(route->network->routes_by_section,
87                                        &route->section);
88         }
89
90         free(route);
91 }
92
93 int route_configure(Route *route, Link *link,
94                     sd_rtnl_message_handler_t callback) {
95         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
96         int r;
97
98         assert(link);
99         assert(link->manager);
100         assert(link->manager->rtnl);
101         assert(link->ifindex > 0);
102         assert(route->family == AF_INET || route->family == AF_INET6);
103
104         r = sd_rtnl_message_route_new(RTM_NEWROUTE, route->family, &req);
105         if (r < 0) {
106                 log_error("Could not create RTM_NEWROUTE message: %s", strerror(-r));
107                 return r;
108         }
109
110         if (route->family == AF_INET)
111                 r = sd_rtnl_message_append_in_addr(req, RTA_GATEWAY, &route->in_addr.in);
112         else if (route->family == AF_INET6)
113                 r = sd_rtnl_message_append_in6_addr(req, RTA_GATEWAY, &route->in_addr.in6);
114         if (r < 0) {
115                 log_error("Could not append RTA_GATEWAY attribute: %s", strerror(-r));
116                 return r;
117         }
118
119         if (route->dst_prefixlen) {
120                 if (route->family == AF_INET)
121                         r = sd_rtnl_message_append_in_addr(req, RTA_DST, &route->dst_addr.in);
122                 else if (route->family == AF_INET6)
123                         r = sd_rtnl_message_append_in6_addr(req, RTA_DST, &route->dst_addr.in6);
124                 if (r < 0) {
125                         log_error("Could not append RTA_DST attribute: %s", strerror(-r));
126                         return r;
127                 }
128
129                 r = sd_rtnl_message_route_set_dst_prefixlen(req, route->dst_prefixlen);
130                 if (r < 0) {
131                         log_error("Could not set destination prefix length: %s", strerror(-r));
132                         return r;
133                 }
134         }
135
136         r = sd_rtnl_message_append_u32(req, RTA_OIF, link->ifindex);
137         if (r < 0) {
138                 log_error("Could not append RTA_OIF attribute: %s", strerror(-r));
139                 return r;
140         }
141
142         r = sd_rtnl_call_async(link->manager->rtnl, req, callback, link, 0, NULL);
143         if (r < 0) {
144                 log_error("Could not send rtnetlink message: %s", strerror(-r));
145                 return r;
146         }
147
148         return 0;
149 }
150
151 int config_parse_gateway(const char *unit,
152                 const char *filename,
153                 unsigned line,
154                 const char *section,
155                 unsigned section_line,
156                 const char *lvalue,
157                 int ltype,
158                 const char *rvalue,
159                 void *data,
160                 void *userdata) {
161         Network *network = userdata;
162         _cleanup_route_free_ Route *n = NULL;
163         _cleanup_free_ char *route = NULL;
164         int r;
165
166         assert(filename);
167         assert(section);
168         assert(lvalue);
169         assert(rvalue);
170         assert(data);
171
172         if (streq(section, "Network")) {
173                 /* we are not in an Route section, so treat
174                  * this as the special '0' section */
175                 section_line = 0;
176         }
177
178         r = route_new_static(network, section_line, &n);
179         if (r < 0)
180                 return r;
181
182         r = net_parse_inaddr(rvalue, &n->family, &n->in_addr);
183         if (r < 0) {
184                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
185                            "Route is invalid, ignoring assignment: %s", route);
186                 return 0;
187         }
188
189         n = NULL;
190
191         return 0;
192 }
193
194 int config_parse_destination(const char *unit,
195                 const char *filename,
196                 unsigned line,
197                 const char *section,
198                 unsigned section_line,
199                 const char *lvalue,
200                 int ltype,
201                 const char *rvalue,
202                 void *data,
203                 void *userdata) {
204         Network *network = userdata;
205         _cleanup_route_free_ Route *n = NULL;
206         _cleanup_free_ char *address = NULL;
207         const char *e;
208         int r;
209
210         assert(filename);
211         assert(section);
212         assert(lvalue);
213         assert(rvalue);
214         assert(data);
215
216         r = route_new_static(network, section_line, &n);
217         if (r < 0)
218                 return r;
219
220         /* Destination=address/prefixlen */
221
222         /* address */
223         e = strchr(rvalue, '/');
224         if (e) {
225                 address = strndup(rvalue, e - rvalue);
226                 if (!address)
227                         return log_oom();
228         } else {
229                 address = strdup(rvalue);
230                 if (!address)
231                         return log_oom();
232         }
233
234         r = net_parse_inaddr(address, &n->family, &n->dst_addr);
235         if (r < 0) {
236                 log_syntax(unit, LOG_ERR, filename, line, EINVAL,
237                            "Destination is invalid, ignoring assignment: %s", address);
238                 return 0;
239         }
240
241         /* prefixlen */
242         if (e) {
243                 unsigned i;
244
245                 r = safe_atou(e + 1, &i);
246                 if (r < 0) {
247                         log_syntax(unit, LOG_ERR, filename, line, EINVAL,
248                                    "Route destination prefix length is invalid, "
249                                    "ignoring assignment: %s", e + 1);
250                         return 0;
251                 }
252
253                 n->dst_prefixlen = (unsigned char) i;
254         } else {
255                 switch (n->family) {
256                         case AF_INET:
257                                 n->dst_prefixlen = 32;
258                                 break;
259                         case AF_INET6:
260                                 n->dst_prefixlen = 128;
261                                 break;
262                 }
263         }
264
265         n = NULL;
266
267         return 0;
268 }