]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-ipv4ll.c
Merge pull request #106 from teg/man-mac-random
[thirdparty/systemd.git] / src / network / networkd-ipv4ll.c
CommitLineData
b22d8a00
TG
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
28static 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
79008bdd 43 log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
b22d8a00
TG
44
45 r = address_new_dynamic(&address);
46 if (r < 0) {
79008bdd 47 log_link_error(link, "Could not allocate address: %s", strerror(-r));
b22d8a00
TG
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) {
79008bdd 60 log_link_error(link, "Could not allocate route: %s",
b22d8a00
TG
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
76static 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) {
79008bdd 85 log_link_error(link, "could not set ipv4ll route: %s", strerror(-r));
b22d8a00
TG
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
97static 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) {
79008bdd 106 log_link_error(link, "could not set ipv4ll address: %s", strerror(-r));
b22d8a00 107 link_enter_failed(link);
45af44d4
TG
108 } else if (r >= 0)
109 link_rtnl_process_address(rtnl, m, link->manager);
b22d8a00
TG
110
111 link->ipv4ll_address = true;
112
113 if (link->ipv4ll_route == true)
114 link_client_handler(link);
115
116 return 1;
117}
118
119static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
120 _cleanup_address_free_ Address *ll_addr = NULL;
121 _cleanup_route_free_ Route *route = NULL;
122 struct in_addr address;
123 int r;
124
125 assert(ll);
126 assert(link);
127
128 r = sd_ipv4ll_get_address(ll, &address);
129 if (r == -ENOENT)
130 return 0;
131 else if (r < 0)
132 return r;
133
79008bdd 134 log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
b22d8a00
TG
135 ADDRESS_FMT_VAL(address));
136
137 r = address_new_dynamic(&ll_addr);
138 if (r < 0)
139 return r;
140
141 ll_addr->family = AF_INET;
142 ll_addr->in_addr.in = address;
143 ll_addr->prefixlen = 16;
144 ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htonl(0xfffffffflu >> ll_addr->prefixlen);
145 ll_addr->scope = RT_SCOPE_LINK;
146
147 r = address_configure(ll_addr, link, ipv4ll_address_handler);
148 if (r < 0)
149 return r;
150
151 link->ipv4ll_address = false;
152
153 r = route_new_dynamic(&route, RTPROT_STATIC);
154 if (r < 0)
155 return r;
156
157 route->family = AF_INET;
158 route->scope = RT_SCOPE_LINK;
159 route->metrics = 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
170static 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 IPV4LL_EVENT_STOP:
183 case 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 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 if (event < 0)
79008bdd 199 log_link_warning(link, "IPv4 link-local error: %s", strerror(-event));
b22d8a00 200 else
79008bdd 201 log_link_warning(link, "IPv4 link-local unknown event: %d", event);
b22d8a00
TG
202 break;
203 }
204}
205
206int ipv4ll_configure(Link *link) {
207 uint8_t seed[8];
208 int r;
209
210 assert(link);
211 assert(link->network);
d0d6a4cd 212 assert(IN_SET(link->network->link_local, ADDRESS_FAMILY_IPV4, ADDRESS_FAMILY_YES));
b22d8a00
TG
213
214 r = sd_ipv4ll_new(&link->ipv4ll);
215 if (r < 0)
216 return r;
217
218 if (link->udev_device) {
219 r = net_get_unique_predictable_data(link->udev_device, seed);
220 if (r >= 0) {
221 r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
222 if (r < 0)
223 return r;
224 }
225 }
226
227 r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
228 if (r < 0)
229 return r;
230
231 r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
232 if (r < 0)
233 return r;
234
235 r = sd_ipv4ll_set_index(link->ipv4ll, link->ifindex);
236 if (r < 0)
237 return r;
238
239 r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
240 if (r < 0)
241 return r;
242
243 return 0;
244}