#pragma once #include "types.h" // Unix domain sockets. // // These exist for Schcom. A compositor and its clients have to exchange // messages, and a pipe cannot do it: a pipe is one-directional, has no // notion of a connection, and gives no way for a server to be *found* by a // client that starts later. A socket with a name in the filesystem is // exactly the missing piece. // // AF_INET arrived later, and exactly as planned: underneath the same syscalls // rather than reshaping them. A socket carries its domain and every entry point // dispatches on it -- kernel/net/unix.c for AF_UNIX, kernel/net/inet.c (lwIP) // for AF_INET. #define AF_UNIX 1 #define AF_LOCAL 1 #define AF_INET 2 // Linux's number, because a musl program passes Linux's constant. getifaddrs() // is implemented over this, which is why it exists here: enumerating interfaces // is something every networked program does and nothing else offers. #define AF_NETLINK 16 #define PF_NETLINK AF_NETLINK #define NETLINK_ROUTE 0 #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 // Flags ORed into socket()'s and accept4()'s `type`/`flags` argument. Linux's // values, because a musl program passes Linux's constants. #define SOCK_NONBLOCK 0x800 // O_NONBLOCK #define SOCK_CLOEXEC 0x80000 // O_CLOEXEC // setsockopt/getsockopt levels and names, Linux's numbering. // // Only what is listed here is accepted; anything else is answered with // ENOPROTOOPT rather than a success it did not earn. See the comment above // sys_setsockopt for why that is worth the risk of failing a caller that never // checked before. #define SOL_SOCKET 1 #define IPPROTO_ICMP 1 #define IPPROTO_TCP 6 #define SO_REUSEADDR 2 #define SO_TYPE 3 #define SO_ERROR 4 #define SO_BROADCAST 6 #define SO_SNDBUF 7 #define SO_RCVBUF 8 #define SO_KEEPALIVE 9 #define SO_ACCEPTCONN 30 #define TCP_NODELAY 1 // --------------------------------------------------------------------------- // Ancillary data -- sendmsg/recvmsg, and SCM_RIGHTS // // A descriptor is the only thing that can be passed between two processes that // is not a number: it names an object the kernel already knows, and handing it // over is the difference between "here is a buffer" and "here is a *copy* of a // buffer". Wayland's wl_shm_create_pool passes one and has no integer-shaped // alternative; docs/plan/hybrid.md's mechanism 1 is this and nothing else. // // Every structure below is Linux's, byte for byte, because a musl-linked // program builds them with Linux's headers and we do not get a vote. The // offsets that matter on x86-64 are, for msghdr: // // 0 msg_name 8 msg_namelen(+4 pad) 16 msg_iov 24 msg_iovlen // 32 msg_control 40 msg_controllen 48 msg_flags // // -- 56 bytes total, which is what musl's int-typed msg_iovlen plus a pad word // adds up to as well. // --------------------------------------------------------------------------- #define MSG_OOB 0x0001 #define MSG_PEEK 0x0002 #define MSG_DONTROUTE 0x0004 #define MSG_CTRUNC 0x0008 // some ancillary data was dropped #define MSG_TRUNC 0x0020 #define MSG_DONTWAIT 0x0040 #define MSG_EOR 0x0080 #define MSG_WAITALL 0x0100 #define MSG_NOSIGNAL 0x4000 #define MSG_CMSG_CLOEXEC 0x40000000 // The only control message that exists here. SCM_CREDENTIALS is deliberately // absent rather than stubbed: a caller told it succeeded would read a uid out // of uninitialised memory and act on it. #define SCM_RIGHTS 1 // The most descriptors one message may carry. A cap has to exist -- the kernel // holds a reference to every one of them until it is received -- and saying so // here means a sender can size its control buffer against the same number the // kernel will enforce. #define SCM_MAXFD 8 struct iovec { void *iov_base; uint64 iov_len; }; struct msghdr { void *msg_name; // struct sockaddr *, or 0 uint32 msg_namelen; uint32 __pad1; struct iovec *msg_iov; uint64 msg_iovlen; void *msg_control; // struct cmsghdr *, or 0 uint64 msg_controllen; int msg_flags; // out on recvmsg: MSG_CTRUNC }; struct cmsghdr { uint64 cmsg_len; // CMSG_LEN(payload), NOT including padding int cmsg_level; // SOL_SOCKET int cmsg_type; // SCM_RIGHTS }; // cmsg_len counts the header plus the payload; CMSG_SPACE additionally counts // the padding to the next header. Conflating the two is the classic way to // build a control buffer the kernel then rejects, which is why both exist. #define CMSG_ALIGN(n) (((uint64)(n) + sizeof(uint64) - 1) & ~(uint64)(sizeof(uint64) - 1)) #define CMSG_LEN(n) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (uint64)(n)) #define CMSG_SPACE(n) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(n)) #define CMSG_DATA(c) ((unsigned char *)((struct cmsghdr *)(c) + 1)) #define CMSG_FIRSTHDR(m) \ ((m)->msg_control && (m)->msg_controllen >= sizeof(struct cmsghdr) \ ? (struct cmsghdr *)(m)->msg_control \ : (struct cmsghdr *)0) #define CMSG_NXTHDR(m, c) \ ((c)->cmsg_len < sizeof(struct cmsghdr) || \ (uint64)((char *)(c) + CMSG_ALIGN((c)->cmsg_len) + sizeof(struct cmsghdr) \ - (char *)(m)->msg_control) > (m)->msg_controllen \ ? (struct cmsghdr *)0 \ : (struct cmsghdr *)((char *)(c) + CMSG_ALIGN((c)->cmsg_len))) #define UNIX_PATH_MAX 108 #define SHUT_RD 0 #define SHUT_WR 1 #define SHUT_RDWR 2 struct sockaddr_un { uint16 sun_family; char sun_path[UNIX_PATH_MAX]; }; // The generic form syscalls take. Same shape as Linux's, so a musl-linked // program's struct lines up byte for byte. struct sockaddr { uint16 sa_family; char sa_data[UNIX_PATH_MAX]; }; // The AF_INET address. Same layout as Linux's, so a musl-linked program's // struct lines up byte for byte. sin_port and sin_addr are network byte order // and stay that way: lwIP wants them so, and converting on the way in and out // again would be two swaps that cancel. struct sockaddr_in { uint16 sin_family; uint16 sin_port; uint32 sin_addr; char sin_zero[8]; }; // --------------------------------------------------------------------------- // rtnetlink, enough of it for getifaddrs(3) // // Every structure below is Linux's, byte for byte, because musl parses what it // is handed with Linux's own headers. They are not ours to design. // // What musl actually does (src/network/netlink.c) is the whole specification // this has to meet: open PF_NETLINK/SOCK_RAW/NETLINK_ROUTE, send an // nlmsghdr+rtgenmsg asking RTM_GETLINK with NLM_F_DUMP, read replies until // NLMSG_DONE, then do it again for RTM_GETADDR on the same socket. // --------------------------------------------------------------------------- struct nlmsghdr { uint32 nlmsg_len; uint16 nlmsg_type; uint16 nlmsg_flags; uint32 nlmsg_seq; uint32 nlmsg_pid; }; struct rtgenmsg { uchar rtgen_family; }; struct ifinfomsg { uchar ifi_family; uchar __ifi_pad; uint16 ifi_type; // ARPHRD_* int32 ifi_index; uint32 ifi_flags; // IFF_* uint32 ifi_change; }; struct ifaddrmsg { uchar ifa_family; uchar ifa_prefixlen; uchar ifa_flags; uchar ifa_scope; uint32 ifa_index; }; struct rtattr { uint16 rta_len; uint16 rta_type; }; #define NLMSG_ERROR 2 #define NLMSG_DONE 3 #define NLM_F_REQUEST 0x001 #define NLM_F_MULTI 0x002 #define NLM_F_DUMP 0x300 #define RTM_NEWLINK 16 #define RTM_GETLINK 18 #define RTM_NEWADDR 20 #define RTM_GETADDR 22 #define IFLA_ADDRESS 1 #define IFLA_IFNAME 3 #define IFA_ADDRESS 1 #define IFA_LOCAL 2 #define IFA_LABEL 3 #define ARPHRD_ETHER 1 #define ARPHRD_LOOPBACK 772 #define IFF_UP 0x1 #define IFF_LOOPBACK 0x8 #define IFF_RUNNING 0x40 #define IFF_BROADCAST 0x2 #define IFF_MULTICAST 0x1000 // Both are four-byte aligned on every architecture Linux supports. #define NLMSG_ALIGN(n) (((n) + 3) & ~3) #define RTA_ALIGN(n) (((n) + 3) & ~3) #ifdef _KERNEL_SOCKET_INTERNALS #define NSOCK 32 // sockets in the system #define SOCKBUF 4096 // receive buffer per connected socket #define NBACKLOG 8 // Descriptors in flight. // // A batch is pinned to the stream position of the first byte of the message // that carried it, and that is the whole delivery rule: a reader that arrives // at that position gets the descriptors, and a reader that has not reached it // yet is stopped *short* of it rather than allowed to read past. Without the // second half a program that read 4096 bytes in one call would swallow three // messages and be handed one message's descriptors, with no way to tell which. // // Eight batches in flight is not a guess: a sender that has eight unreceived // messages carrying descriptors and keeps going is not going to be rescued by // a ninth slot, and ENOBUFS says so where a larger array would only move the // same failure further away. #define NSCM 8 // messages carrying descriptors, in flight struct file; struct scm_fds { uint pos; // stream position of the message's first byte int n; struct file *f[SCM_MAXFD]; // references held by the kernel, not by anyone }; enum sockstate { SS_FREE = 0, SS_UNBOUND, SS_BOUND, SS_LISTENING, SS_CONNECTED, }; // Every field here is protected by the socket table's single lock in // kernel/net/unix.c -- there is deliberately no per-socket lock; see the // comment at the top of that file for why. struct socket { int ref; int type; int state; int domain; // AF_UNIX or AF_INET // For AF_INET: the lwIP-backed socket in kernel/net/inet.c. Opaque here so // that socket.h does not have to drag lwIP's headers into every file that // wants a struct socket. void *inet; char name[UNIX_PATH_MAX]; // the bound path, for a listener // A connected socket's peer. Writing to a socket appends to the *peer's* // buffer, which is what makes one buffer per socket enough for a // bidirectional connection. struct socket *peer; int peer_closed; char data[SOCKBUF]; uint nread, nwrite; // Connections waiting to be accepted, on a listener. struct socket *queue[NBACKLOG]; int nqueue; // Socket options that are genuinely honoured. Kept here rather than only in // lwIP's pcb because getsockopt has to answer for an AF_UNIX socket too, and // because a flag set before bind() has to survive until there is a pcb to // apply it to. int o_reuseaddr; int o_keepalive; int o_broadcast; int o_nodelay; // Descriptors sent to this socket and not yet received. They live on the // *receiving* side, next to the bytes they were sent with, because that is // the only side that can say when they have been delivered. struct scm_fds scm[NSCM]; int nscm; }; #endif