Add sunrpc.
diff --git a/src/libsunrpc/udp.c b/src/libsunrpc/udp.c
new file mode 100644
index 0000000..ff668f8
--- /dev/null
+++ b/src/libsunrpc/udp.c
@@ -0,0 +1,120 @@
+#include <u.h>
+#include <libc.h>
+#include <ip.h>
+#include <thread.h>
+#include <sunrpc.h>
+
+typedef struct SunMsgUdp SunMsgUdp;
+struct SunMsgUdp
+{
+	SunMsg msg;
+	Udphdr udp;
+};
+
+typedef struct Arg Arg;
+struct Arg
+{
+	SunSrv *srv;
+	Channel *creply;
+	Channel *csync;
+	int fd;
+};
+
+enum
+{
+	UdpMaxRead = 65536+Udphdrsize
+};
+static void
+sunudpread(void *v)
+{
+	int n, paraport, port;
+	uchar *buf;
+	Arg arg = *(Arg*)v;
+	SunMsgUdp *msg;
+	SunSrv *srv;
+	Udphdr udp;
+	uchar localip[IPaddrlen];
+
+	sendp(arg.csync, 0);
+	srv = arg.srv;
+	paraport = -1;
+
+	/* 127.1 */
+	memmove(localip, v4prefix, IPaddrlen);
+	localip[12] = 127;
+	localip[15] = 1;
+
+	buf = emalloc(UdpMaxRead);
+	while((n = udpread(arg.fd, &udp, buf, UdpMaxRead)) > 0){
+		if(arg.srv->chatty)
+			fprint(2, "udpread got %d (%d) from %I\n", n, Udphdrsize, udp.raddr);
+		if((srv->localonly || srv->localparanoia) && ipcmp(udp.raddr, localip) != 0){
+			fprint(2, "dropping message from %I: not local\n", udp.raddr);
+			continue;
+		}
+		if(srv->localparanoia){
+			port = nhgets(udp.rport);
+			if(paraport == -1){
+				fprint(2, "paranoid mode: only %I/%d allowed\n", localip, port);
+				paraport = port;
+			}else if(paraport != port){
+				fprint(2, "dropping message from %I: not port %d\n", udp.raddr, port);
+				continue;
+			}
+		}
+		msg = emalloc(sizeof(SunMsgUdp));
+		msg->udp = udp;
+		msg->msg.data = emalloc(n);
+		msg->msg.count = n;
+		memmove(msg->msg.data, buf, n);
+		msg->msg.creply = arg.creply;
+		if(arg.srv->chatty)
+			fprint(2, "message %p count %d\n", msg, msg->msg.count);
+		sendp(arg.srv->crequest, msg);
+	}
+}
+
+static void
+sunudpwrite(void *v)
+{
+	uchar *buf;
+	Arg arg = *(Arg*)v;
+	SunMsgUdp *msg;
+
+	sendp(arg.csync, 0);
+
+	buf = emalloc(UdpMaxRead);
+	while((msg = recvp(arg.creply)) != nil){
+		if(udpwrite(arg.fd, &msg->udp, msg->msg.data, msg->msg.count) != msg->msg.count)
+			fprint(2, "udpwrite: %r\n");
+		free(msg->msg.data);
+		free(msg);
+	}
+}
+
+int
+sunsrvudp(SunSrv *srv, char *address)
+{
+	int fd;
+	char adir[40];
+	Arg *arg;
+
+	fd = announce(address, adir);
+	if(fd < 0)
+		return -1;
+
+	arg = emalloc(sizeof(Arg));
+	arg->fd = fd;
+	arg->srv = srv;
+	arg->creply = chancreate(sizeof(SunMsg*), 10);
+	arg->csync = chancreate(sizeof(void*), 10);
+
+	proccreate(sunudpread, arg, SunStackSize);
+	proccreate(sunudpwrite, arg, SunStackSize);
+	recvp(arg->csync);
+	recvp(arg->csync);
+	chanfree(arg->csync);
+	free(arg);
+
+	return 0;
+}