Coverage Report

Created: 2019-06-19 13:33

/src/systemd/src/network/netdev/fou-tunnel.c
Line
Count
Source (jump to first uncovered line)
1
/* SPDX-License-Identifier: LGPL-2.1+ */
2
3
#include <linux/fou.h>
4
#include <net/if.h>
5
#include <netinet/in.h>
6
#include <linux/ip.h>
7
8
#include "conf-parser.h"
9
#include "ip-protocol-list.h"
10
#include "missing.h"
11
#include "netdev/fou-tunnel.h"
12
#include "netlink-util.h"
13
#include "networkd-link.h"
14
#include "networkd-manager.h"
15
#include "parse-util.h"
16
#include "sd-netlink.h"
17
#include "string-table.h"
18
#include "string-util.h"
19
#include "util.h"
20
21
static const char* const fou_encap_type_table[_NETDEV_FOO_OVER_UDP_ENCAP_MAX] = {
22
        [NETDEV_FOO_OVER_UDP_ENCAP_DIRECT] = "FooOverUDP",
23
        [NETDEV_FOO_OVER_UDP_ENCAP_GUE] = "GenericUDPEncapsulation",
24
};
25
26
DEFINE_STRING_TABLE_LOOKUP(fou_encap_type, FooOverUDPEncapType);
27
DEFINE_CONFIG_PARSE_ENUM(config_parse_fou_encap_type, fou_encap_type, FooOverUDPEncapType,
28
                         "Failed to parse Encapsulation=");
29
30
0
static int netdev_fill_fou_tunnel_message(NetDev *netdev, sd_netlink_message **ret) {
31
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
32
0
        FouTunnel *t;
33
0
        uint8_t encap_type;
34
0
        int r;
35
0
36
0
        assert(netdev);
37
0
38
0
        t = FOU(netdev);
39
0
40
0
        assert(t);
41
0
42
0
        r = sd_genl_message_new(netdev->manager->genl, SD_GENL_FOU, FOU_CMD_ADD, &m);
43
0
        if (r < 0)
44
0
                return log_netdev_error_errno(netdev, r, "Failed to allocate generic netlink message: %m");
45
0
46
0
        r = sd_netlink_message_append_u16(m, FOU_ATTR_PORT, htobe16(t->port));
47
0
        if (r < 0)
48
0
                return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PORT attribute: %m");
49
0
50
0
        if (IN_SET(t->peer_family, AF_INET, AF_INET6)) {
51
0
                r = sd_netlink_message_append_u16(m, FOU_ATTR_PEER_PORT, htobe16(t->peer_port));
52
0
                if (r < 0)
53
0
                        return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_PORT attribute: %m");
54
0
        }
55
0
56
0
        switch (t->fou_encap_type) {
57
0
        case NETDEV_FOO_OVER_UDP_ENCAP_DIRECT:
58
0
                encap_type = FOU_ENCAP_DIRECT;
59
0
                break;
60
0
        case NETDEV_FOO_OVER_UDP_ENCAP_GUE:
61
0
                encap_type = FOU_ENCAP_GUE;
62
0
                break;
63
0
        default:
64
0
                assert_not_reached("invalid encap type");
65
0
        }
66
0
67
0
        r = sd_netlink_message_append_u8(m, FOU_ATTR_TYPE, encap_type);
68
0
        if (r < 0)
69
0
                return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_TYPE attribute: %m");
70
0
71
0
        r = sd_netlink_message_append_u8(m, FOU_ATTR_AF, AF_INET);
72
0
        if (r < 0)
73
0
                return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_AF attribute: %m");
74
0
75
0
        r = sd_netlink_message_append_u8(m, FOU_ATTR_IPPROTO, t->fou_protocol);
76
0
        if (r < 0)
77
0
                return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_IPPROTO attribute: %m");
78
0
79
0
        if (t->local_family == AF_INET) {
80
0
                r = sd_netlink_message_append_in_addr(m, FOU_ATTR_LOCAL_V4, &t->local.in);
81
0
                if (r < 0)
82
0
                        return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V4 attribute: %m");
83
0
        } else if (t->local_family == AF_INET6) {
84
0
                r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_LOCAL_V6, &t->local.in6);
85
0
                if (r < 0)
86
0
                        return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_LOCAL_V6 attribute: %m");
87
0
        }
88
0
89
0
        if (t->peer_family == AF_INET) {
90
0
                r = sd_netlink_message_append_in_addr(m, FOU_ATTR_PEER_V4, &t->peer.in);
91
0
                if (r < 0)
92
0
                        return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V4 attribute: %m");
93
0
        } else if (t->peer_family == AF_INET6){
94
0
                r = sd_netlink_message_append_in6_addr(m, FOU_ATTR_PEER_V6, &t->peer.in6);
95
0
                if (r < 0)
96
0
                        return log_netdev_error_errno(netdev, r, "Could not append FOU_ATTR_PEER_V6 attribute: %m");
97
0
        }
98
0
99
0
        *ret = TAKE_PTR(m);
100
0
        return 0;
101
0
}
102
103
0
static int fou_tunnel_create_handler(sd_netlink *rtnl, sd_netlink_message *m, NetDev *netdev) {
104
0
        int r;
105
0
106
0
        assert(netdev);
107
0
        assert(netdev->state != _NETDEV_STATE_INVALID);
108
0
109
0
        r = sd_netlink_message_get_errno(m);
110
0
        if (r == -EEXIST)
111
0
                log_netdev_info(netdev, "netdev exists, using existing without changing its parameters");
112
0
        else if (r < 0) {
113
0
                log_netdev_warning_errno(netdev, r, "netdev could not be created: %m");
114
0
                netdev_drop(netdev);
115
0
116
0
                return 1;
117
0
        }
118
0
119
0
        log_netdev_debug(netdev, "FooOverUDP tunnel is created");
120
0
        return 1;
121
0
}
122
123
0
static int netdev_fou_tunnel_create(NetDev *netdev) {
124
0
        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
125
0
        int r;
126
0
127
0
        assert(netdev);
128
0
        assert(FOU(netdev));
129
0
130
0
        r = netdev_fill_fou_tunnel_message(netdev, &m);
131
0
        if (r < 0)
132
0
                return r;
133
0
134
0
        r = netlink_call_async(netdev->manager->genl, NULL, m, fou_tunnel_create_handler,
135
0
                               netdev_destroy_callback, netdev);
136
0
        if (r < 0)
137
0
                return log_netdev_error_errno(netdev, r, "Failed to create FooOverUDP tunnel: %m");
138
0
139
0
        netdev_ref(netdev);
140
0
        return 0;
141
0
}
142
143
int config_parse_ip_protocol(
144
                const char *unit,
145
                const char *filename,
146
                unsigned line,
147
                const char *section,
148
                unsigned section_line,
149
                const char *lvalue,
150
                int ltype,
151
                const char *rvalue,
152
                void *data,
153
0
                void *userdata) {
154
0
155
0
        uint8_t *protocol = data;
156
0
        int r;
157
0
158
0
        assert(filename);
159
0
        assert(section);
160
0
        assert(lvalue);
161
0
        assert(rvalue);
162
0
        assert(data);
163
0
164
0
        assert_cc(IPPROTO_MAX-1 <= UINT8_MAX);
165
0
166
0
        r = parse_ip_protocol(rvalue);
167
0
        if (r < 0) {
168
0
                r = safe_atou8(rvalue, protocol);
169
0
                if (r < 0)
170
0
                        log_syntax(unit, LOG_ERR, filename, line, r,
171
0
                                   "Failed to parse IP protocol '%s' for Foo over UDP tunnel, "
172
0
                                   "ignoring assignment: %m", rvalue);
173
0
                return 0;
174
0
        }
175
0
176
0
        *protocol = r;
177
0
        return 0;
178
0
}
179
180
int config_parse_fou_tunnel_address(
181
                const char *unit,
182
                const char *filename,
183
                unsigned line,
184
                const char *section,
185
                unsigned section_line,
186
                const char *lvalue,
187
                int ltype,
188
                const char *rvalue,
189
                void *data,
190
0
                void *userdata) {
191
0
192
0
        union in_addr_union *addr = data;
193
0
        FouTunnel *t = userdata;
194
0
        int r, *f;
195
0
196
0
        assert(filename);
197
0
        assert(lvalue);
198
0
        assert(rvalue);
199
0
        assert(data);
200
0
201
0
        if (streq(lvalue, "Local"))
202
0
                f = &t->local_family;
203
0
        else
204
0
                f = &t->peer_family;
205
0
206
0
        r = in_addr_from_string_auto(rvalue, f, addr);
207
0
        if (r < 0)
208
0
                log_syntax(unit, LOG_ERR, filename, line, r,
209
0
                           "Foo over UDP tunnel '%s' address is invalid, ignoring assignment: %s",
210
0
                           lvalue, rvalue);
211
0
212
0
        return 0;
213
0
}
214
215
1
static int netdev_fou_tunnel_verify(NetDev *netdev, const char *filename) {
216
1
        FouTunnel *t;
217
1
218
1
        assert(netdev);
219
1
        assert(filename);
220
1
221
1
        t = FOU(netdev);
222
1
223
1
        assert(t);
224
1
225
1
        switch (t->fou_encap_type) {
226
1
        case NETDEV_FOO_OVER_UDP_ENCAP_DIRECT:
227
1
                if (t->fou_protocol <= 0)
228
1
                        return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
229
0
                                                      "FooOverUDP protocol not configured in %s. Rejecting configuration.",
230
0
                                                      filename);
231
0
                break;
232
0
        case NETDEV_FOO_OVER_UDP_ENCAP_GUE:
233
0
                if (t->fou_protocol > 0)
234
0
                        return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
235
0
                                                      "FooOverUDP GUE can't be set with protocol configured in %s. Rejecting configuration.",
236
0
                                                      filename);
237
0
                break;
238
0
        default:
239
0
                assert_not_reached("Invalid fou encap type");
240
1
        }
241
1
242
1
        if (t->peer_family == AF_UNSPEC && t->peer_port > 0)
243
0
                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
244
0
                                              "FooOverUDP peer port is set but peer address not configured in %s. Rejecting configuration.",
245
0
                                              filename);
246
0
        else if (t->peer_family != AF_UNSPEC && t->peer_port == 0)
247
0
                return log_netdev_error_errno(netdev, SYNTHETIC_ERRNO(EINVAL),
248
0
                                              "FooOverUDP peer port not set but peer address is configured in %s. Rejecting configuration.",
249
0
                                              filename);
250
0
        return 0;
251
0
}
252
253
1
static void fou_tunnel_init(NetDev *netdev) {
254
1
        FouTunnel *t;
255
1
256
1
        assert(netdev);
257
1
258
1
        t = FOU(netdev);
259
1
260
1
        assert(t);
261
1
262
1
        t->fou_encap_type = NETDEV_FOO_OVER_UDP_ENCAP_DIRECT;
263
1
}
264
265
const NetDevVTable foutnl_vtable = {
266
        .object_size = sizeof(FouTunnel),
267
        .init = fou_tunnel_init,
268
        .sections = "Match\0NetDev\0FooOverUDP\0",
269
        .create = netdev_fou_tunnel_create,
270
        .create_type = NETDEV_CREATE_INDEPENDENT,
271
        .config_verify = netdev_fou_tunnel_verify,
272
};