1 /* 2 * passt network backend 3 * 4 * Copyright Red Hat 5 * 6 * SPDX-License-Identifier: GPL-2.0-or-later 7 */ 8 #include "qemu/osdep.h" 9 #include <glib/gstdio.h> 10 #include <gio/gio.h> 11 #include "net/net.h" 12 #include "clients.h" 13 #include "qapi/error.h" 14 #include "io/net-listener.h" 15 #include "stream_data.h" 16 17 typedef struct NetPasstState { 18 NetStreamData data; 19 GPtrArray *args; 20 gchar *pidfile; 21 pid_t pid; 22 } NetPasstState; 23 24 static int net_passt_stream_start(NetPasstState *s, Error **errp); 25 26 static void net_passt_cleanup(NetClientState *nc) 27 { 28 NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); 29 30 kill(s->pid, SIGTERM); 31 g_remove(s->pidfile); 32 g_free(s->pidfile); 33 g_ptr_array_free(s->args, TRUE); 34 } 35 36 static ssize_t net_passt_receive(NetClientState *nc, const uint8_t *buf, 37 size_t size) 38 { 39 NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); 40 41 return net_stream_data_receive(d, buf, size); 42 } 43 44 static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, 45 gpointer data) 46 { 47 if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { 48 NetPasstState *s = DO_UPCAST(NetPasstState, data, data); 49 Error *error; 50 51 /* we need to restart passt */ 52 kill(s->pid, SIGTERM); 53 if (net_passt_stream_start(s, &error) == -1) { 54 error_report_err(error); 55 } 56 57 return G_SOURCE_REMOVE; 58 } 59 60 return G_SOURCE_CONTINUE; 61 } 62 63 static NetClientInfo net_passt_info = { 64 .type = NET_CLIENT_DRIVER_PASST, 65 .size = sizeof(NetPasstState), 66 .receive = net_passt_receive, 67 .cleanup = net_passt_cleanup, 68 }; 69 70 static void net_passt_client_connected(QIOTask *task, gpointer opaque) 71 { 72 NetPasstState *s = opaque; 73 74 if (net_stream_data_client_connected(task, &s->data) == 0) { 75 qemu_set_info_str(&s->data.nc, "stream,connected to pid %d", s->pid); 76 } 77 } 78 79 static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp) 80 { 81 g_autoptr(GSubprocess) daemon = NULL; 82 g_autofree gchar *contents = NULL; 83 g_autoptr(GError) error = NULL; 84 GSubprocessLauncher *launcher; 85 86 qemu_set_info_str(&s->data.nc, "launching passt"); 87 88 launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE); 89 g_subprocess_launcher_take_fd(launcher, sock, 3); 90 91 daemon = g_subprocess_launcher_spawnv(launcher, 92 (const gchar *const *)s->args->pdata, 93 &error); 94 g_object_unref(launcher); 95 96 if (!daemon) { 97 error_setg(errp, "Error creating daemon: %s", error->message); 98 return -1; 99 } 100 101 if (!g_subprocess_wait(daemon, NULL, &error)) { 102 error_setg(errp, "Error waiting for daemon: %s", error->message); 103 return -1; 104 } 105 106 if (g_subprocess_get_if_exited(daemon) && 107 g_subprocess_get_exit_status(daemon)) { 108 return -1; 109 } 110 111 if (!g_file_get_contents(s->pidfile, &contents, NULL, &error)) { 112 error_setg(errp, "Cannot read passt pid: %s", error->message); 113 return -1; 114 } 115 116 s->pid = (pid_t)g_ascii_strtoll(contents, NULL, 10); 117 if (s->pid <= 0) { 118 error_setg(errp, "File '%s' did not contain a valid PID.", s->pidfile); 119 return -1; 120 } 121 122 return 0; 123 } 124 125 static int net_passt_stream_start(NetPasstState *s, Error **errp) 126 { 127 QIOChannelSocket *sioc; 128 SocketAddress *addr; 129 int sv[2]; 130 131 if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { 132 error_setg_errno(errp, errno, "socketpair() failed"); 133 return -1; 134 } 135 136 /* connect to passt */ 137 qemu_set_info_str(&s->data.nc, "connecting to passt"); 138 139 /* create socket channel */ 140 sioc = qio_channel_socket_new(); 141 s->data.ioc = QIO_CHANNEL(sioc); 142 s->data.nc.link_down = true; 143 s->data.send = net_passt_send; 144 145 addr = g_new0(SocketAddress, 1); 146 addr->type = SOCKET_ADDRESS_TYPE_FD; 147 addr->u.fd.str = g_strdup_printf("%d", sv[0]); 148 149 qio_channel_socket_connect_async(sioc, addr, 150 net_passt_client_connected, s, 151 NULL, NULL); 152 153 qapi_free_SocketAddress(addr); 154 155 /* start passt */ 156 if (net_passt_start_daemon(s, sv[1], errp) == -1) { 157 close(sv[0]); 158 close(sv[1]); 159 return -1; 160 } 161 close(sv[1]); 162 163 return 0; 164 } 165 166 static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, 167 gchar *pidfile, Error **errp) 168 { 169 GPtrArray *args = g_ptr_array_new_with_free_func(g_free); 170 171 if (passt->path) { 172 g_ptr_array_add(args, g_strdup(passt->path)); 173 } else { 174 g_ptr_array_add(args, g_strdup("passt")); 175 } 176 177 /* by default, be quiet */ 178 if (!passt->has_quiet || passt->quiet) { 179 g_ptr_array_add(args, g_strdup("--quiet")); 180 } 181 182 if (passt->has_mtu) { 183 g_ptr_array_add(args, g_strdup("--mtu")); 184 g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu)); 185 } 186 187 if (passt->address) { 188 g_ptr_array_add(args, g_strdup("--address")); 189 g_ptr_array_add(args, g_strdup(passt->address)); 190 } 191 192 if (passt->netmask) { 193 g_ptr_array_add(args, g_strdup("--netmask")); 194 g_ptr_array_add(args, g_strdup(passt->netmask)); 195 } 196 197 if (passt->mac) { 198 g_ptr_array_add(args, g_strdup("--mac-addr")); 199 g_ptr_array_add(args, g_strdup(passt->mac)); 200 } 201 202 if (passt->gateway) { 203 g_ptr_array_add(args, g_strdup("--gateway")); 204 g_ptr_array_add(args, g_strdup(passt->gateway)); 205 } 206 207 if (passt->interface) { 208 g_ptr_array_add(args, g_strdup("--interface")); 209 g_ptr_array_add(args, g_strdup(passt->interface)); 210 } 211 212 if (passt->outbound) { 213 g_ptr_array_add(args, g_strdup("--outbound")); 214 g_ptr_array_add(args, g_strdup(passt->outbound)); 215 } 216 217 if (passt->outbound_if4) { 218 g_ptr_array_add(args, g_strdup("--outbound-if4")); 219 g_ptr_array_add(args, g_strdup(passt->outbound_if4)); 220 } 221 222 if (passt->outbound_if6) { 223 g_ptr_array_add(args, g_strdup("--outbound-if6")); 224 g_ptr_array_add(args, g_strdup(passt->outbound_if6)); 225 } 226 227 if (passt->dns) { 228 g_ptr_array_add(args, g_strdup("--dns")); 229 g_ptr_array_add(args, g_strdup(passt->dns)); 230 } 231 if (passt->fqdn) { 232 g_ptr_array_add(args, g_strdup("--fqdn")); 233 g_ptr_array_add(args, g_strdup(passt->fqdn)); 234 } 235 236 if (passt->has_dhcp_dns && !passt->dhcp_dns) { 237 g_ptr_array_add(args, g_strdup("--no-dhcp-dns")); 238 } 239 240 if (passt->has_dhcp_search && !passt->dhcp_search) { 241 g_ptr_array_add(args, g_strdup("--no-dhcp-search")); 242 } 243 244 if (passt->map_host_loopback) { 245 g_ptr_array_add(args, g_strdup("--map-host-loopback")); 246 g_ptr_array_add(args, g_strdup(passt->map_host_loopback)); 247 } 248 249 if (passt->map_guest_addr) { 250 g_ptr_array_add(args, g_strdup("--map-guest-addr")); 251 g_ptr_array_add(args, g_strdup(passt->map_guest_addr)); 252 } 253 254 if (passt->dns_forward) { 255 g_ptr_array_add(args, g_strdup("--dns-forward")); 256 g_ptr_array_add(args, g_strdup(passt->dns_forward)); 257 } 258 259 if (passt->dns_host) { 260 g_ptr_array_add(args, g_strdup("--dns-host")); 261 g_ptr_array_add(args, g_strdup(passt->dns_host)); 262 } 263 264 if (passt->has_tcp && !passt->tcp) { 265 g_ptr_array_add(args, g_strdup("--no-tcp")); 266 } 267 268 if (passt->has_udp && !passt->udp) { 269 g_ptr_array_add(args, g_strdup("--no-udp")); 270 } 271 272 if (passt->has_icmp && !passt->icmp) { 273 g_ptr_array_add(args, g_strdup("--no-icmp")); 274 } 275 276 if (passt->has_dhcp && !passt->dhcp) { 277 g_ptr_array_add(args, g_strdup("--no-dhcp")); 278 } 279 280 if (passt->has_ndp && !passt->ndp) { 281 g_ptr_array_add(args, g_strdup("--no-ndp")); 282 } 283 if (passt->has_dhcpv6 && !passt->dhcpv6) { 284 g_ptr_array_add(args, g_strdup("--no-dhcpv6")); 285 } 286 287 if (passt->has_ra && !passt->ra) { 288 g_ptr_array_add(args, g_strdup("--no-ra")); 289 } 290 291 if (passt->has_freebind && passt->freebind) { 292 g_ptr_array_add(args, g_strdup("--freebind")); 293 } 294 295 if (passt->has_ipv4 && !passt->ipv4) { 296 g_ptr_array_add(args, g_strdup("--ipv6-only")); 297 } 298 299 if (passt->has_ipv6 && !passt->ipv6) { 300 g_ptr_array_add(args, g_strdup("--ipv4-only")); 301 } 302 303 if (passt->has_search && passt->search) { 304 const StringList *list = passt->search; 305 GString *domains = g_string_new(list->value->str); 306 307 list = list->next; 308 while (list) { 309 g_string_append(domains, " "); 310 g_string_append(domains, list->value->str); 311 list = list->next; 312 } 313 314 g_ptr_array_add(args, g_strdup("--search")); 315 g_ptr_array_add(args, g_string_free(domains, FALSE)); 316 } 317 318 if (passt->has_tcp_ports && passt->tcp_ports) { 319 const StringList *list = passt->tcp_ports; 320 GString *tcp_ports = g_string_new(list->value->str); 321 322 list = list->next; 323 while (list) { 324 g_string_append(tcp_ports, ","); 325 g_string_append(tcp_ports, list->value->str); 326 list = list->next; 327 } 328 329 g_ptr_array_add(args, g_strdup("--tcp-ports")); 330 g_ptr_array_add(args, g_string_free(tcp_ports, FALSE)); 331 } 332 333 if (passt->has_udp_ports && passt->udp_ports) { 334 const StringList *list = passt->udp_ports; 335 GString *udp_ports = g_string_new(list->value->str); 336 337 list = list->next; 338 while (list) { 339 g_string_append(udp_ports, ","); 340 g_string_append(udp_ports, list->value->str); 341 list = list->next; 342 } 343 344 g_ptr_array_add(args, g_strdup("--udp-ports")); 345 g_ptr_array_add(args, g_string_free(udp_ports, FALSE)); 346 } 347 348 if (passt->has_param && passt->param) { 349 const StringList *list = passt->param; 350 351 while (list) { 352 g_ptr_array_add(args, g_strdup(list->value->str)); 353 list = list->next; 354 } 355 } 356 357 /* provide a pid file to be able to kil passt on exit */ 358 g_ptr_array_add(args, g_strdup("--pid")); 359 g_ptr_array_add(args, g_strdup(pidfile)); 360 361 /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */ 362 g_ptr_array_add(args, g_strdup("--fd")); 363 g_ptr_array_add(args, g_strdup("3")); 364 365 g_ptr_array_add(args, NULL); 366 367 return args; 368 } 369 370 int net_init_passt(const Netdev *netdev, const char *name, 371 NetClientState *peer, Error **errp) 372 { 373 g_autoptr(GError) error = NULL; 374 NetClientState *nc; 375 NetPasstState *s; 376 GPtrArray *args; 377 gchar *pidfile; 378 int pidfd; 379 380 assert(netdev->type == NET_CLIENT_DRIVER_PASST); 381 382 pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error); 383 if (pidfd == -1) { 384 error_setg(errp, "Failed to create temporary file: %s", error->message); 385 return -1; 386 } 387 close(pidfd); 388 389 args = net_passt_decode_args(&netdev->u.passt, pidfile, errp); 390 if (args == NULL) { 391 g_free(pidfile); 392 return -1; 393 } 394 395 nc = qemu_new_net_client(&net_passt_info, peer, "passt", name); 396 s = DO_UPCAST(NetPasstState, data.nc, nc); 397 398 s->args = args; 399 s->pidfile = pidfile; 400 401 if (net_passt_stream_start(s, errp) == -1) { 402 qemu_del_net_client(nc); 403 return -1; 404 } 405 406 return 0; 407 } 408