VND_FRAMEIO_READ(3VND)                                    VND_FRAMEIO_READ(3VND)
NAME
       vnd_frameio_read, vnd_frameio_write - perform framed I/O to a vnd device
SYNOPSIS
       cc [ flag... ] file... -lvnd [ library... ]
       #include <libvnd.h>
       int vnd_frameio_read(vnd_handle_t *vhp, frameio_t *fiop);
       int vnd_frameio_write(vnd_handle_t *vhp, frameio_t *fiop);
DESCRIPTION
       Framed I/O is a general means to manipulate data that is inherently
       framed, meaning that there is a maximum frame size, but the data may
       often be less than that size. As an example, an Ethernet device's MTU
       describes the maximum frame size, but the size of an individual frame is
       often much less. You can read a single frame at a time, or you can read
       multiple frames in a single call.
       In addition, framed I/O allows the consumer to break individual frames
       into a series of vectors. This is analogous to the use of an 
iovec(9S)       with 
readv(2) and 
writev(2).
       vnd_frameio_read performs a framed I/O read of the device represented by
       the handle vhp, with the framed I/O data described by fiop.
       vnd_frameio_write works in the same manner, except performing a write
       instead of a read.
       The basic vector component of the frameio_t is the framevec_t. Each
       framevec_t represents a single vector entry. An array of these is present
       in the frameio_t. The framevec_t structure has the following members:
         void      *fv_buf        /* data buffer */
         size_t    fv_buflen;     /* total size of buffer */
         size_t    fv_actlen;     /* amount of buffer consumed */
       The fv_buf member points to a buffer which contains the data for this
       individual vector. When reading, data is consumed from fv_buf. When
       writing, data is written into fv_buf.
       The fv_buflen should indicate the total amount of data that is in the
       buffer. When reading, it indicates the size of the buffer. It must be set
       prior to calling vnd_frameio_read(). When writing, it indicates the
       amount of data that is valid in the buffer.
       The fv_actlen is a read-only member. It is set on successful return of
       the functions vnd_frameio_read and vnd_frameio_write. When reading, it is
       updated with the amount of data that was read into fv_buf. When writing,
       it is instead updated with the amount of data from fv_buf that was
       actually consumed. Generally when writing data, a framevec_t will either
       be entirely consumed or it will not be consumed at all.
       A series of framevec_t's is encapsulated in a frameio_t. The frameio_t
       structure has the following members:
         uint_t         fio_version;   /* current version */
         uint_t         fio_nvpf;      /* number of vectors in one frame */
         uint_t         fio_nvecs;     /* The total number of vectors */
         framevec_t     fio_vecs[];    /* vectors */
       The fio_version member represents the current version of the frameio_t.
       The fio_version should be set to the macro FRAMEIO_CURRENT_VERSION, which
       is currently 1.
       The members fio_nvpf and fio_nvecs describe the number of frames that
       exist. fio_nvecs describes the total number of vectors that are present
       in fio_vecs.  The upper bound on this is described by FRAMEIO_NVECS_MAX
       which is currently 32. fio_nvpf describe the number of vectors that
       should be used to make up each frame. By setting fio_vecs to be an even
       multiple of fio_nvpf, multiple frames can be read or written in a single
       call.
       After a call to vnd_frameio_read or vnd_frameio_write fio_nvecs is
       updated with total number of vectors read or written to. This value can
       be divided by fio_nvpf to determine the total number of frames that were
       written or read.
       Each frame can be broken down into a series of multiple vectors. As an
       example, someone might want to break Ethernet frames into mac headers and
       payloads. The value of fio_nvpf would be set to two, to indicate that a
       single frame consists of two different vector components. The member
       fio_nvecs describes the total number of frames. As such, the value of
       fio_vecs divided by fio_nvpf describes the total number of frames that
       can be consumed in one call. As a result of this, fio_nvpf must evenly
       divide fio_vecs. If fio_nvpf is set to two and fio_nvecs is set to ten,
       then a total of five frames can be processed at once, each frame being
       broken down into two different vector components.
       A given frame will never overflow the number of vectors described by
       fio_nvpf. Consider the case where each vector component has a buffer
       sized to 1518 bytes, fio_nvpf is set to one, and fio_nvecs is set to
       three. If a call to vnd_frameio_read is made and four 500 byte Ethernet
       frames come in, then each frame will be mapped to a single vector. The
       500 bytes will be copied into fio_nvecs[i]->fio_buf and
       fio_nvecs[i]->fio_actlen will be set to 500. To contrast this, if       
readv(2) had been called, the first three frames would all be in the
       first iov and the fourth frame's first eight bytes would be in the first
       iov and the remaining in the second.
       The user must properly initialize fio_nvecs framevec_t's worth of the
       fio_vecs array. When multiple vectors comprise a frame, fv_buflen data is
       consumed before moving onto the next vector. Consider the case where the
       user wants to break a vector into three different components, an 18 byte
       vector for an Ethernet VLAN header, a 20 byte vector for an IPv4 header,
       and a third 1500 byte vector for the remaining payload. If a frame was
       received that only had 30 bytes, then the first 18 bytes would fill up
       the first vector, the remaining 12 bytes would fill up the IPv4 header.
       If instead a 524 byte frame came in, then the first 18 bytes would be
       placed in the first vector, the next 24 bytes would be placed in the next
       vector, and the remaining 500 bytes in the third.
       The functions vnd_frameio_read and vnd_frameio_write operate in both
       blocking and non-blocking mode. If either O_NONBLOCK or O_NDELAY have
       been set on the file descriptor, then the I/O will behave in non-blocking
       mode. When in non-blocking mode, if no data is available when
       vnd_frameio_read is called, EAGAIN is returned. When vnd_frameio_write is
       called in non-blocking mode, if sufficient buffer space to hold all of
       the output frames is not available, then vnd_frameio_write will return
       EAGAIN. To know when the given vnd device has sufficient space, the
       device fires POLLIN/POLLRDNORM when data is available for read and
       POLLOUT/POLLRDOUT when space in the buffer has opened up for write. These
       events can be watched for through 
port_associate(3C) and similar routines
       with a file descriptor returned from 
vnd_polfd(3VND).
       When non-blocking mode is disabled, calls to vnd_frameio_read will block
       until some amount of data is available. Calls to vnd_frameio_write will
       block until sufficient buffer space is available.
       Similar to 
read(2) and 
write(2), vnd_frameio_read and vnd_frameio_write
       make no guarantees about the ordering of data when multiple threads
       simultaneously call the interface. While the data itself will be atomic,
       the ordering of multiple simultaneous calls is not defined.
RETURN VALUES
       The vnd_frameio_read function returns zero on success. The member
       fio_nvecs of fiop is updated with the total number of vectors that had
       data read into them. Each updated framevec_t will have the buffer pointed
       to by fv_buf filled in with data, and fv_actlen will be updated with the
       amount of valid data in fv_buf.
       The vnd_frameio_write function returns zero on success. The member
       fio_nvecs of fiop is updated with the total number of vectors that were
       written out to the underlying datalink. The fv_actlen of each vector is
       updated to indicate the amount of data that was written from that buffer.
       On failure, both vnd_frameio_read and vnd_frameio_write return -1. The
       vnd and system error numbers are updated and available via       
vnd_errno(3VND) and 
vnd_syserrno(3VND). See ERRORS below for a list of
       errors and their meaning.
ERRORS
       The functions vnd_frameio_read and vnd_frameio_write always set the vnd
       error to VND_E_SYS. The following system errors will be encountered:
       EAGAIN
                 Insufficient system memory was available for the operation.
                 Non-blocking mode was enabled and during the call to
                 vnd_frameio_read, no data was available. Non-blocking mode was
                 enabled and during the call to vnd_frameio_write, insufficient
                 buffer space was available.
       ENXIO
                 The vnd device referred to by vhp is not currently attached to
                 an underlying data link and cannot send data.
       EFAULT
                 The fiop argument points to an illegal address or the fv_buf
                 members of the framevec_t's associated with the fiop member
                 fio_vecs point to illegal addresses.
       EINVAL
                 The fio_version member of fiop was unknown, the number of
                 vectors specified by fio_nvecs is zero or greater than
                 FRAMEIO_NVECS_MAX, fio_nvpf equals zero, fio_nvecs is not
                 evenly divisible by fio_nvpf, or a buffer in fio_vecs[] has set
                 fv_buf or fv_buflen to zero.
       EINTR
                 A signal was caught during vnd_frameio_read or
                 vnd_frameio_write, and no data was transferred.
       EOVERFLOW
                 During vnd_frameio_read, the size of a frame specified by
                 fiop->fio_nvpf and fiop->fio_vecs[].fv_buflen cannot contain a
                 frame.
                 In a ILP32 environment, more data than UINT_MAX would be set in
                 fv_actlen.
       ERANGE
                 During vnd_frameio_write, the size of a frame is less than the
                 device's minimum transmission unit or it is larger than the
                 size of the maximum transmission unit.
EXAMPLES
       Example 1    Read a single frame with a single vector
       The following sample C program opens an existing vnd device named "vnd0"
       in the current zone and performs a blocking read of a single frame from
       it.
         #include <libvnd.h>
         #include <stdio.h>
         int
         main(void)
         {
              vnd_handle_t *vhp;
              vnd_errno_t vnderr;
              int syserr, i;
              frameio_t *fiop;
              fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t));
              if (fiop == NULL) {
                   perror("malloc frameio_t");
                   return (1);
              }
              fiop->fio_version = FRAMEIO_CURRENT_VERSION;
              fiop->fio_nvpf = 1;
              fiop->fio_nvecs = 1;
              fiop->fio_vecs[0].fv_buf = 
malloc(1518);
              fiop->fio_vecs[0].fv_buflen = 1518;
              if (fiop->fio_vecs[0].fv_buf == NULL) {
                   perror("malloc framevec_t.fv_buf");
                   free(fiop);
                   return (1);
              }
              vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
              if (vhp != NULL) {
                   if (vnderr == VND_E_SYS)
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strsyserror(syserr));
                   else
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strerror(vnderr));
                   free(fiop->fio_vecs[0].fv_buf);
                   free(fiop);
                   return (1);
              }
              if (frameio_read(vhp, fiop) != 0) {
                   vnd_errno_t vnderr = vnd_errno(vhp);
                   int syserr = vnd_syserrno(vhp);
                   /* Most consumers should retry on EINTR */
                   if (vnderr == VND_E_SYS)
                        (void) fprintf(stderr, "failed to read: %s",
                            vnd_strsyserror(syserr));
                   else
                        (void) fprintf(stderr, "failed to read: %s",
                            vnd_strerror(vnderr));
                   vnd_close(vhp);
                   free(fiop->fio_vecs[0].fv_buf);
                   free(fiop);
                   return (1);
              }
              /* Consume the data however it's desired */
              (void) printf("received %d bytes0, fiop->fio_vecs[0].fv_actlen);
              for (i = 0; i < fiop->fio_vecs[0].fv_actlen)
                   (void) printf("%x ", fiop->fio_vecs[0].fv_buf[i]);
              vnd_close(vhp);
              free(fiop->fio_vecs[0].fv_buf);
              free(viop);
              return (0);
         }
       Example 2    Write a single frame with a single vector
       The following sample C program opens an existing vnd device named "vnd0"
       in the current zone and performs a blocking write of a single frame to
       it.
         #include <libvnd.h>
         #include <stdio.h>
         #include <string.h>
         int
         main(void)
         {
              vnd_handle_t *vhp;
              vnd_errno_t vnderr;
              int syserr;
              frameio_t *fiop;
              fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t));
              if (fiop == NULL) {
                   perror("malloc frameio_t");
                   return (1);
              }
              fiop->fio_version = FRAMEIO_CURRENT_VERSION;
              fiop->fio_nvpf = 1;
              fiop->fio_nvecs = 1;
              fiop->fio_vecs[0].fv_buf = 
malloc(1518);
              if (fiop->fio_vecs[0].fv_buf == NULL) {
                   perror("malloc framevec_t.fv_buf");
                   free(fiop);
                   return (1);
              }
              /*
               * Fill in your data however you desire. This is an entirely
               * invalid frame and while the frameio write may succeed, the
               * networking stack will almost certainly drop it.
               */
              (void) memset(fiop->fio_vecs[0].fv_buf, 'r', 1518);
              fiop->fio_vecs[0].fv_buflen = 1518;
              vhp = vnd_open(NULL, "vnd0", &vnderr, &syserr);
              if (vhp != NULL) {
                   if (vnderr == VND_E_SYS)
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strsyserror(syserr));
                   else
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strerror(vnderr));
                   free(fiop->fio_vecs[0].fv_buf);
                   free(fiop);
                   return (1);
              }
              if (frameio_write(vhp, fiop) != 0) {
                   /* Most consumers should retry on EINTR */
                   if (vnderr == VND_E_SYS)
                        (void) fprintf(stderr, "failed to write: %s",
                            vnd_strsyserror(syserr));
                   else
                        (void) fprintf(stderr, "failed to write: %s",
                            vnd_strerror(vnderr));
                   vnd_close(vhp);
                   free(fiop->fio_vecs[0].fv_buf);
                   free(fiop);
                   return (1);
              }
              (void) printf("wrote %d bytes0, fiop->fio_vecs[0].fv_actlen);
              vnd_close(vhp);
              free(fiop->fio_vecs[0].fv_buf);
              free(viop);
              return (0);
         }
       Example 3    Read frames comprised of multiple vectors
       The following sample C program is similar to example 1, except instead of
       reading a single frame consisting of a single vector it reads multiple
       frames consisting of two vectors. The first vector has room for an 18
       byte VLAN enabled Ethernet header and the second vector has room for a
       1500 byte payload.
         #include <libvnd.h>
         #include <stdio.h>
         int
         main(void)
         {
              vnd_handle_t *vhp;
              vnd_errno_t vnderr;
              int syserr, i, nframes;
              frameio_t *fiop;
              /* Allocate enough framevec_t's for 5 frames */
              fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t) * 10);
              if (fiop == NULL) {
                   perror("malloc frameio_t");
                   return (1);
              }
              fiop->fio_version = FRAMEIO_CURRENT_VERSION;
              fiop->fio_nvpf = 2;
              fiop->fio_nvecs = 10;
              for (i = 0; i < 10; i += 2) {
                   fiop->fio_vecs[i].fv_buf = 
malloc(18);
                   fiop->fio_vecs[i].fv_buflen = 18;
                   if (fiop->fio_vecs[i].fv_buf == NULL) {
                        perror("malloc framevec_t.fv_buf");
                        /* Perform appropriate memory cleanup */
                        return (1);
                   }
                   fiop->fio_vecs[i+1].fv_buf = 
malloc(1500);
                   fiop->fio_vecs[i+1].fv_buflen = 1500;
                   if (fiop->fio_vecs[i+1].fv_buf == NULL) {
                        perror("malloc framevec_t.fv_buf");
                        /* Perform appropriate memory cleanup */
                        return (1);
                   }
              }
              vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
              if (vhp != NULL) {
                   if (vnderr == VND_E_SYS)
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strsyserror(syserr));
                   else
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strerror(vnderr));
                   /* Perform appropriate memory cleanup */
                   return (1);
              }
              if (frameio_read(vhp, fiop) != 0) {
                   /* Most consumers should retry on EINTR */
                   if (vnderr == VND_E_SYS)
                        (void) fprintf(stderr, "failed to read: %s",
                            vnd_strsyserror(syserr));
                   else
                        (void) fprintf(stderr, "failed to read: %s",
                            vnd_strerror(vnderr));
                   vnd_close(vhp);
                   /* Perform appropriate memory cleanup */
                   return (1);
              }
              /* Consume the data however it's desired */
              nframes = fiop->fio_nvecs / fiop->fio_nvpf;
              (void) printf("consumed %d frames!0, nframes);
              for (i = 0; i < nframes; i++) {
                   (void) printf("received %d bytes of Ethernet Header0,
                       fiop->fio_vecs[i].fv_actlen);
                   (void) printf("received %d bytes of payload0,
                       fiop->fio_vecs[i+1].fv_actlen);
              }
              vnd_close(vhp);
              /* Do proper memory cleanup */
              return (0);
         }
       Example 4    Perform non-blocking reads of multiple frames with a single
       vector
       In this sample C program, opens an existing vnd device named "vnd0" in
       the current zone, ensures that it is in non-blocking mode, and uses event
       ports to do device reads.
         #include <libvnd.h>
         #include <stdio.h>
         #include <port.h>
         #include <unistd.h>
         #include <errno.h>
         #include <sys/tpyes.h>
         #include <fcntl.h>
         int
         main(void)
         {
              vnd_handle_t *vhp;
              vnd_errno_t vnderr;
              int syserr, i, nframes, port, vfd;
              frameio_t *fiop;
              port = port_create();
              if (port < 0) {
                   perror("port_create");
                   return (1);
              }
              /* Allocate enough framevec_t's for 10 frames */
              fiop = malloc(sizeof (frameio_t) + sizeof (framevec_t) * 10);
              if (fiop == NULL) {
                   perror("malloc frameio_t");
                   (void) close(port);
                   return (1);
              }
              fiop->fio_version = FRAMEIO_CURRENT_VERSION;
              fiop->fio_nvpf = 1;
              fiop->fio_nvecs = 10;
              for (i = 0; i < 10; i++) {
                   fiop->fio_vecs[i].fv_buf = 
malloc(1518);
                   fiop->fio_vecs[i].fv_buflen = 1518;
                   if (fiop->fio_vecs[i].fv_buf == NULL) {
                        perror("malloc framevec_t.fv_buf");
                        /* Perform appropriate memory cleanup */
                        (void) close(port);
                        return (1);
                   }
              }
              vhp = vnd_open(NULL, "vnd1", &vnderr, &syserr);
              if (vhp != NULL) {
                   if (vnderr == VND_E_SYS)
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strsyserror(syserr));
                   else
                        (void) fprintf(stderr, "failed to open device: %s",
                            vnd_strerror(vnderr));
                   /* Perform appropriate memory cleanup */
                   (void) close(port);
                   return (1);
              }
              vfd = vnd_pollfd(vhp);
              if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) {
                   (void) fprintf(stderr, "failed to enable non-blocking mode: %s",
                       strerrror(errno));
              }
              for (;;) {
                   port_event_t pe;
                   if (port_associate(port, PORT_SOURCE_FD, vfd, POLLIN,
                       vhp) != 0) {
                        perror("port_associate");
                        vnd_close(vhp);
                        /* Perform appropriate memory cleanup */
                        (void) close(port);
                        return (1);
                   }
                   if (port_get(port, &pe, NULL) != 0) {
                        if (errno == EINTR)
                             continue;
                        perror("port_associate");
                        vnd_close(vhp);
                        /* Perform appropriate memory cleanup */
                        (void) close(port);
                        return (1);
                   }
                   /*
                    * Most real applications will need to compare the file
                    * descriptor and switch on it. In this case, assume
                    * that the fd in question that is readable is 'vfd'.
                    */
                   if (frameio_read(pe.portev_user, fiop) != 0) {
                        vnd_errno_t vnderr = vnd_errno(vhp);
                        int syserr = vnd_syserrno(vhp);
                        if (vnderr == VND_E_SYS && (syserr == EINTR ||
                            syserr == EAGAIN))
                             continue;
                        (void) fprintf(stderr, "failed to get read: %s",
                            vnd_strsyserror(vnderr));
                        vnd_close(vhp);
                        /* Perform appropriate memory cleanup */
                        (void) close(port);
                        return (1);
                   }
                   /* Consume the data however it's desired */
                   nframes = fiop->fio_nvecs / fiop->fio_nvpf;
                   for (i = 0; i < nframes; i++) {
                        (void) printf("frame %d is %d bytes large0, i,
                            fiop->fio_vecs[i].fv_actlen);
                   }
              }
              vnd_close(vhp);
              /* Do proper memory cleanup */
              return (0);
         }
ATTRIBUTES
       See 
attributes(5) for descriptions of the following attributes:
       +---------------+---------------------------------+
       |ATTRIBUTE TYPE |         ATTRIBUTE VALUE         |
       +---------------+---------------------------------+
       |Stability      | Committed                       |
       +---------------+---------------------------------+
       |MT-Level       | See "THREADING" in 
libvnd(3LIB) |
       +---------------+---------------------------------+
SEE ALSO
       Intro(2), 
getmsg(2), 
read(2), 
readv(2), 
write(2), 
writev(2),       
libvnd(3VND), 
vnd_errno(3VND), 
vnd_pollfd(3VND), 
vnd_syserrno(3VND),       
iovec(9S)                                  March 6, 2014           
VND_FRAMEIO_READ(3VND)