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
31int
32mibopen(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
52int
53conn_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