]> git.proxmox.com Git - systemd.git/blob - src/network/networkd-ipv4ll.c
Imported Upstream version 228
[systemd.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 "network-internal.h"
26 #include "networkd-link.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_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
44
45 r = address_new(&address);
46 if (r < 0) {
47 log_link_error_errno(link, r, "Could not allocate address: %m");
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_remove(address, link, &link_address_remove_handler);
57
58 r = route_new(&route);
59 if (r < 0) {
60 log_link_error_errno(link, r, "Could not allocate route: %m");
61 return r;
62 }
63
64 route->family = AF_INET;
65 route->scope = RT_SCOPE_LINK;
66 route->priority = IPV4LL_ROUTE_METRIC;
67
68 route_remove(route, link, &link_route_remove_handler);
69
70 link_check_ready(link);
71
72 return 0;
73 }
74
75 static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
76 _cleanup_link_unref_ Link *link = userdata;
77 int r;
78
79 assert(link);
80 assert(!link->ipv4ll_route);
81
82 r = sd_netlink_message_get_errno(m);
83 if (r < 0 && r != -EEXIST) {
84 log_link_error_errno(link, r, "could not set ipv4ll route: %m");
85 link_enter_failed(link);
86 }
87
88 link->ipv4ll_route = true;
89
90 if (link->ipv4ll_address == true)
91 link_check_ready(link);
92
93 return 1;
94 }
95
96 static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
97 _cleanup_link_unref_ Link *link = userdata;
98 int r;
99
100 assert(link);
101 assert(!link->ipv4ll_address);
102
103 r = sd_netlink_message_get_errno(m);
104 if (r < 0 && r != -EEXIST) {
105 log_link_error_errno(link, r, "could not set ipv4ll address: %m");
106 link_enter_failed(link);
107 } else if (r >= 0)
108 manager_rtnl_process_address(rtnl, m, link->manager);
109
110 link->ipv4ll_address = true;
111
112 if (link->ipv4ll_route == true)
113 link_check_ready(link);
114
115 return 1;
116 }
117
118 static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
119 _cleanup_address_free_ Address *ll_addr = NULL;
120 _cleanup_route_free_ Route *route = NULL;
121 struct in_addr address;
122 int r;
123
124 assert(ll);
125 assert(link);
126
127 r = sd_ipv4ll_get_address(ll, &address);
128 if (r == -ENOENT)
129 return 0;
130 else if (r < 0)
131 return r;
132
133 log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
134 ADDRESS_FMT_VAL(address));
135
136 r = address_new(&ll_addr);
137 if (r < 0)
138 return r;
139
140 ll_addr->family = AF_INET;
141 ll_addr->in_addr.in = address;
142 ll_addr->prefixlen = 16;
143 ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htonl(0xfffffffflu >> ll_addr->prefixlen);
144 ll_addr->scope = RT_SCOPE_LINK;
145
146 r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
147 if (r < 0)
148 return r;
149
150 link->ipv4ll_address = false;
151
152 r = route_new(&route);
153 if (r < 0)
154 return r;
155
156 route->family = AF_INET;
157 route->scope = RT_SCOPE_LINK;
158 route->protocol = RTPROT_STATIC;
159 route->priority = IPV4LL_ROUTE_METRIC;
160
161 r = route_configure(route, link, ipv4ll_route_handler);
162 if (r < 0)
163 return r;
164
165 link->ipv4ll_route = false;
166
167 return 0;
168 }
169
170 static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
171 Link *link = userdata;
172 int r;
173
174 assert(link);
175 assert(link->network);
176 assert(link->manager);
177
178 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
179 return;
180
181 switch(event) {
182 case SD_IPV4LL_EVENT_STOP:
183 case SD_IPV4LL_EVENT_CONFLICT:
184 r = ipv4ll_address_lost(link);
185 if (r < 0) {
186 link_enter_failed(link);
187 return;
188 }
189 break;
190 case SD_IPV4LL_EVENT_BIND:
191 r = ipv4ll_address_claimed(ll, link);
192 if (r < 0) {
193 link_enter_failed(link);
194 return;
195 }
196 break;
197 default:
198 log_link_warning(link, "IPv4 link-local unknown event: %d", event);
199 break;
200 }
201 }
202
203 int ipv4ll_configure(Link *link) {
204 uint64_t seed;
205 int r;
206
207 assert(link);
208 assert(link->network);
209 assert(link->network->link_local & ADDRESS_FAMILY_IPV4);
210
211 if (!link->ipv4ll) {
212 r = sd_ipv4ll_new(&link->ipv4ll);
213 if (r < 0)
214 return r;
215 }
216
217 if (link->udev_device) {
218 r = net_get_unique_predictable_data(link->udev_device, &seed);
219 if (r >= 0) {
220 assert_cc(sizeof(unsigned) <= 8);
221
222 r = sd_ipv4ll_set_address_seed(link->ipv4ll, (unsigned)seed);
223 if (r < 0)
224 return r;
225 }
226 }
227
228 r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
229 if (r < 0)
230 return r;
231
232 r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
233 if (r < 0)
234 return r;
235
236 r = sd_ipv4ll_set_index(link->ipv4ll, link->ifindex);
237 if (r < 0)
238 return r;
239
240 r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
241 if (r < 0)
242 return r;
243
244 return 0;
245 }