xref: /openbmc/qemu/tests/vhost-user-bridge.c (revision 99d423e5)
1 /*
2  * Vhost User Bridge
3  *
4  * Copyright (c) 2015 Red Hat, Inc.
5  *
6  * Authors:
7  *  Victor Kaplansky <victork@redhat.com>
8  *
9  * This work is licensed under the terms of the GNU GPL, version 2 or
10  * later.  See the COPYING file in the top-level directory.
11  */
12 
13 /*
14  * TODO:
15  *     - main should get parameters from the command line.
16  *     - implement all request handlers. Still not implemented:
17  *          vubr_get_queue_num_exec()
18  *          vubr_send_rarp_exec()
19  *     - test for broken requests and virtqueue.
20  *     - implement features defined by Virtio 1.0 spec.
21  *     - support mergeable buffers and indirect descriptors.
22  *     - implement clean shutdown.
23  *     - implement non-blocking writes to UDP backend.
24  *     - implement polling strategy.
25  *     - implement clean starting/stopping of vq processing
26  *     - implement clean starting/stopping of used and buffers
27  *       dirty page logging.
28  */
29 
30 #define _FILE_OFFSET_BITS 64
31 
32 #include "qemu/osdep.h"
33 #include "qemu-common.h"
34 #include "qemu/atomic.h"
35 #include "qemu/iov.h"
36 #include "standard-headers/linux/virtio_net.h"
37 #include "contrib/libvhost-user/libvhost-user.h"
38 
39 #define VHOST_USER_BRIDGE_DEBUG 1
40 
41 #define DPRINT(...) \
42     do { \
43         if (VHOST_USER_BRIDGE_DEBUG) { \
44             printf(__VA_ARGS__); \
45         } \
46     } while (0)
47 
48 typedef void (*CallbackFunc)(int sock, void *ctx);
49 
50 typedef struct Event {
51     void *ctx;
52     CallbackFunc callback;
53 } Event;
54 
55 typedef struct Dispatcher {
56     int max_sock;
57     fd_set fdset;
58     Event events[FD_SETSIZE];
59 } Dispatcher;
60 
61 typedef struct VubrDev {
62     VuDev vudev;
63     Dispatcher dispatcher;
64     int backend_udp_sock;
65     struct sockaddr_in backend_udp_dest;
66     int hdrlen;
67     int sock;
68     int ready;
69     int quit;
70     struct {
71         int fd;
72         void *addr;
73         pthread_t thread;
74     } notifier;
75 } VubrDev;
76 
77 static void
78 vubr_die(const char *s)
79 {
80     perror(s);
81     exit(1);
82 }
83 
84 static int
85 dispatcher_init(Dispatcher *dispr)
86 {
87     FD_ZERO(&dispr->fdset);
88     dispr->max_sock = -1;
89     return 0;
90 }
91 
92 static int
93 dispatcher_add(Dispatcher *dispr, int sock, void *ctx, CallbackFunc cb)
94 {
95     if (sock >= FD_SETSIZE) {
96         fprintf(stderr,
97                 "Error: Failed to add new event. sock %d should be less than %d\n",
98                 sock, FD_SETSIZE);
99         return -1;
100     }
101 
102     dispr->events[sock].ctx = ctx;
103     dispr->events[sock].callback = cb;
104 
105     FD_SET(sock, &dispr->fdset);
106     if (sock > dispr->max_sock) {
107         dispr->max_sock = sock;
108     }
109     DPRINT("Added sock %d for watching. max_sock: %d\n",
110            sock, dispr->max_sock);
111     return 0;
112 }
113 
114 static int
115 dispatcher_remove(Dispatcher *dispr, int sock)
116 {
117     if (sock >= FD_SETSIZE) {
118         fprintf(stderr,
119                 "Error: Failed to remove event. sock %d should be less than %d\n",
120                 sock, FD_SETSIZE);
121         return -1;
122     }
123 
124     FD_CLR(sock, &dispr->fdset);
125     DPRINT("Sock %d removed from dispatcher watch.\n", sock);
126     return 0;
127 }
128 
129 /* timeout in us */
130 static int
131 dispatcher_wait(Dispatcher *dispr, uint32_t timeout)
132 {
133     struct timeval tv;
134     tv.tv_sec = timeout / 1000000;
135     tv.tv_usec = timeout % 1000000;
136 
137     fd_set fdset = dispr->fdset;
138 
139     /* wait until some of sockets become readable. */
140     int rc = select(dispr->max_sock + 1, &fdset, 0, 0, &tv);
141 
142     if (rc == -1) {
143         vubr_die("select");
144     }
145 
146     /* Timeout */
147     if (rc == 0) {
148         return 0;
149     }
150 
151     /* Now call callback for every ready socket. */
152 
153     int sock;
154     for (sock = 0; sock < dispr->max_sock + 1; sock++) {
155         /* The callback on a socket can remove other sockets from the
156          * dispatcher, thus we have to check that the socket is
157          * still not removed from dispatcher's list
158          */
159         if (FD_ISSET(sock, &fdset) && FD_ISSET(sock, &dispr->fdset)) {
160             Event *e = &dispr->events[sock];
161             e->callback(sock, e->ctx);
162         }
163     }
164 
165     return 0;
166 }
167 
168 static void
169 vubr_handle_tx(VuDev *dev, int qidx)
170 {
171     VuVirtq *vq = vu_get_queue(dev, qidx);
172     VubrDev *vubr = container_of(dev, VubrDev, vudev);
173     int hdrlen = vubr->hdrlen;
174     VuVirtqElement *elem = NULL;
175 
176     assert(qidx % 2);
177 
178     for (;;) {
179         ssize_t ret;
180         unsigned int out_num;
181         struct iovec sg[VIRTQUEUE_MAX_SIZE], *out_sg;
182 
183         elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
184         if (!elem) {
185             break;
186         }
187 
188         out_num = elem->out_num;
189         out_sg = elem->out_sg;
190         if (out_num < 1) {
191             fprintf(stderr, "virtio-net header not in first element\n");
192             break;
193         }
194         if (VHOST_USER_BRIDGE_DEBUG) {
195             iov_hexdump(out_sg, out_num, stderr, "TX:", 1024);
196         }
197 
198         if (hdrlen) {
199             unsigned sg_num = iov_copy(sg, ARRAY_SIZE(sg),
200                                        out_sg, out_num,
201                                        hdrlen, -1);
202             out_num = sg_num;
203             out_sg = sg;
204         }
205 
206         struct msghdr msg = {
207             .msg_name = (struct sockaddr *) &vubr->backend_udp_dest,
208             .msg_namelen = sizeof(struct sockaddr_in),
209             .msg_iov = out_sg,
210             .msg_iovlen = out_num,
211         };
212         do {
213             ret = sendmsg(vubr->backend_udp_sock, &msg, 0);
214         } while (ret == -1 && (errno == EAGAIN || errno == EINTR));
215 
216         if (ret == -1) {
217             vubr_die("sendmsg()");
218         }
219 
220         vu_queue_push(dev, vq, elem, 0);
221         vu_queue_notify(dev, vq);
222 
223         free(elem);
224         elem = NULL;
225     }
226 
227     free(elem);
228 }
229 
230 
231 /* this function reverse the effect of iov_discard_front() it must be
232  * called with 'front' being the original struct iovec and 'bytes'
233  * being the number of bytes you shaved off
234  */
235 static void
236 iov_restore_front(struct iovec *front, struct iovec *iov, size_t bytes)
237 {
238     struct iovec *cur;
239 
240     for (cur = front; cur != iov; cur++) {
241         assert(bytes >= cur->iov_len);
242         bytes -= cur->iov_len;
243     }
244 
245     cur->iov_base -= bytes;
246     cur->iov_len += bytes;
247 }
248 
249 static void
250 iov_truncate(struct iovec *iov, unsigned iovc, size_t bytes)
251 {
252     unsigned i;
253 
254     for (i = 0; i < iovc; i++, iov++) {
255         if (bytes < iov->iov_len) {
256             iov->iov_len = bytes;
257             return;
258         }
259 
260         bytes -= iov->iov_len;
261     }
262 
263     assert(!"couldn't truncate iov");
264 }
265 
266 static void
267 vubr_backend_recv_cb(int sock, void *ctx)
268 {
269     VubrDev *vubr = (VubrDev *) ctx;
270     VuDev *dev = &vubr->vudev;
271     VuVirtq *vq = vu_get_queue(dev, 0);
272     VuVirtqElement *elem = NULL;
273     struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE];
274     struct virtio_net_hdr_mrg_rxbuf mhdr;
275     unsigned mhdr_cnt = 0;
276     int hdrlen = vubr->hdrlen;
277     int i = 0;
278     struct virtio_net_hdr hdr = {
279         .flags = 0,
280         .gso_type = VIRTIO_NET_HDR_GSO_NONE
281     };
282 
283     DPRINT("\n\n   ***   IN UDP RECEIVE CALLBACK    ***\n\n");
284     DPRINT("    hdrlen = %d\n", hdrlen);
285 
286     if (!vu_queue_enabled(dev, vq) ||
287         !vu_queue_started(dev, vq) ||
288         !vu_queue_avail_bytes(dev, vq, hdrlen, 0)) {
289         DPRINT("Got UDP packet, but no available descriptors on RX virtq.\n");
290         return;
291     }
292 
293     while (1) {
294         struct iovec *sg;
295         ssize_t ret, total = 0;
296         unsigned int num;
297 
298         elem = vu_queue_pop(dev, vq, sizeof(VuVirtqElement));
299         if (!elem) {
300             break;
301         }
302 
303         if (elem->in_num < 1) {
304             fprintf(stderr, "virtio-net contains no in buffers\n");
305             break;
306         }
307 
308         sg = elem->in_sg;
309         num = elem->in_num;
310         if (i == 0) {
311             if (hdrlen == 12) {
312                 mhdr_cnt = iov_copy(mhdr_sg, ARRAY_SIZE(mhdr_sg),
313                                     sg, elem->in_num,
314                                     offsetof(typeof(mhdr), num_buffers),
315                                     sizeof(mhdr.num_buffers));
316             }
317             iov_from_buf(sg, elem->in_num, 0, &hdr, sizeof hdr);
318             total += hdrlen;
319             ret = iov_discard_front(&sg, &num, hdrlen);
320             assert(ret == hdrlen);
321         }
322 
323         struct msghdr msg = {
324             .msg_name = (struct sockaddr *) &vubr->backend_udp_dest,
325             .msg_namelen = sizeof(struct sockaddr_in),
326             .msg_iov = sg,
327             .msg_iovlen = num,
328             .msg_flags = MSG_DONTWAIT,
329         };
330         do {
331             ret = recvmsg(vubr->backend_udp_sock, &msg, 0);
332         } while (ret == -1 && (errno == EINTR));
333 
334         if (i == 0) {
335             iov_restore_front(elem->in_sg, sg, hdrlen);
336         }
337 
338         if (ret == -1) {
339             if (errno == EWOULDBLOCK) {
340                 vu_queue_rewind(dev, vq, 1);
341                 break;
342             }
343 
344             vubr_die("recvmsg()");
345         }
346 
347         total += ret;
348         iov_truncate(elem->in_sg, elem->in_num, total);
349         vu_queue_fill(dev, vq, elem, total, i++);
350 
351         free(elem);
352         elem = NULL;
353 
354         break;        /* could loop if DONTWAIT worked? */
355     }
356 
357     if (mhdr_cnt) {
358         mhdr.num_buffers = i;
359         iov_from_buf(mhdr_sg, mhdr_cnt,
360                      0,
361                      &mhdr.num_buffers, sizeof mhdr.num_buffers);
362     }
363 
364     vu_queue_flush(dev, vq, i);
365     vu_queue_notify(dev, vq);
366 
367     free(elem);
368 }
369 
370 static void
371 vubr_receive_cb(int sock, void *ctx)
372 {
373     VubrDev *vubr = (VubrDev *)ctx;
374 
375     if (!vu_dispatch(&vubr->vudev)) {
376         fprintf(stderr, "Error while dispatching\n");
377     }
378 }
379 
380 typedef struct WatchData {
381     VuDev *dev;
382     vu_watch_cb cb;
383     void *data;
384 } WatchData;
385 
386 static void
387 watch_cb(int sock, void *ctx)
388 {
389     struct WatchData *wd = ctx;
390 
391     wd->cb(wd->dev, VU_WATCH_IN, wd->data);
392 }
393 
394 static void
395 vubr_set_watch(VuDev *dev, int fd, int condition,
396                vu_watch_cb cb, void *data)
397 {
398     VubrDev *vubr = container_of(dev, VubrDev, vudev);
399     static WatchData watches[FD_SETSIZE];
400     struct WatchData *wd = &watches[fd];
401 
402     wd->cb = cb;
403     wd->data = data;
404     wd->dev = dev;
405     dispatcher_add(&vubr->dispatcher, fd, wd, watch_cb);
406 }
407 
408 static void
409 vubr_remove_watch(VuDev *dev, int fd)
410 {
411     VubrDev *vubr = container_of(dev, VubrDev, vudev);
412 
413     dispatcher_remove(&vubr->dispatcher, fd);
414 }
415 
416 static int
417 vubr_send_rarp_exec(VuDev *dev, VhostUserMsg *vmsg)
418 {
419     DPRINT("Function %s() not implemented yet.\n", __func__);
420     return 0;
421 }
422 
423 static int
424 vubr_process_msg(VuDev *dev, VhostUserMsg *vmsg, int *do_reply)
425 {
426     switch (vmsg->request) {
427     case VHOST_USER_SEND_RARP:
428         *do_reply = vubr_send_rarp_exec(dev, vmsg);
429         return 1;
430     default:
431         /* let the library handle the rest */
432         return 0;
433     }
434 
435     return 0;
436 }
437 
438 static void
439 vubr_set_features(VuDev *dev, uint64_t features)
440 {
441     VubrDev *vubr = container_of(dev, VubrDev, vudev);
442 
443     if ((features & (1ULL << VIRTIO_F_VERSION_1)) ||
444         (features & (1ULL << VIRTIO_NET_F_MRG_RXBUF))) {
445         vubr->hdrlen = 12;
446     } else {
447         vubr->hdrlen = 10;
448     }
449 }
450 
451 static uint64_t
452 vubr_get_features(VuDev *dev)
453 {
454     return 1ULL << VIRTIO_NET_F_GUEST_ANNOUNCE |
455         1ULL << VIRTIO_NET_F_MRG_RXBUF |
456         1ULL << VIRTIO_F_VERSION_1;
457 }
458 
459 static void
460 vubr_queue_set_started(VuDev *dev, int qidx, bool started)
461 {
462     VubrDev *vubr = container_of(dev, VubrDev, vudev);
463     VuVirtq *vq = vu_get_queue(dev, qidx);
464 
465     if (started && vubr->notifier.fd >= 0) {
466         vu_set_queue_host_notifier(dev, vq, vubr->notifier.fd,
467                                    getpagesize(),
468                                    qidx * getpagesize());
469     }
470 
471     if (qidx % 2 == 1) {
472         vu_set_queue_handler(dev, vq, started ? vubr_handle_tx : NULL);
473     }
474 }
475 
476 static void
477 vubr_panic(VuDev *dev, const char *msg)
478 {
479     VubrDev *vubr = container_of(dev, VubrDev, vudev);
480 
481     fprintf(stderr, "PANIC: %s\n", msg);
482 
483     dispatcher_remove(&vubr->dispatcher, dev->sock);
484     vubr->quit = 1;
485 }
486 
487 static bool
488 vubr_queue_is_processed_in_order(VuDev *dev, int qidx)
489 {
490     return true;
491 }
492 
493 static const VuDevIface vuiface = {
494     .get_features = vubr_get_features,
495     .set_features = vubr_set_features,
496     .process_msg = vubr_process_msg,
497     .queue_set_started = vubr_queue_set_started,
498     .queue_is_processed_in_order = vubr_queue_is_processed_in_order,
499 };
500 
501 static void
502 vubr_accept_cb(int sock, void *ctx)
503 {
504     VubrDev *dev = (VubrDev *)ctx;
505     int conn_fd;
506     struct sockaddr_un un;
507     socklen_t len = sizeof(un);
508 
509     conn_fd = accept(sock, (struct sockaddr *) &un, &len);
510     if (conn_fd == -1) {
511         vubr_die("accept()");
512     }
513     DPRINT("Got connection from remote peer on sock %d\n", conn_fd);
514 
515     vu_init(&dev->vudev,
516             conn_fd,
517             vubr_panic,
518             vubr_set_watch,
519             vubr_remove_watch,
520             &vuiface);
521 
522     dispatcher_add(&dev->dispatcher, conn_fd, ctx, vubr_receive_cb);
523     dispatcher_remove(&dev->dispatcher, sock);
524 }
525 
526 static VubrDev *
527 vubr_new(const char *path, bool client)
528 {
529     VubrDev *dev = (VubrDev *) calloc(1, sizeof(VubrDev));
530     struct sockaddr_un un;
531     CallbackFunc cb;
532     size_t len;
533 
534     /* Get a UNIX socket. */
535     dev->sock = socket(AF_UNIX, SOCK_STREAM, 0);
536     if (dev->sock == -1) {
537         vubr_die("socket");
538     }
539 
540     dev->notifier.fd = -1;
541 
542     un.sun_family = AF_UNIX;
543     strcpy(un.sun_path, path);
544     len = sizeof(un.sun_family) + strlen(path);
545 
546     if (!client) {
547         unlink(path);
548 
549         if (bind(dev->sock, (struct sockaddr *) &un, len) == -1) {
550             vubr_die("bind");
551         }
552 
553         if (listen(dev->sock, 1) == -1) {
554             vubr_die("listen");
555         }
556         cb = vubr_accept_cb;
557 
558         DPRINT("Waiting for connections on UNIX socket %s ...\n", path);
559     } else {
560         if (connect(dev->sock, (struct sockaddr *)&un, len) == -1) {
561             vubr_die("connect");
562         }
563         vu_init(&dev->vudev,
564                 dev->sock,
565                 vubr_panic,
566                 vubr_set_watch,
567                 vubr_remove_watch,
568                 &vuiface);
569         cb = vubr_receive_cb;
570     }
571 
572     dispatcher_init(&dev->dispatcher);
573 
574     dispatcher_add(&dev->dispatcher, dev->sock, (void *)dev, cb);
575 
576     return dev;
577 }
578 
579 static void *notifier_thread(void *arg)
580 {
581     VuDev *dev = (VuDev *)arg;
582     VubrDev *vubr = container_of(dev, VubrDev, vudev);
583     int pagesize = getpagesize();
584     int qidx;
585 
586     while (true) {
587         for (qidx = 0; qidx < VHOST_MAX_NR_VIRTQUEUE; qidx++) {
588             uint16_t *n = vubr->notifier.addr + pagesize * qidx;
589 
590             if (*n == qidx) {
591                 *n = 0xffff;
592                 /* We won't miss notifications if we reset
593                  * the memory first. */
594                 smp_mb();
595 
596                 DPRINT("Got a notification for queue%d via host notifier.\n",
597                        qidx);
598 
599                 if (qidx % 2 == 1) {
600                     vubr_handle_tx(dev, qidx);
601                 }
602             }
603             usleep(1000);
604         }
605     }
606 
607     return NULL;
608 }
609 
610 static void
611 vubr_host_notifier_setup(VubrDev *dev)
612 {
613     char template[] = "/tmp/vubr-XXXXXX";
614     pthread_t thread;
615     size_t length;
616     void *addr;
617     int fd;
618 
619     length = getpagesize() * VHOST_MAX_NR_VIRTQUEUE;
620 
621     fd = mkstemp(template);
622     if (fd < 0) {
623         vubr_die("mkstemp()");
624     }
625 
626     if (posix_fallocate(fd, 0, length) != 0) {
627         vubr_die("posix_fallocate()");
628     }
629 
630     addr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
631     if (addr == MAP_FAILED) {
632         vubr_die("mmap()");
633     }
634 
635     memset(addr, 0xff, length);
636 
637     if (pthread_create(&thread, NULL, notifier_thread, &dev->vudev) != 0) {
638         vubr_die("pthread_create()");
639     }
640 
641     dev->notifier.fd = fd;
642     dev->notifier.addr = addr;
643     dev->notifier.thread = thread;
644 }
645 
646 static void
647 vubr_set_host(struct sockaddr_in *saddr, const char *host)
648 {
649     if (qemu_isdigit(host[0])) {
650         if (!inet_aton(host, &saddr->sin_addr)) {
651             fprintf(stderr, "inet_aton() failed.\n");
652             exit(1);
653         }
654     } else {
655         struct hostent *he = gethostbyname(host);
656 
657         if (!he) {
658             fprintf(stderr, "gethostbyname() failed.\n");
659             exit(1);
660         }
661         saddr->sin_addr = *(struct in_addr *)he->h_addr;
662     }
663 }
664 
665 static void
666 vubr_backend_udp_setup(VubrDev *dev,
667                        const char *local_host,
668                        const char *local_port,
669                        const char *remote_host,
670                        const char *remote_port)
671 {
672     int sock;
673     const char *r;
674 
675     int lport, rport;
676 
677     lport = strtol(local_port, (char **)&r, 0);
678     if (r == local_port) {
679         fprintf(stderr, "lport parsing failed.\n");
680         exit(1);
681     }
682 
683     rport = strtol(remote_port, (char **)&r, 0);
684     if (r == remote_port) {
685         fprintf(stderr, "rport parsing failed.\n");
686         exit(1);
687     }
688 
689     struct sockaddr_in si_local = {
690         .sin_family = AF_INET,
691         .sin_port = htons(lport),
692     };
693 
694     vubr_set_host(&si_local, local_host);
695 
696     /* setup destination for sends */
697     dev->backend_udp_dest = (struct sockaddr_in) {
698         .sin_family = AF_INET,
699         .sin_port = htons(rport),
700     };
701     vubr_set_host(&dev->backend_udp_dest, remote_host);
702 
703     sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
704     if (sock == -1) {
705         vubr_die("socket");
706     }
707 
708     if (bind(sock, (struct sockaddr *)&si_local, sizeof(si_local)) == -1) {
709         vubr_die("bind");
710     }
711 
712     dev->backend_udp_sock = sock;
713     dispatcher_add(&dev->dispatcher, sock, dev, vubr_backend_recv_cb);
714     DPRINT("Waiting for data from udp backend on %s:%d...\n",
715            local_host, lport);
716 }
717 
718 static void
719 vubr_run(VubrDev *dev)
720 {
721     while (!dev->quit) {
722         /* timeout 200ms */
723         dispatcher_wait(&dev->dispatcher, 200000);
724         /* Here one can try polling strategy. */
725     }
726 }
727 
728 static int
729 vubr_parse_host_port(const char **host, const char **port, const char *buf)
730 {
731     char *p = strchr(buf, ':');
732 
733     if (!p) {
734         return -1;
735     }
736     *p = '\0';
737     *host = strdup(buf);
738     *port = strdup(p + 1);
739     return 0;
740 }
741 
742 #define DEFAULT_UD_SOCKET "/tmp/vubr.sock"
743 #define DEFAULT_LHOST "127.0.0.1"
744 #define DEFAULT_LPORT "4444"
745 #define DEFAULT_RHOST "127.0.0.1"
746 #define DEFAULT_RPORT "5555"
747 
748 static const char *ud_socket_path = DEFAULT_UD_SOCKET;
749 static const char *lhost = DEFAULT_LHOST;
750 static const char *lport = DEFAULT_LPORT;
751 static const char *rhost = DEFAULT_RHOST;
752 static const char *rport = DEFAULT_RPORT;
753 
754 int
755 main(int argc, char *argv[])
756 {
757     VubrDev *dev;
758     int opt;
759     bool client = false;
760     bool host_notifier = false;
761 
762     while ((opt = getopt(argc, argv, "l:r:u:cH")) != -1) {
763 
764         switch (opt) {
765         case 'l':
766             if (vubr_parse_host_port(&lhost, &lport, optarg) < 0) {
767                 goto out;
768             }
769             break;
770         case 'r':
771             if (vubr_parse_host_port(&rhost, &rport, optarg) < 0) {
772                 goto out;
773             }
774             break;
775         case 'u':
776             ud_socket_path = strdup(optarg);
777             break;
778         case 'c':
779             client = true;
780             break;
781         case 'H':
782             host_notifier = true;
783             break;
784         default:
785             goto out;
786         }
787     }
788 
789     DPRINT("ud socket: %s (%s)\n", ud_socket_path,
790            client ? "client" : "server");
791     DPRINT("local:     %s:%s\n", lhost, lport);
792     DPRINT("remote:    %s:%s\n", rhost, rport);
793 
794     dev = vubr_new(ud_socket_path, client);
795     if (!dev) {
796         return 1;
797     }
798 
799     if (host_notifier) {
800         vubr_host_notifier_setup(dev);
801     }
802 
803     vubr_backend_udp_setup(dev, lhost, lport, rhost, rport);
804     vubr_run(dev);
805 
806     vu_deinit(&dev->vudev);
807 
808     return 0;
809 
810 out:
811     fprintf(stderr, "Usage: %s ", argv[0]);
812     fprintf(stderr, "[-c] [-H] [-u ud_socket_path] [-l lhost:lport] [-r rhost:rport]\n");
813     fprintf(stderr, "\t-u path to unix doman socket. default: %s\n",
814             DEFAULT_UD_SOCKET);
815     fprintf(stderr, "\t-l local host and port. default: %s:%s\n",
816             DEFAULT_LHOST, DEFAULT_LPORT);
817     fprintf(stderr, "\t-r remote host and port. default: %s:%s\n",
818             DEFAULT_RHOST, DEFAULT_RPORT);
819     fprintf(stderr, "\t-c client mode\n");
820     fprintf(stderr, "\t-H use host notifier\n");
821 
822     return 1;
823 }
824