1 /*
   2  * CDDL HEADER START
   3  *
   4  * This file and its contents are supplied under the terms of the
   5  * Common Development and Distribution License ("CDDL"), version 1.0.
   6  * You may only use this file in accordance with the terms of version
   7  * 1.0 of the CDDL.
   8  *
   9  * A full copy of the text of the CDDL should have accompanied this
  10  * source.  A copy of the CDDL is also available via the Internet at
  11  * http://www.illumos.org/license/CDDL.
  12  *
  13  * CDDL HEADER END
  14  */
  15 /*
  16  * Copyright (c) 2015 by Delphix. All rights reserved.
  17  */
  18 
  19 #include <err.h>
  20 #include <stdio.h>
  21 #include <stdlib.h>
  22 #include <errno.h>
  23 #include <fcntl.h>
  24 #include <strings.h>
  25 #include <unistd.h>
  26 #include <stropts.h>
  27 #include <sys/debug.h>
  28 #include <sys/tihdr.h>
  29 #include "connstat.h"
  30 
  31 int
  32 mibopen(const char *proto)
  33 {
  34         int saved;
  35         int fd;
  36 
  37         fd = open("/dev/arp", O_RDWR);
  38         if (fd == -1) {
  39                 return (-1);
  40         }
  41 
  42         if (ioctl(fd, I_PUSH, proto) == -1) {
  43                 saved = errno;
  44                 (void) close(fd);
  45                 errno = saved;
  46                 return (-1);
  47         }
  48 
  49         return (fd);
  50 }
  51 
  52 int
  53 conn_walk(int fd, connstat_proto_t *proto, conn_walk_state_t *state)
  54 {
  55         struct strbuf cbuf, dbuf;
  56         struct opthdr *hdr;
  57         int flags, r, err = 0;
  58         struct {
  59                 struct T_optmgmt_req req;
  60                 struct opthdr hdr;
  61         } req;
  62         union {
  63                 struct T_optmgmt_ack ack;
  64                 uint8_t space[sizeof (struct T_optmgmt_ack) +
  65                     sizeof (struct opthdr) * 2];
  66         } ack;
  67 
  68         bzero(&cbuf, sizeof (cbuf));
  69         bzero(&dbuf, sizeof (dbuf));
  70 
  71         req.req.PRIM_type = T_OPTMGMT_REQ;
  72         req.req.OPT_offset = (caddr_t)&req.hdr - (caddr_t)&req;
  73         req.req.OPT_length = sizeof (req.hdr);
  74         req.req.MGMT_flags = T_CURRENT;
  75 
  76         req.hdr.level = proto->csp_miblevel;
  77         req.hdr.name = 0;
  78         req.hdr.len = 0;
  79 
  80         cbuf.buf = (caddr_t)&req;
  81         cbuf.len = sizeof (req);
  82 
  83         if (putmsg(fd, &cbuf, NULL, 0) == -1) {
  84                 warn("failed to request connection info: putmsg");
  85                 return (-1);
  86         }
  87 
  88         /*
  89          * Each reply consists of a control part for one fixed structure or
  90          * table, as defined in mib2.h.  The format is a T_OPTMGMT_ACK
  91          * containing an opthdr structure.  The level and name identify the
  92          * entry, and len is the size of the data part of the message.
  93          */
  94         for (;;) {
  95                 cbuf.buf = (caddr_t)&ack;
  96                 cbuf.maxlen = sizeof (ack);
  97                 flags = 0;
  98 
  99                 /*
 100                  * We first do a getmsg() for the control part so that we
 101                  * can allocate a properly sized buffer to read the data
 102                  * part.
 103                  */
 104                 do {
 105                         r = getmsg(fd, &cbuf, NULL, &flags);
 106                 } while (r < 0 && errno == EINTR);
 107 
 108                 if (r < 0) {
 109                         warn("failed to fetch further connection info");
 110                         err = -1;
 111                         break;
 112                 } else if ((r & MORECTL) != 0) {
 113                         warnx("failed to fetch full control message");
 114                         err = -1;
 115                         break;
 116                 }
 117 
 118                 if (cbuf.len < sizeof (struct T_optmgmt_ack) ||
 119                     ack.ack.PRIM_type != T_OPTMGMT_ACK ||
 120                     ack.ack.MGMT_flags != T_SUCCESS ||
 121                     ack.ack.OPT_length < sizeof (struct opthdr)) {
 122                         warnx("cannot process invalid message from getmsg()");
 123                         err = -1;
 124                         break;
 125                 }
 126 
 127                 /* LINTED E_BAD_PTR_CAST_ALIGN */
 128                 hdr = (struct opthdr *)((caddr_t)&ack + ack.ack.OPT_offset);
 129                 if (r == 0 && hdr->level == 0 && hdr->name == 0) {
 130                         /*
 131                          * snmpcom_req() has sent us the final End-Of-Data
 132                          * message, so there's nothing further to read.
 133                          */
 134                         break;
 135                 }
 136 
 137                 /* Only data should remain. */
 138                 VERIFY3S(r, ==, MOREDATA);
 139 
 140                 /* Allocate a buffer to hold the data portion of the message */
 141                 if ((dbuf.buf = realloc(dbuf.buf, hdr->len)) == NULL) {
 142                         warn("failed to realloc() buffer");
 143                         err = -1;
 144                         break;
 145                 }
 146                 dbuf.maxlen = hdr->len;
 147                 dbuf.len = 0;
 148                 flags = 0;
 149 
 150                 do {
 151                         r = getmsg(fd, NULL, &dbuf, &flags);
 152                 } while (r < 0 && errno == EINTR);
 153 
 154                 if (r < 0) {
 155                         warn("failed to fetch connection data: getmsg()");
 156                         err = -1;
 157                         break;
 158                 } else if (r != 0) {
 159                         warnx("failed to fetch all data: "
 160                             "getmsg() returned %d", r);
 161                         err = -1;
 162                         break;
 163                 }
 164 
 165                 if ((state->cws_flags & CS_IPV4) &&
 166                     hdr->name == proto->csp_mibv4name) {
 167                         proto->csp_v4walk(&dbuf, state);
 168                 } else if ((state->cws_flags & CS_IPV6) &&
 169                     hdr->name == proto->csp_mibv6name) {
 170                         proto->csp_v6walk(&dbuf, state);
 171                 }
 172         }
 173 
 174         free(dbuf.buf);
 175 
 176         return (err);
 177 }