1/*
2 * Copyright (c) 2001-2003
3 *	Fraunhofer Institute for Open Communication Systems (FhG Fokus).
4 *	All rights reserved.
5 *
6 * Author: Harti Brandt <harti@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 *
29 * $Begemot: bsnmp/snmp_mibII/mibII_ip.c,v 1.11 2005/05/23 09:03:40 brandt_h Exp $
30 *
31 * ip group scalars.
32 */
33#include "mibII.h"
34#include "mibII_oid.h"
35#include <netinet/in_systm.h>
36#include <netinet/ip.h>
37#include <netinet/ip_var.h>
38#include <netinet/ip_icmp.h>
39#include <netinet/icmp_var.h>
40
41static struct ipstat ipstat;
42static u_int	ip_idrop;
43static struct icmpstat icmpstat;
44
45static int	ip_forwarding;
46static int	ip_defttl;
47static uint64_t ip_tick;
48
49static uint64_t ipstat_tick;
50
51static int
52fetch_ipstat(void)
53{
54	size_t len;
55
56	len = sizeof(ipstat);
57	if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, NULL, 0) == -1) {
58		syslog(LOG_ERR, "net.inet.ip.stats: %m");
59		return (-1);
60	}
61	if (len != sizeof(ipstat)) {
62		syslog(LOG_ERR, "net.inet.ip.stats: wrong size");
63		return (-1);
64	}
65	len = sizeof(ip_idrop);
66	if (sysctlbyname("net.inet.ip.intr_queue_drops", &ip_idrop, &len, NULL, 0) == -1)
67		syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: %m");
68	if (len != sizeof(ip_idrop)) {
69		syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: wrong size");
70		ip_idrop = 0;
71	}
72	len = sizeof(icmpstat);
73	if (sysctlbyname("net.inet.icmp.stats", &icmpstat, &len, NULL, 0) == -1) {
74		syslog(LOG_ERR, "net.inet.icmp.stats: %m");
75		return (-1);
76	}
77	if (len != sizeof(icmpstat)) {
78		syslog(LOG_ERR, "net.inet.icmp.stats: wrong size");
79		return (-1);
80	}
81
82	ipstat_tick = get_ticks();
83	return (0);
84}
85
86static int
87fetch_ip(void)
88{
89	size_t len;
90
91	len = sizeof(ip_forwarding);
92	if (sysctlbyname("net.inet.ip.forwarding", &ip_forwarding, &len,
93	    NULL, 0) == -1) {
94		syslog(LOG_ERR, "net.inet.ip.forwarding: %m");
95		return (-1);
96	}
97	if (len != sizeof(ip_forwarding)) {
98		syslog(LOG_ERR, "net.inet.ip.forwarding: wrong size");
99		return (-1);
100	}
101
102	len = sizeof(ip_defttl);
103	if (sysctlbyname("net.inet.ip.ttl", &ip_defttl, &len,
104	    NULL, 0) == -1) {
105		syslog(LOG_ERR, "net.inet.ip.ttl: %m");
106		return (-1);
107	}
108	if (len != sizeof(ip_defttl)) {
109		syslog(LOG_ERR, "net.inet.ip.ttl: wrong size");
110		return (-1);
111	}
112
113	ip_tick = get_ticks();
114	return (0);
115}
116
117static int
118ip_forward(int forw, int *old)
119{
120	size_t olen;
121
122	olen = sizeof(*old);
123	if (sysctlbyname("net.inet.ip.forwarding", old, old ? &olen : NULL,
124	    &forw, sizeof(forw)) == -1) {
125		syslog(LOG_ERR, "set net.inet.ip.forwarding: %m");
126		return (-1);
127	}
128	ip_forwarding = forw;
129	return (0);
130}
131
132static int
133ip_setttl(int ttl, int *old)
134{
135	size_t olen;
136
137	olen = sizeof(*old);
138	if (sysctlbyname("net.inet.ip.ttl", old, old ? &olen : NULL,
139	    &ttl, sizeof(ttl)) == -1) {
140		syslog(LOG_ERR, "set net.inet.ip.ttl: %m");
141		return (-1);
142	}
143	ip_defttl = ttl;
144	return (0);
145}
146
147/*
148 * READ/WRITE ip group.
149 */
150int
151op_ip(struct snmp_context *ctx, struct snmp_value *value,
152    u_int sub, u_int idx __unused, enum snmp_op op)
153{
154	int old;
155
156	switch (op) {
157
158	  case SNMP_OP_GETNEXT:
159		abort();
160
161	  case SNMP_OP_GET:
162		break;
163
164	  case SNMP_OP_SET:
165		if (ip_tick < this_tick)
166			if (fetch_ip() == -1)
167				return (SNMP_ERR_GENERR);
168
169		switch (value->var.subs[sub - 1]) {
170
171		  case LEAF_ipForwarding:
172			ctx->scratch->int1 = ip_forwarding ? 1 : 2;
173			ctx->scratch->int2 = value->v.integer;
174			if (value->v.integer == 1) {
175				if (!ip_forwarding && ip_forward(1, &old))
176					return (SNMP_ERR_GENERR);
177				ctx->scratch->int1 = old ? 1 : 2;
178			} else if (value->v.integer == 2) {
179				if (ip_forwarding && ip_forward(0, &old))
180					return (SNMP_ERR_GENERR);
181				ctx->scratch->int1 = old;
182			} else
183				return (SNMP_ERR_WRONG_VALUE);
184			break;
185
186		  case LEAF_ipDefaultTTL:
187			ctx->scratch->int1 = ip_defttl;
188			ctx->scratch->int2 = value->v.integer;
189			if (value->v.integer < 1 || value->v.integer > 255)
190				return (SNMP_ERR_WRONG_VALUE);
191			if (ip_defttl != value->v.integer &&
192			    ip_setttl(value->v.integer, &old))
193				return (SNMP_ERR_GENERR);
194			ctx->scratch->int1 = old;
195			break;
196		}
197		return (SNMP_ERR_NOERROR);
198
199	  case SNMP_OP_ROLLBACK:
200		switch (value->var.subs[sub - 1]) {
201
202		  case LEAF_ipForwarding:
203			if (ctx->scratch->int1 == 1) {
204				if (ctx->scratch->int2 == 2)
205					(void)ip_forward(1, NULL);
206			} else {
207				if (ctx->scratch->int2 == 1)
208					(void)ip_forward(0, NULL);
209			}
210			break;
211
212		  case LEAF_ipDefaultTTL:
213			if (ctx->scratch->int1 != ctx->scratch->int2)
214				(void)ip_setttl(ctx->scratch->int1, NULL);
215			break;
216		}
217		return (SNMP_ERR_NOERROR);
218
219	  case SNMP_OP_COMMIT:
220		return (SNMP_ERR_NOERROR);
221	}
222
223	if (ip_tick < this_tick)
224		if (fetch_ip() == -1)
225			return (SNMP_ERR_GENERR);
226
227	switch (value->var.subs[sub - 1]) {
228
229	  case LEAF_ipForwarding:
230		value->v.integer = ip_forwarding ? 1 : 2;
231		break;
232
233	  case LEAF_ipDefaultTTL:
234		value->v.integer = ip_defttl;
235		break;
236	}
237	return (SNMP_ERR_NOERROR);
238}
239
240/*
241 * READ-ONLY statistics ip group.
242 */
243int
244op_ipstat(struct snmp_context *ctx __unused, struct snmp_value *value,
245    u_int sub, u_int idx __unused, enum snmp_op op)
246{
247	switch (op) {
248
249	  case SNMP_OP_GETNEXT:
250		abort();
251
252	  case SNMP_OP_GET:
253		break;
254
255	  case SNMP_OP_SET:
256		return (SNMP_ERR_NOT_WRITEABLE);
257
258	  case SNMP_OP_ROLLBACK:
259	  case SNMP_OP_COMMIT:
260		abort();
261	}
262
263	if (ipstat_tick < this_tick)
264		fetch_ipstat();
265
266	switch (value->var.subs[sub - 1]) {
267
268	  case LEAF_ipInReceives:
269		value->v.uint32 = ipstat.ips_total;
270		break;
271
272	  case LEAF_ipInHdrErrors:
273		value->v.uint32 = ipstat.ips_badsum + ipstat.ips_tooshort
274		    + ipstat.ips_toosmall + ipstat.ips_badhlen
275		    + ipstat.ips_badlen + ipstat.ips_badvers +
276		    + ipstat.ips_toolong;
277		break;
278
279	  case LEAF_ipInAddrErrors:
280		value->v.uint32 = ipstat.ips_cantforward;
281		break;
282
283	  case LEAF_ipForwDatagrams:
284		value->v.uint32 = ipstat.ips_forward;
285		break;
286
287	  case LEAF_ipInUnknownProtos:
288		value->v.uint32 = ipstat.ips_noproto;
289		break;
290
291	  case LEAF_ipInDiscards:
292		value->v.uint32 = ip_idrop;
293		break;
294
295	  case LEAF_ipInDelivers:
296		value->v.uint32 = ipstat.ips_delivered;
297		break;
298
299	  case LEAF_ipOutRequests:
300		value->v.uint32 = ipstat.ips_localout;
301		break;
302
303	  case LEAF_ipOutDiscards:
304		value->v.uint32 = ipstat.ips_odropped;
305		break;
306
307	  case LEAF_ipOutNoRoutes:
308		value->v.uint32 = ipstat.ips_noroute;
309		break;
310
311	  case LEAF_ipReasmTimeout:
312		value->v.integer = IPFRAGTTL;
313		break;
314
315	  case LEAF_ipReasmReqds:
316		value->v.uint32 = ipstat.ips_fragments;
317		break;
318
319	  case LEAF_ipReasmOKs:
320		value->v.uint32 = ipstat.ips_reassembled;
321		break;
322
323	  case LEAF_ipReasmFails:
324		value->v.uint32 = ipstat.ips_fragdropped
325		    + ipstat.ips_fragtimeout;
326		break;
327
328	  case LEAF_ipFragOKs:
329		value->v.uint32 = ipstat.ips_fragmented;
330		break;
331
332	  case LEAF_ipFragFails:
333		value->v.uint32 = ipstat.ips_cantfrag;
334		break;
335
336	  case LEAF_ipFragCreates:
337		value->v.uint32 = ipstat.ips_ofragments;
338		break;
339	}
340	return (SNMP_ERR_NOERROR);
341}
342
343/*
344 * READ-ONLY statistics icmp group.
345 */
346int
347op_icmpstat(struct snmp_context *ctx __unused, struct snmp_value *value,
348    u_int sub, u_int idx __unused, enum snmp_op op)
349{
350	u_int i;
351
352	switch (op) {
353
354	  case SNMP_OP_GETNEXT:
355		abort();
356
357	  case SNMP_OP_GET:
358		break;
359
360	  case SNMP_OP_SET:
361		return (SNMP_ERR_NOT_WRITEABLE);
362
363	  case SNMP_OP_ROLLBACK:
364	  case SNMP_OP_COMMIT:
365		abort();
366	}
367
368	if (ipstat_tick < this_tick)
369		fetch_ipstat();
370
371	switch (value->var.subs[sub - 1]) {
372
373	  case LEAF_icmpInMsgs:
374		value->v.integer = 0;
375		for (i = 0; i <= ICMP_MAXTYPE; i++)
376			value->v.integer += icmpstat.icps_inhist[i];
377		value->v.integer += icmpstat.icps_tooshort +
378		    icmpstat.icps_checksum;
379		/* missing: bad type and packets on faith */
380		break;
381
382	  case LEAF_icmpInErrors:
383		value->v.integer = icmpstat.icps_tooshort +
384		    icmpstat.icps_checksum +
385		    icmpstat.icps_badlen +
386		    icmpstat.icps_badcode +
387		    icmpstat.icps_bmcastecho +
388		    icmpstat.icps_bmcasttstamp;
389		break;
390
391	  case LEAF_icmpInDestUnreachs:
392		value->v.integer = icmpstat.icps_inhist[ICMP_UNREACH];
393		break;
394
395	  case LEAF_icmpInTimeExcds:
396		value->v.integer = icmpstat.icps_inhist[ICMP_TIMXCEED];
397		break;
398
399	  case LEAF_icmpInParmProbs:
400		value->v.integer = icmpstat.icps_inhist[ICMP_PARAMPROB];
401		break;
402
403	  case LEAF_icmpInSrcQuenchs:
404		value->v.integer = icmpstat.icps_inhist[ICMP_SOURCEQUENCH];
405		break;
406
407	  case LEAF_icmpInRedirects:
408		value->v.integer = icmpstat.icps_inhist[ICMP_REDIRECT];
409		break;
410
411	  case LEAF_icmpInEchos:
412		value->v.integer = icmpstat.icps_inhist[ICMP_ECHO];
413		break;
414
415	  case LEAF_icmpInEchoReps:
416		value->v.integer = icmpstat.icps_inhist[ICMP_ECHOREPLY];
417		break;
418
419	  case LEAF_icmpInTimestamps:
420		value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMP];
421		break;
422
423	  case LEAF_icmpInTimestampReps:
424		value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMPREPLY];
425		break;
426
427	  case LEAF_icmpInAddrMasks:
428		value->v.integer = icmpstat.icps_inhist[ICMP_MASKREQ];
429		break;
430
431	  case LEAF_icmpInAddrMaskReps:
432		value->v.integer = icmpstat.icps_inhist[ICMP_MASKREPLY];
433		break;
434
435	  case LEAF_icmpOutMsgs:
436		value->v.integer = 0;
437		for (i = 0; i <= ICMP_MAXTYPE; i++)
438			value->v.integer += icmpstat.icps_outhist[i];
439		value->v.integer += icmpstat.icps_badaddr +
440		    icmpstat.icps_noroute;
441		break;
442
443	  case LEAF_icmpOutErrors:
444		value->v.integer = icmpstat.icps_badaddr +
445		    icmpstat.icps_noroute;
446		break;
447
448	  case LEAF_icmpOutDestUnreachs:
449		value->v.integer = icmpstat.icps_outhist[ICMP_UNREACH];
450		break;
451
452	  case LEAF_icmpOutTimeExcds:
453		value->v.integer = icmpstat.icps_outhist[ICMP_TIMXCEED];
454		break;
455
456	  case LEAF_icmpOutParmProbs:
457		value->v.integer = icmpstat.icps_outhist[ICMP_PARAMPROB];
458		break;
459
460	  case LEAF_icmpOutSrcQuenchs:
461		value->v.integer = icmpstat.icps_outhist[ICMP_SOURCEQUENCH];
462		break;
463
464	  case LEAF_icmpOutRedirects:
465		value->v.integer = icmpstat.icps_outhist[ICMP_REDIRECT];
466		break;
467
468	  case LEAF_icmpOutEchos:
469		value->v.integer = icmpstat.icps_outhist[ICMP_ECHO];
470		break;
471
472	  case LEAF_icmpOutEchoReps:
473		value->v.integer = icmpstat.icps_outhist[ICMP_ECHOREPLY];
474		break;
475
476	  case LEAF_icmpOutTimestamps:
477		value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMP];
478		break;
479
480	  case LEAF_icmpOutTimestampReps:
481		value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMPREPLY];
482		break;
483
484	  case LEAF_icmpOutAddrMasks:
485		value->v.integer = icmpstat.icps_outhist[ICMP_MASKREQ];
486		break;
487
488	  case LEAF_icmpOutAddrMaskReps:
489		value->v.integer = icmpstat.icps_outhist[ICMP_MASKREPLY];
490		break;
491	}
492	return (SNMP_ERR_NOERROR);
493}
494