chiark / gitweb /
networkd: link - split out dhcp4 handling
[elogind.git] / src / network / networkd-ipv4ll.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-2014 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 <netinet/ether.h>
23 #include <linux/if.h>
24
25 #include "networkd-link.h"
26 #include "network-internal.h"
27
28 static int ipv4ll_address_lost(Link *link) {
29         _cleanup_address_free_ Address *address = NULL;
30         _cleanup_route_free_ Route *route = NULL;
31         struct in_addr addr;
32         int r;
33
34         assert(link);
35
36         link->ipv4ll_route = false;
37         link->ipv4ll_address =  false;
38
39         r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
40         if (r < 0)
41                 return 0;
42
43         log_debug_link(link, "IPv4 link-local release %u.%u.%u.%u",
44                        ADDRESS_FMT_VAL(addr));
45
46         r = address_new_dynamic(&address);
47         if (r < 0) {
48                 log_error_link(link, "Could not allocate address: %s", strerror(-r));
49                 return r;
50         }
51
52         address->family = AF_INET;
53         address->in_addr.in = addr;
54         address->prefixlen = 16;
55         address->scope = RT_SCOPE_LINK;
56
57         address_drop(address, link, &link_address_drop_handler);
58
59         r = route_new_dynamic(&route, RTPROT_UNSPEC);
60         if (r < 0) {
61                 log_error_link(link, "Could not allocate route: %s",
62                                strerror(-r));
63                 return r;
64         }
65
66         route->family = AF_INET;
67         route->scope = RT_SCOPE_LINK;
68         route->metrics = IPV4LL_ROUTE_METRIC;
69
70         route_drop(route, link, &link_route_drop_handler);
71
72         link_client_handler(link);
73
74         return 0;
75 }
76
77 static int ipv4ll_route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
78         _cleanup_link_unref_ Link *link = userdata;
79         int r;
80
81         assert(link);
82         assert(!link->ipv4ll_route);
83
84         r = sd_rtnl_message_get_errno(m);
85         if (r < 0 && r != -EEXIST) {
86                 log_error_link(link, "could not set ipv4ll route: %s", strerror(-r));
87                 link_enter_failed(link);
88         }
89
90         link->ipv4ll_route = true;
91
92         if (link->ipv4ll_address == true)
93                 link_client_handler(link);
94
95         return 1;
96 }
97
98 static int ipv4ll_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
99         _cleanup_link_unref_ Link *link = userdata;
100         int r;
101
102         assert(link);
103         assert(!link->ipv4ll_address);
104
105         r = sd_rtnl_message_get_errno(m);
106         if (r < 0 && r != -EEXIST) {
107                 log_error_link(link, "could not set ipv4ll address: %s", strerror(-r));
108                 link_enter_failed(link);
109         } else if (r >= 0) {
110                 /* calling handler directly so take a ref */
111                 link_ref(link);
112                 link_get_address_handler(rtnl, m, link);
113         }
114
115         link->ipv4ll_address = true;
116
117         if (link->ipv4ll_route == true)
118                 link_client_handler(link);
119
120         return 1;
121 }
122
123 static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
124         _cleanup_address_free_ Address *ll_addr = NULL;
125         _cleanup_route_free_ Route *route = NULL;
126         struct in_addr address;
127         int r;
128
129         assert(ll);
130         assert(link);
131
132         r = sd_ipv4ll_get_address(ll, &address);
133         if (r == -ENOENT)
134                 return 0;
135         else if (r < 0)
136                 return r;
137
138         log_debug_link(link, "IPv4 link-local claim %u.%u.%u.%u",
139                        ADDRESS_FMT_VAL(address));
140
141         r = address_new_dynamic(&ll_addr);
142         if (r < 0)
143                 return r;
144
145         ll_addr->family = AF_INET;
146         ll_addr->in_addr.in = address;
147         ll_addr->prefixlen = 16;
148         ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htonl(0xfffffffflu >> ll_addr->prefixlen);
149         ll_addr->scope = RT_SCOPE_LINK;
150
151         r = address_configure(ll_addr, link, ipv4ll_address_handler);
152         if (r < 0)
153                 return r;
154
155         link->ipv4ll_address = false;
156
157         r = route_new_dynamic(&route, RTPROT_STATIC);
158         if (r < 0)
159                 return r;
160
161         route->family = AF_INET;
162         route->scope = RT_SCOPE_LINK;
163         route->metrics = IPV4LL_ROUTE_METRIC;
164
165         r = route_configure(route, link, ipv4ll_route_handler);
166         if (r < 0)
167                 return r;
168
169         link->ipv4ll_route = false;
170
171         return 0;
172 }
173
174 static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
175         Link *link = userdata;
176         int r;
177
178         assert(link);
179         assert(link->network);
180         assert(link->manager);
181
182         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
183                 return;
184
185         switch(event) {
186                 case IPV4LL_EVENT_STOP:
187                 case IPV4LL_EVENT_CONFLICT:
188                         r = ipv4ll_address_lost(link);
189                         if (r < 0) {
190                                 link_enter_failed(link);
191                                 return;
192                         }
193                         break;
194                 case IPV4LL_EVENT_BIND:
195                         r = ipv4ll_address_claimed(ll, link);
196                         if (r < 0) {
197                                 link_enter_failed(link);
198                                 return;
199                         }
200                         break;
201                 default:
202                         if (event < 0)
203                                 log_warning_link(link, "IPv4 link-local error: %s", strerror(-event));
204                         else
205                                 log_warning_link(link, "IPv4 link-local unknown event: %d", event);
206                         break;
207         }
208 }
209
210 int ipv4ll_configure(Link *link) {
211         uint8_t seed[8];
212         int r;
213
214         assert(link);
215         assert(link->network);
216         assert(link->network->ipv4ll);
217
218         r = sd_ipv4ll_new(&link->ipv4ll);
219         if (r < 0)
220                 return r;
221
222         if (link->udev_device) {
223                 r = net_get_unique_predictable_data(link->udev_device, seed);
224                 if (r >= 0) {
225                         r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
226                         if (r < 0)
227                                 return r;
228                 }
229         }
230
231         r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
232         if (r < 0)
233                 return r;
234
235         r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
236         if (r < 0)
237                 return r;
238
239         r = sd_ipv4ll_set_index(link->ipv4ll, link->ifindex);
240         if (r < 0)
241                 return r;
242
243         r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
244         if (r < 0)
245                 return r;
246
247         return 0;
248 }