Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/core/loopback-setup.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1+ */
2
3
#include <net/if.h>
4
#include <stdlib.h>
5
6
#include "sd-netlink.h"
7
8
#include "loopback-setup.h"
9
#include "missing.h"
10
#include "netlink-util.h"
11
#include "time-util.h"
12
13
0
#define LOOPBACK_SETUP_TIMEOUT_USEC (5 * USEC_PER_SEC)
14
15
struct state {
16
        unsigned n_messages;
17
        int rcode;
18
        const char *error_message;
19
        const char *success_message;
20
};
21
22
0
static int generic_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
23
0
        struct state *s = userdata;
24
0
        int r;
25
0
26
0
        assert(s);
27
0
        assert(s->n_messages > 0);
28
0
        s->n_messages--;
29
0
30
0
        errno = 0;
31
0
32
0
        r = sd_netlink_message_get_errno(m);
33
0
        if (r < 0)
34
0
                log_debug_errno(r, "%s: %m", s->error_message);
35
0
        else
36
0
                log_debug("%s", s->success_message);
37
0
38
0
        s->rcode = r;
39
0
        return 0;
40
0
}
41
42
0
static int start_loopback(sd_netlink *rtnl, struct state *s) {
43
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
44
0
        int r;
45
0
46
0
        assert(rtnl);
47
0
        assert(s);
48
0
49
0
        r = sd_rtnl_message_new_link(rtnl, &req, RTM_SETLINK, LOOPBACK_IFINDEX);
50
0
        if (r < 0)
51
0
                return r;
52
0
53
0
        r = sd_rtnl_message_link_set_flags(req, IFF_UP, IFF_UP);
54
0
        if (r < 0)
55
0
                return r;
56
0
57
0
        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, LOOPBACK_SETUP_TIMEOUT_USEC, "systemd-start-loopback");
58
0
        if (r < 0)
59
0
                return r;
60
0
61
0
        s->n_messages ++;
62
0
        return 0;
63
0
}
64
65
0
static int add_ipv4_address(sd_netlink *rtnl, struct state *s) {
66
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
67
0
        int r;
68
0
69
0
        assert(rtnl);
70
0
        assert(s);
71
0
72
0
        r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET);
73
0
        if (r < 0)
74
0
                return r;
75
0
76
0
        r = sd_rtnl_message_addr_set_prefixlen(req, 8);
77
0
        if (r < 0)
78
0
                return r;
79
0
80
0
        r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
81
0
        if (r < 0)
82
0
                return r;
83
0
84
0
        r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
85
0
        if (r < 0)
86
0
                return r;
87
0
88
0
        r = sd_netlink_message_append_in_addr(req, IFA_LOCAL, &(struct in_addr) { .s_addr = htobe32(INADDR_LOOPBACK) } );
89
0
        if (r < 0)
90
0
                return r;
91
0
92
0
        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv4");
93
0
        if (r < 0)
94
0
                return r;
95
0
96
0
        s->n_messages ++;
97
0
        return 0;
98
0
}
99
100
0
static int add_ipv6_address(sd_netlink *rtnl, struct state *s) {
101
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
102
0
        int r;
103
0
104
0
        assert(rtnl);
105
0
        assert(s);
106
0
107
0
        r = sd_rtnl_message_new_addr(rtnl, &req, RTM_NEWADDR, LOOPBACK_IFINDEX, AF_INET6);
108
0
        if (r < 0)
109
0
                return r;
110
0
111
0
        r = sd_rtnl_message_addr_set_prefixlen(req, 128);
112
0
        if (r < 0)
113
0
                return r;
114
0
115
0
        r = sd_rtnl_message_addr_set_flags(req, IFA_F_PERMANENT);
116
0
        if (r < 0)
117
0
                return r;
118
0
119
0
        r = sd_rtnl_message_addr_set_scope(req, RT_SCOPE_HOST);
120
0
        if (r < 0)
121
0
                return r;
122
0
123
0
        r = sd_netlink_message_append_in6_addr(req, IFA_LOCAL, &in6addr_loopback);
124
0
        if (r < 0)
125
0
                return r;
126
0
127
0
        r = sd_netlink_call_async(rtnl, NULL, req, generic_handler, NULL, s, USEC_INFINITY, "systemd-loopback-ipv6");
128
0
        if (r < 0)
129
0
                return r;
130
0
131
0
        s->n_messages ++;
132
0
        return 0;
133
0
}
134
135
0
static bool check_loopback(sd_netlink *rtnl) {
136
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
137
0
        unsigned flags;
138
0
        int r;
139
0
140
0
        r = sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, LOOPBACK_IFINDEX);
141
0
        if (r < 0)
142
0
                return false;
143
0
144
0
        r = sd_netlink_call(rtnl, req, USEC_INFINITY, &reply);
145
0
        if (r < 0)
146
0
                return false;
147
0
148
0
        r = sd_rtnl_message_link_get_flags(reply, &flags);
149
0
        if (r < 0)
150
0
                return false;
151
0
152
0
        return flags & IFF_UP;
153
0
}
154
155
0
int loopback_setup(void) {
156
0
        _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
157
0
        struct state state_4 = {
158
0
                .error_message = "Failed to add address 127.0.0.1 to loopback interface",
159
0
                .success_message = "Successfully added address 127.0.0.1 to loopback interface",
160
0
        }, state_6 = {
161
0
                .error_message = "Failed to add address ::1 to loopback interface",
162
0
                .success_message = "Successfully added address ::1 to loopback interface",
163
0
        }, state_up = {
164
0
                .error_message = "Failed to bring loopback interface up",
165
0
                .success_message = "Successfully brought loopback interface up",
166
0
        };
167
0
        int r;
168
0
169
0
        r = sd_netlink_open(&rtnl);
170
0
        if (r < 0)
171
0
                return log_error_errno(r, "Failed to open netlink: %m");
172
0
173
0
        /* Note that we add the IP addresses here explicitly even though the kernel does that too implicitly when
174
0
         * setting up the loopback device. The reason we do this here a second time (and possibly race against the
175
0
         * kernel) is that we want to synchronously wait until the IP addresses are set up correctly, see
176
0
         *
177
0
         * https://github.com/systemd/systemd/issues/5641 */
178
0
179
0
        r = add_ipv4_address(rtnl, &state_4);
180
0
        if (r < 0)
181
0
                return log_error_errno(r, "Failed to enqueue IPv4 loopback address add request: %m");
182
0
183
0
        r = add_ipv6_address(rtnl, &state_6);
184
0
        if (r < 0)
185
0
                return log_error_errno(r, "Failed to enqueue IPv6 loopback address add request: %m");
186
0
187
0
        r = start_loopback(rtnl, &state_up);
188
0
        if (r < 0)
189
0
                return log_error_errno(r, "Failed to enqueue loopback interface start request: %m");
190
0
191
0
        while (state_4.n_messages + state_6.n_messages + state_up.n_messages > 0) {
192
0
                r = sd_netlink_wait(rtnl, LOOPBACK_SETUP_TIMEOUT_USEC);
193
0
                if (r < 0)
194
0
                        return log_error_errno(r, "Failed to wait for netlink event: %m");
195
0
196
0
                r = sd_netlink_process(rtnl, NULL);
197
0
                if (r < 0)
198
0
                        return log_warning_errno(r, "Failed to process netlink event: %m");
199
0
        }
200
0
201
0
        /* Note that we don't really care whether the addresses could be added or not */
202
0
        if (state_up.rcode != 0) {
203
0
                /* If we lack the permissions to configure the loopback device,
204
0
                 * but we find it to be already configured, let's exit cleanly,
205
0
                 * in order to supported unprivileged containers. */
206
0
                if (state_up.rcode == -EPERM && check_loopback(rtnl))
207
0
                        return 0;
208
0
209
0
                return log_warning_errno(state_up.rcode, "Failed to configure loopback device: %m");
210
0
        }
211
0
212
0
        return 0;
213
0
}