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