]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
af5bc85d LP |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2010 Lennart Poettering | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
5430f7f2 LP |
8 | under the terms of the GNU Lesser General Public License as published by |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
af5bc85d LP |
10 | (at your option) any later version. |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
5430f7f2 | 15 | Lesser General Public License for more details. |
af5bc85d | 16 | |
5430f7f2 | 17 | You should have received a copy of the GNU Lesser General Public License |
af5bc85d LP |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. |
19 | ***/ | |
20 | ||
af5bc85d | 21 | #include <net/if.h> |
af5bc85d | 22 | #include <stdlib.h> |
af5bc85d | 23 | |
1c4baffc | 24 | #include "sd-netlink.h" |
07630cea | 25 | |
9ca903cc | 26 | #include "loopback-setup.h" |
07630cea LP |
27 | #include "missing.h" |
28 | #include "netlink-util.h" | |
5a723174 | 29 | |
2d2a815c ZJS |
30 | #define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC) |
31 | ||
fb893927 LP |
32 | struct state { |
33 | unsigned n_messages; | |
34 | int rcode; | |
c23218ae | 35 | const char *title; |
fb893927 LP |
36 | }; |
37 | ||
c23218ae | 38 | static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) { |
fb893927 LP |
39 | struct state *s = userdata; |
40 | ||
41 | assert(s); | |
42 | assert(s->n_messages > 0); | |
43 | s->n_messages--; | |
44 | ||
45 | errno = 0; | |
c23218ae | 46 | log_debug_errno(sd_netlink_message_get_errno(m), "Failed to %s: %m", s->title); |
fb893927 LP |
47 | |
48 | s->rcode = sd_netlink_message_get_errno(m); | |
49 | ||
50 | return 0; | |
51 | } | |
52 | ||
53 | static int start_loopback(sd_netlink *rtnl, struct state *s) { | |
4afd3348 | 54 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; |
81eca919 | 55 | int r; |
af5bc85d | 56 | |
fb893927 LP |
57 | assert(rtnl); |
58 | assert(s); | |
59 | ||
f3fc4815 | 60 | r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX); |
fc25d7f8 TG |
61 | if (r < 0) |
62 | return r; | |
63 | ||
5d4795f3 | 64 | r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP); |
81eca919 TG |
65 | if (r < 0) |
66 | return r; | |
af5bc85d | 67 | |
c23218ae | 68 | r = sd_netlink_call_async(rtnl, req, generic_handler, s, LOOPBACK_SETUP_TIMEOUT_USEC, NULL); |
fb893927 LP |
69 | if (r < 0) |
70 | return r; | |
71 | ||
72 | s->n_messages ++; | |
73 | return 0; | |
74 | } | |
75 | ||
fb893927 LP |
76 | static int add_ipv4_address(sd_netlink *rtnl, struct state *s) { |
77 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
78 | int r; | |
79 | ||
80 | assert(rtnl); | |
81 | assert(s); | |
82 | ||
83 | r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET); | |
84 | if (r < 0) | |
85 | return r; | |
86 | ||
87 | r = sd_rtnl_message_addr_set_prefixlen(req, 8); | |
88 | if (r < 0) | |
89 | return r; | |
90 | ||
91 | r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); | |
92 | if (r < 0) | |
93 | return r; | |
94 | ||
95 | r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); | |
96 | if (r < 0) | |
97 | return r; | |
98 | ||
99 | r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } ); | |
100 | if (r < 0) | |
101 | return r; | |
102 | ||
103 | r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL); | |
104 | if (r < 0) | |
105 | return r; | |
106 | ||
107 | s->n_messages ++; | |
108 | return 0; | |
109 | } | |
110 | ||
111 | static int add_ipv6_address(sd_netlink *rtnl, struct state *s) { | |
112 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL; | |
113 | int r; | |
114 | ||
115 | assert(rtnl); | |
116 | assert(s); | |
117 | ||
118 | r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6); | |
119 | if (r < 0) | |
120 | return r; | |
121 | ||
122 | r = sd_rtnl_message_addr_set_prefixlen(req, 128); | |
123 | if (r < 0) | |
124 | return r; | |
125 | ||
126 | r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT); | |
127 | if (r < 0) | |
128 | return r; | |
129 | ||
130 | r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST); | |
131 | if (r < 0) | |
132 | return r; | |
133 | ||
134 | r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback); | |
81eca919 TG |
135 | if (r < 0) |
136 | return r; | |
af5bc85d | 137 | |
fb893927 LP |
138 | r = sd_netlink_call_async(rtnl, req, generic_handler, s, USEC_INFINITY, NULL); |
139 | if (r < 0) | |
140 | return r; | |
141 | ||
142 | s->n_messages ++; | |
af5bc85d LP |
143 | return 0; |
144 | } | |
145 | ||
1c4baffc | 146 | static bool check_loopback(sd_netlink *rtnl) { |
4afd3348 | 147 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL; |
e95e909d | 148 | unsigned flags; |
e62d8c39 | 149 | int r; |
e95e909d TG |
150 | |
151 | r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX); | |
152 | if (r < 0) | |
2f0af4e1 | 153 | return false; |
e95e909d | 154 | |
fb893927 | 155 | r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply); |
e95e909d | 156 | if (r < 0) |
2f0af4e1 | 157 | return false; |
e95e909d TG |
158 | |
159 | r = sd_rtnl_message_link_get_flags(reply, &flags); | |
160 | if (r < 0) | |
2f0af4e1 | 161 | return false; |
e95e909d TG |
162 | |
163 | return flags & IFF_UP; | |
2c3ff76e LP |
164 | } |
165 | ||
af5bc85d | 166 | int loopback_setup(void) { |
4afd3348 | 167 | _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL; |
c23218ae ZJS |
168 | struct state state_4 = { .title = "add address 127.0.0.1 to loopback interface" }, |
169 | state_6 = { .title = "add address ::1 to loopback interface"}, | |
170 | state_up = { .title = "bring loopback interface up" }; | |
f3fc4815 | 171 | int r; |
0a0dc69b | 172 | |
1c4baffc | 173 | r = sd_netlink_open(&rtnl); |
2c3ff76e | 174 | if (r < 0) |
fb893927 LP |
175 | return log_error_errno(r, "Failed to open netlink: %m"); |
176 | ||
177 | /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when | |
178 | * setting up the loopback device. The reason we do this here a second time (and possibly race against the | |
179 | * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see | |
180 | * | |
181 | * https://github.com/systemd/systemd/issues/5641 */ | |
182 | ||
c23218ae | 183 | r = add_ipv4_address(rtnl, &state_4); |
fb893927 | 184 | if (r < 0) |
3561eafa | 185 | return log_error_errno(r, "Failed to enqueue IPv4 loopback address add request: %m"); |
fb893927 | 186 | |
c23218ae | 187 | r = add_ipv6_address(rtnl, &state_6); |
fb893927 | 188 | if (r < 0) |
c23218ae | 189 | return log_error_errno(r, "Failed to enqueue IPv6 loopback address add request: %m"); |
fb893927 | 190 | |
c23218ae | 191 | r = start_loopback(rtnl, &state_up); |
fb893927 | 192 | if (r < 0) |
3561eafa | 193 | return log_error_errno(r, "Failed to enqueue loopback interface start request: %m"); |
fb893927 | 194 | |
c23218ae | 195 | while (state_4.n_messages + state_6.n_messages + state_up.n_messages > 0) { |
2d2a815c | 196 | r = sd_netlink_wait(rtnl, LOOPBACK_SETUP_TIMEOUT_USEC); |
fb893927 LP |
197 | if (r < 0) |
198 | return log_error_errno(r, "Failed to wait for netlink event: %m"); | |
199 | ||
200 | r = sd_netlink_process(rtnl, NULL); | |
201 | if (r < 0) | |
202 | return log_warning_errno(r, "Failed to process netlink event: %m"); | |
203 | } | |
af5bc85d | 204 | |
c23218ae ZJS |
205 | /* Note that we don't really care whether the addresses could be added or not */ |
206 | if (state_up.rcode != 0) { | |
207 | /* If we lack the permissions to configure the loopback device, | |
208 | * but we find it to be already configured, let's exit cleanly, | |
209 | * in order to supported unprivileged containers. */ | |
210 | if (state_up.rcode == -EPERM && check_loopback(rtnl)) | |
8f084002 | 211 | return 0; |
af5bc85d | 212 | |
c23218ae | 213 | return log_warning_errno(state_up.rcode, "Failed to configure loopback device: %m"); |
8f084002 | 214 | } |
2c3ff76e | 215 | |
e62d8c39 | 216 | return 0; |
af5bc85d | 217 | } |