]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/netdev/tuntap.c
tree-wide: use -EBADF for fd initialization
[thirdparty/systemd.git] / src / network / netdev / tuntap.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include <errno.h>
4 #include <fcntl.h>
5 #include <net/if.h>
6 #include <netinet/if_ether.h>
7 #include <sys/ioctl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <linux/if_tun.h>
11
12 #include "alloc-util.h"
13 #include "daemon-util.h"
14 #include "fd-util.h"
15 #include "networkd-link.h"
16 #include "networkd-manager.h"
17 #include "socket-util.h"
18 #include "tuntap.h"
19 #include "user-util.h"
20
21 #define TUN_DEV "/dev/net/tun"
22
23 static TunTap* TUNTAP(NetDev *netdev) {
24 assert(netdev);
25
26 switch (netdev->kind) {
27 case NETDEV_KIND_TAP:
28 return TAP(netdev);
29 case NETDEV_KIND_TUN:
30 return TUN(netdev);
31 default:
32 return NULL;
33 }
34 }
35
36 static void *close_fd_ptr(void *p) {
37 safe_close(PTR_TO_FD(p));
38 return NULL;
39 }
40
41 DEFINE_PRIVATE_HASH_OPS_FULL(named_fd_hash_ops, char, string_hash_func, string_compare_func, free, void, close_fd_ptr);
42
43 int manager_add_tuntap_fd(Manager *m, int fd, const char *name) {
44 _cleanup_free_ char *tuntap_name = NULL;
45 const char *p;
46 int r;
47
48 assert(m);
49 assert(fd >= 0);
50 assert(name);
51
52 p = startswith(name, "tuntap-");
53 if (!p)
54 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received unknown fd (%s).", name);
55
56 if (!ifname_valid(p))
57 return log_debug_errno(SYNTHETIC_ERRNO(EINVAL), "Received tuntap fd with invalid name (%s).", p);
58
59 tuntap_name = strdup(p);
60 if (!tuntap_name)
61 return log_oom_debug();
62
63 r = hashmap_ensure_put(&m->tuntap_fds_by_name, &named_fd_hash_ops, tuntap_name, FD_TO_PTR(fd));
64 if (r < 0)
65 return log_debug_errno(r, "Failed to store tuntap fd: %m");
66
67 TAKE_PTR(tuntap_name);
68 return 0;
69 }
70
71 void manager_clear_unmanaged_tuntap_fds(Manager *m) {
72 char *name;
73 void *p;
74
75 assert(m);
76
77 while ((p = hashmap_steal_first_key_and_value(m->tuntap_fds_by_name, (void**) &name))) {
78 close_and_notify_warn(PTR_TO_FD(p), name);
79 name = mfree(name);
80 }
81 }
82
83 static int tuntap_take_fd(NetDev *netdev) {
84 _cleanup_free_ char *name = NULL;
85 void *p;
86 int r;
87
88 assert(netdev);
89 assert(netdev->manager);
90
91 r = link_get_by_name(netdev->manager, netdev->ifname, NULL);
92 if (r < 0)
93 return r;
94
95 p = hashmap_remove2(netdev->manager->tuntap_fds_by_name, netdev->ifname, (void**) &name);
96 if (!p)
97 return -ENOENT;
98
99 log_netdev_debug(netdev, "Found file descriptor in fd store.");
100 return PTR_TO_FD(p);
101 }
102
103 static int netdev_create_tuntap(NetDev *netdev) {
104 _cleanup_close_ int fd = -EBADF;
105 struct ifreq ifr = {};
106 TunTap *t;
107 int r;
108
109 assert(netdev);
110 t = TUNTAP(netdev);
111 assert(t);
112
113 fd = TAKE_FD(t->fd);
114 if (fd < 0)
115 fd = tuntap_take_fd(netdev);
116 if (fd < 0)
117 fd = open(TUN_DEV, O_RDWR|O_CLOEXEC);
118 if (fd < 0)
119 return log_netdev_error_errno(netdev, errno, "Failed to open " TUN_DEV ": %m");
120
121 if (netdev->kind == NETDEV_KIND_TAP)
122 ifr.ifr_flags |= IFF_TAP;
123 else
124 ifr.ifr_flags |= IFF_TUN;
125
126 if (!t->packet_info)
127 ifr.ifr_flags |= IFF_NO_PI;
128
129 if (t->multi_queue)
130 ifr.ifr_flags |= IFF_MULTI_QUEUE;
131
132 if (t->vnet_hdr)
133 ifr.ifr_flags |= IFF_VNET_HDR;
134
135 strncpy(ifr.ifr_name, netdev->ifname, IFNAMSIZ-1);
136
137 if (ioctl(fd, TUNSETIFF, &ifr) < 0)
138 return log_netdev_error_errno(netdev, errno, "TUNSETIFF failed: %m");
139
140 if (t->user_name) {
141 const char *user = t->user_name;
142 uid_t uid;
143
144 r = get_user_creds(&user, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
145 if (r < 0)
146 return log_netdev_error_errno(netdev, r, "Cannot resolve user name %s: %m", t->user_name);
147
148 if (ioctl(fd, TUNSETOWNER, uid) < 0)
149 return log_netdev_error_errno(netdev, errno, "TUNSETOWNER failed: %m");
150 }
151
152 if (t->group_name) {
153 const char *group = t->group_name;
154 gid_t gid;
155
156 r = get_group_creds(&group, &gid, USER_CREDS_ALLOW_MISSING);
157 if (r < 0)
158 return log_netdev_error_errno(netdev, r, "Cannot resolve group name %s: %m", t->group_name);
159
160 if (ioctl(fd, TUNSETGROUP, gid) < 0)
161 return log_netdev_error_errno(netdev, errno, "TUNSETGROUP failed: %m");
162
163 }
164
165 if (ioctl(fd, TUNSETPERSIST, 1) < 0)
166 return log_netdev_error_errno(netdev, errno, "TUNSETPERSIST failed: %m");
167
168 if (t->keep_fd) {
169 t->fd = TAKE_FD(fd);
170 (void) notify_push_fdf(t->fd, "tuntap-%s", netdev->ifname);
171 }
172
173 return 0;
174 }
175
176 static void tuntap_init(NetDev *netdev) {
177 TunTap *t;
178
179 assert(netdev);
180 t = TUNTAP(netdev);
181 assert(t);
182
183 t->fd = -EBADF;
184 }
185
186 static void tuntap_drop(NetDev *netdev) {
187 TunTap *t;
188
189 assert(netdev);
190 t = TUNTAP(netdev);
191 assert(t);
192
193 t->fd = close_and_notify_warn(t->fd, netdev->ifname);
194 }
195
196 static void tuntap_done(NetDev *netdev) {
197 TunTap *t;
198
199 assert(netdev);
200 t = TUNTAP(netdev);
201 assert(t);
202
203 t->fd = safe_close(t->fd);
204 t->user_name = mfree(t->user_name);
205 t->group_name = mfree(t->group_name);
206 }
207
208 static int tuntap_verify(NetDev *netdev, const char *filename) {
209 assert(netdev);
210
211 if (netdev->mtu != 0)
212 log_netdev_warning(netdev,
213 "MTUBytes= configured for %s device in %s will be ignored.\n"
214 "Please set it in the corresponding .network file.",
215 netdev_kind_to_string(netdev->kind), filename);
216
217 if (netdev->hw_addr.length > 0)
218 log_netdev_warning(netdev,
219 "MACAddress= configured for %s device in %s will be ignored.\n"
220 "Please set it in the corresponding .network file.",
221 netdev_kind_to_string(netdev->kind), filename);
222
223 return 0;
224 }
225
226 const NetDevVTable tun_vtable = {
227 .object_size = sizeof(TunTap),
228 .sections = NETDEV_COMMON_SECTIONS "Tun\0",
229 .config_verify = tuntap_verify,
230 .init = tuntap_init,
231 .drop = tuntap_drop,
232 .done = tuntap_done,
233 .create = netdev_create_tuntap,
234 .create_type = NETDEV_CREATE_INDEPENDENT,
235 .iftype = ARPHRD_NONE,
236 };
237
238 const NetDevVTable tap_vtable = {
239 .object_size = sizeof(TunTap),
240 .sections = NETDEV_COMMON_SECTIONS "Tap\0",
241 .config_verify = tuntap_verify,
242 .init = tuntap_init,
243 .drop = tuntap_drop,
244 .done = tuntap_done,
245 .create = netdev_create_tuntap,
246 .create_type = NETDEV_CREATE_INDEPENDENT,
247 .iftype = ARPHRD_ETHER,
248 };