/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright Red Hat * Author: David Gibson * * Passt/pasta interface types and IDs */ #include #include #include #include #include "util.h" #include "pif.h" #include "siphash.h" #include "ip.h" #include "inany.h" #include "epoll_ctl.h" const char pif_type_str[][PIF_NAME_SIZE] = { [PIF_NONE] = "", [PIF_HOST] = "HOST", [PIF_TAP] = "TAP", [PIF_SPLICE] = "SPLICE", }; static_assert(ARRAY_SIZE(pif_type_str) == PIF_NUM_TYPES, "pif_type_str[] doesn't match enum pif_type"); /** pif_sockaddr() - Construct a socket address suitable for an interface * @c: Execution context * @sa: Pointer to sockaddr to fill in * @pif: Interface to create the socket address * @addr: IPv[46] address * @port: Port (host byte order) */ void pif_sockaddr(const struct ctx *c, union sockaddr_inany *sa, uint8_t pif, const union inany_addr *addr, in_port_t port) { const struct in_addr *v4 = inany_v4(addr); assert(pif_is_socket(pif)); if (v4) { sa->sa_family = AF_INET; sa->sa4.sin_addr = *v4; sa->sa4.sin_port = htons(port); memset(&sa->sa4.sin_zero, 0, sizeof(sa->sa4.sin_zero)); } else { sa->sa_family = AF_INET6; sa->sa6.sin6_addr = addr->a6; sa->sa6.sin6_port = htons(port); if (IN6_IS_ADDR_LINKLOCAL(&addr->a6)) { if (pif == PIF_HOST) sa->sa6.sin6_scope_id = c->ifi6; else if (pif == PIF_SPLICE) sa->sa6.sin6_scope_id = c->pasta_ifi; } else { sa->sa6.sin6_scope_id = 0; } sa->sa6.sin6_flowinfo = 0; } } /** pif_listen() - Open a listening socket on a specified pif * @c: Execution context * @proto: Socket protocol (IPPROTO_TCP or IPPROTO_UDP) * @pif: Interface for this socket * @addr: Address to bind to, or NULL for dual-stack any * @ifname: Interface for binding, NULL for any * @port: Port number to bind to (host byte order) * @rule: Forwarding rule index this socket belongs to * * NOTE: For namespace pifs, this must be called having already entered the * relevant namespace. * * Return: newly created socket, negative error code on failure */ int pif_listen(const struct ctx *c, uint8_t proto, uint8_t pif, const union inany_addr *addr, const char *ifname, in_port_t port, unsigned rule) { union epoll_ref ref; int ret; assert(pif_is_socket(pif)); if (!c->ifi4) { if (!addr) /* Restrict to v6 only */ addr = &inany_any6; else if (inany_v4(addr)) return -EAFNOSUPPORT; } if (!c->ifi6) { if (!addr) /* Restrict to v4 only */ addr = &inany_any4; else if (!inany_v4(addr)) return -EAFNOSUPPORT; } if (proto == IPPROTO_TCP) ref.type = EPOLL_TYPE_TCP_LISTEN; else if (proto == IPPROTO_UDP) ref.type = EPOLL_TYPE_UDP_LISTEN; else return -EPROTONOSUPPORT; if (!addr) { ref.fd = sock_l4_dualstack_any(c, ref.type, port, ifname); } else { union sockaddr_inany sa; pif_sockaddr(c, &sa, pif, addr, port); ref.fd = sock_l4(c, ref.type, &sa, ifname); } if (ref.fd < 0) return ref.fd; ref.listen.port = port; ref.listen.pif = pif; ref.listen.rule = rule; if (proto == IPPROTO_TCP && listen(ref.fd, 128) < 0) { ret = -errno; goto fail; } ret = epoll_add(c->epollfd, EPOLLIN, ref); if (ret < 0) goto fail; return ref.fd; fail: close(ref.fd); return ret; }