xref: /illumos-gate/usr/src/cmd/connstat/connstat_mib.c (revision 8a2b682e57a046b828f37bcde1776f131ef4629f)
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 }
178