+static void slip_unstuff(struct slip *st, uint8_t *buf, uint32_t l)
+{
+ uint32_t i;
+
+ BUF_ASSERT_USED(st->buff);
+ for (i=0; i<l; i++) {
+ int outputchr;
+ enum { OUTPUT_END = 256, OUTPUT_NOTHING = 257 };
+
+ if (!st->buff->size)
+ buffer_init(st->buff,calculate_max_start_pad());
+
+ if (st->pending_esc) {
+ st->pending_esc=False;
+ switch(buf[i]) {
+ case SLIP_ESCEND:
+ outputchr=SLIP_END;
+ break;
+ case SLIP_ESCESC:
+ outputchr=SLIP_ESC;
+ break;
+ default:
+ if (!st->ignoring_packet) {
+ Message(M_WARNING, "userv_afterpoll: bad SLIP escape"
+ " character, dropping packet\n");
+ }
+ st->ignoring_packet=True;
+ outputchr=OUTPUT_NOTHING;
+ break;
+ }
+ } else {
+ switch (buf[i]) {
+ case SLIP_END:
+ outputchr=OUTPUT_END;
+ break;
+ case SLIP_ESC:
+ st->pending_esc=True;
+ outputchr=OUTPUT_NOTHING;
+ break;
+ default:
+ outputchr=buf[i];
+ break;
+ }
+ }
+
+ if (st->ignoring_packet) {
+ if (outputchr == OUTPUT_END) {
+ st->ignoring_packet=False;
+ st->buff->size=0;
+ }
+ } else {
+ if (outputchr == OUTPUT_END) {
+ if (st->buff->size>0) {
+ st->netlink_to_tunnel(&st->nl,st->buff);
+ BUF_ALLOC(st->buff,"userv_afterpoll");
+ }
+ st->buff->size=0;
+ } else if (outputchr != OUTPUT_NOTHING) {
+ if (buf_remaining_space(st->buff)) {
+ buf_append_uint8(st->buff,outputchr);
+ } else {
+ Message(M_WARNING, "userv_afterpoll: dropping overlong"
+ " SLIP packet\n");
+ st->ignoring_packet=True;
+ }
+ }
+ }
+ }
+}
+
+static void slip_init(struct slip *st, struct cloc loc, dict_t *dict,
+ cstring_t name, netlink_deliver_fn *to_host)
+{
+ st->netlink_to_tunnel=
+ netlink_init(&st->nl,st,loc,dict,
+ "netlink-userv-ipif",NULL,to_host);
+ st->buff=find_cl_if(dict,"buffer",CL_BUFFER,True,"name",loc);
+ BUF_ALLOC(st->buff,"slip_init");
+ st->pending_esc=False;
+ st->ignoring_packet=False;
+}
+
+/* Connection to the kernel through userv-ipif */
+
+struct userv {
+ struct slip slip;
+ int txfd; /* We transmit to userv */
+ int rxfd; /* We receive from userv */
+ cstring_t userv_path;
+ cstring_t service_user;
+ cstring_t service_name;
+ pid_t pid;
+ bool_t expecting_userv_exit;
+};
+
+static int userv_beforepoll(void *sst, struct pollfd *fds, int *nfds_io,
+ int *timeout_io)
+{
+ struct userv *st=sst;
+
+ if (st->rxfd!=-1) {
+ *nfds_io=2;
+ fds[0].fd=st->txfd;
+ fds[0].events=0; /* Might want to pick up POLLOUT sometime */
+ fds[1].fd=st->rxfd;
+ fds[1].events=POLLIN;
+ } else {
+ *nfds_io=0;
+ }
+ return 0;
+}
+
+static void userv_afterpoll(void *sst, struct pollfd *fds, int nfds)
+{
+ struct userv *st=sst;
+ uint8_t rxbuf[DEFAULT_BUFSIZE];
+ int l;
+
+ if (nfds==0) return;
+
+ if (fds[1].revents&POLLERR) {
+ Message(M_ERR,"%s: userv_afterpoll: POLLERR!\n",st->slip.nl.name);
+ }
+ if (fds[1].revents&POLLIN) {
+ l=read(st->rxfd,rxbuf,DEFAULT_BUFSIZE);
+ if (l<0) {
+ if (errno!=EINTR)
+ fatal_perror("%s: userv_afterpoll: read(rxfd)",
+ st->slip.nl.name);
+ } else if (l==0) {
+ fatal("%s: userv_afterpoll: read(rxfd)=0; userv gone away?",
+ st->slip.nl.name);
+ } else slip_unstuff(&st->slip,rxbuf,l);
+ }
+}
+
+/* Send buf to the kernel. Free buf before returning. */
+static void userv_deliver_to_kernel(void *sst, struct buffer_if *buf)
+{
+ struct userv *st=sst;
+
+ if (buf->size > st->slip.nl.mtu) {
+ Message(M_ERR,"%s: packet of size %"PRIu32" exceeds mtu %"PRIu32":"
+ " cannot be injected into kernel, dropped\n",
+ st->slip.nl.name, buf->size, st->slip.nl.mtu);
+ BUF_FREE(buf);
+ return;
+ }
+
+ slip_stuff(&st->slip,buf,st->txfd);
+}
+
+static void userv_userv_callback(void *sst, pid_t pid, int status)