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 u_int	ip_fragttl;
48static uint64_t ip_tick;
49
50static uint64_t ipstat_tick;
51
52static int
53fetch_ipstat(void)
54{
55	size_t len;
56
57	len = sizeof(ipstat);
58	if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, NULL, 0) == -1) {
59		syslog(LOG_ERR, "net.inet.ip.stats: %m");
60		return (-1);
61	}
62	if (len != sizeof(ipstat)) {
63		syslog(LOG_ERR, "net.inet.ip.stats: wrong size");
64		return (-1);
65	}
66	len = sizeof(ip_idrop);
67	if (sysctlbyname("net.inet.ip.intr_queue_drops", &ip_idrop, &len, NULL, 0) == -1)
68		syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: %m");
69	if (len != sizeof(ip_idrop)) {
70		syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: wrong size");
71		ip_idrop = 0;
72	}
73	len = sizeof(icmpstat);
74	if (sysctlbyname("net.inet.icmp.stats", &icmpstat, &len, NULL, 0) == -1) {
75		syslog(LOG_ERR, "net.inet.icmp.stats: %m");
76		return (-1);
77	}
78	if (len != sizeof(icmpstat)) {
79		syslog(LOG_ERR, "net.inet.icmp.stats: wrong size");
80		return (-1);
81	}
82
83	len = sizeof(ip_fragttl);
84	if (sysctlbyname("net.inet.ip.fragttl", &ip_fragttl, &len,
85	    NULL, 0) == -1) {
86		syslog(LOG_ERR, "net.inet.ip.fragttl: %m");
87		return (-1);
88	}
89	if (len != sizeof(ip_fragttl)) {
90		syslog(LOG_ERR, "net.inet.ip.fragttl: wrong size");
91		return (-1);
92	}
93
94	ipstat_tick = get_ticks();
95	return (0);
96}
97
98static int
99fetch_ip(void)
100{
101	size_t len;
102
103	len = sizeof(ip_forwarding);
104	if (sysctlbyname("net.inet.ip.forwarding", &ip_forwarding, &len,
105	    NULL, 0) == -1) {
106		syslog(LOG_ERR, "net.inet.ip.forwarding: %m");
107		return (-1);
108	}
109	if (len != sizeof(ip_forwarding)) {
110		syslog(LOG_ERR, "net.inet.ip.forwarding: wrong size");
111		return (-1);
112	}
113
114	len = sizeof(ip_defttl);
115	if (sysctlbyname("net.inet.ip.ttl", &ip_defttl, &len,
116	    NULL, 0) == -1) {
117		syslog(LOG_ERR, "net.inet.ip.ttl: %m");
118		return (-1);
119	}
120	if (len != sizeof(ip_defttl)) {
121		syslog(LOG_ERR, "net.inet.ip.ttl: wrong size");
122		return (-1);
123	}
124
125	ip_tick = get_ticks();
126	return (0);
127}
128
129static int
130ip_forward(int forw, int *old)
131{
132	size_t olen;
133
134	olen = sizeof(*old);
135	if (sysctlbyname("net.inet.ip.forwarding", old, old ? &olen : NULL,
136	    &forw, sizeof(forw)) == -1) {
137		syslog(LOG_ERR, "set net.inet.ip.forwarding: %m");
138		return (-1);
139	}
140	ip_forwarding = forw;
141	return (0);
142}
143
144static int
145ip_setttl(int ttl, int *old)
146{
147	size_t olen;
148
149	olen = sizeof(*old);
150	if (sysctlbyname("net.inet.ip.ttl", old, old ? &olen : NULL,
151	    &ttl, sizeof(ttl)) == -1) {
152		syslog(LOG_ERR, "set net.inet.ip.ttl: %m");
153		return (-1);
154	}
155	ip_defttl = ttl;
156	return (0);
157}
158
159/*
160 * READ/WRITE ip group.
161 */
162int
163op_ip(struct snmp_context *ctx, struct snmp_value *value,
164    u_int sub, u_int idx __unused, enum snmp_op op)
165{
166	int old = 0;
167
168	switch (op) {
169
170	  case SNMP_OP_GETNEXT:
171		abort();
172
173	  case SNMP_OP_GET:
174		break;
175
176	  case SNMP_OP_SET:
177		if (ip_tick < this_tick)
178			if (fetch_ip() == -1)
179				return (SNMP_ERR_GENERR);
180
181		switch (value->var.subs[sub - 1]) {
182
183		  case LEAF_ipForwarding:
184			ctx->scratch->int1 = ip_forwarding ? 1 : 2;
185			ctx->scratch->int2 = value->v.integer;
186			if (value->v.integer == 1) {
187				if (!ip_forwarding && ip_forward(1, &old))
188					return (SNMP_ERR_GENERR);
189				ctx->scratch->int1 = old ? 1 : 2;
190			} else if (value->v.integer == 2) {
191				if (ip_forwarding && ip_forward(0, &old))
192					return (SNMP_ERR_GENERR);
193				ctx->scratch->int1 = old;
194			} else
195				return (SNMP_ERR_WRONG_VALUE);
196			break;
197
198		  case LEAF_ipDefaultTTL:
199			ctx->scratch->int1 = ip_defttl;
200			ctx->scratch->int2 = value->v.integer;
201			if (value->v.integer < 1 || value->v.integer > 255)
202				return (SNMP_ERR_WRONG_VALUE);
203			if (ip_defttl != value->v.integer &&
204			    ip_setttl(value->v.integer, &old))
205				return (SNMP_ERR_GENERR);
206			ctx->scratch->int1 = old;
207			break;
208		}
209		return (SNMP_ERR_NOERROR);
210
211	  case SNMP_OP_ROLLBACK:
212		switch (value->var.subs[sub - 1]) {
213
214		  case LEAF_ipForwarding:
215			if (ctx->scratch->int1 == 1) {
216				if (ctx->scratch->int2 == 2)
217					(void)ip_forward(1, NULL);
218			} else {
219				if (ctx->scratch->int2 == 1)
220					(void)ip_forward(0, NULL);
221			}
222			break;
223
224		  case LEAF_ipDefaultTTL:
225			if (ctx->scratch->int1 != ctx->scratch->int2)
226				(void)ip_setttl(ctx->scratch->int1, NULL);
227			break;
228		}
229		return (SNMP_ERR_NOERROR);
230
231	  case SNMP_OP_COMMIT:
232		return (SNMP_ERR_NOERROR);
233	}
234
235	if (ip_tick < this_tick)
236		if (fetch_ip() == -1)
237			return (SNMP_ERR_GENERR);
238
239	switch (value->var.subs[sub - 1]) {
240
241	  case LEAF_ipForwarding:
242		value->v.integer = ip_forwarding ? 1 : 2;
243		break;
244
245	  case LEAF_ipDefaultTTL:
246		value->v.integer = ip_defttl;
247		break;
248	}
249	return (SNMP_ERR_NOERROR);
250}
251
252/*
253 * READ-ONLY statistics ip group.
254 */
255int
256op_ipstat(struct snmp_context *ctx __unused, struct snmp_value *value,
257    u_int sub, u_int idx __unused, enum snmp_op op)
258{
259	switch (op) {
260
261	  case SNMP_OP_GETNEXT:
262		abort();
263
264	  case SNMP_OP_GET:
265		break;
266
267	  case SNMP_OP_SET:
268		return (SNMP_ERR_NOT_WRITEABLE);
269
270	  case SNMP_OP_ROLLBACK:
271	  case SNMP_OP_COMMIT:
272		abort();
273	}
274
275	if (ipstat_tick < this_tick)
276		fetch_ipstat();
277
278	switch (value->var.subs[sub - 1]) {
279
280	  case LEAF_ipInReceives:
281		value->v.uint32 = ipstat.ips_total;
282		break;
283
284	  case LEAF_ipInHdrErrors:
285		value->v.uint32 = ipstat.ips_badsum + ipstat.ips_tooshort
286		    + ipstat.ips_toosmall + ipstat.ips_badhlen
287		    + ipstat.ips_badlen + ipstat.ips_badvers +
288		    + ipstat.ips_toolong;
289		break;
290
291	  case LEAF_ipInAddrErrors:
292		value->v.uint32 = ipstat.ips_cantforward;
293		break;
294
295	  case LEAF_ipForwDatagrams:
296		value->v.uint32 = ipstat.ips_forward;
297		break;
298
299	  case LEAF_ipInUnknownProtos:
300		value->v.uint32 = ipstat.ips_noproto;
301		break;
302
303	  case LEAF_ipInDiscards:
304		value->v.uint32 = ip_idrop;
305		break;
306
307	  case LEAF_ipInDelivers:
308		value->v.uint32 = ipstat.ips_delivered;
309		break;
310
311	  case LEAF_ipOutRequests:
312		value->v.uint32 = ipstat.ips_localout;
313		break;
314
315	  case LEAF_ipOutDiscards:
316		value->v.uint32 = ipstat.ips_odropped;
317		break;
318
319	  case LEAF_ipOutNoRoutes:
320		value->v.uint32 = ipstat.ips_noroute;
321		break;
322
323	  case LEAF_ipReasmTimeout:
324		value->v.integer = ip_fragttl;
325		break;
326
327	  case LEAF_ipReasmReqds:
328		value->v.uint32 = ipstat.ips_fragments;
329		break;
330
331	  case LEAF_ipReasmOKs:
332		value->v.uint32 = ipstat.ips_reassembled;
333		break;
334
335	  case LEAF_ipReasmFails:
336		value->v.uint32 = ipstat.ips_fragdropped
337		    + ipstat.ips_fragtimeout;
338		break;
339
340	  case LEAF_ipFragOKs:
341		value->v.uint32 = ipstat.ips_fragmented;
342		break;
343
344	  case LEAF_ipFragFails:
345		value->v.uint32 = ipstat.ips_cantfrag;
346		break;
347
348	  case LEAF_ipFragCreates:
349		value->v.uint32 = ipstat.ips_ofragments;
350		break;
351	}
352	return (SNMP_ERR_NOERROR);
353}
354
355/*
356 * READ-ONLY statistics icmp group.
357 */
358int
359op_icmpstat(struct snmp_context *ctx __unused, struct snmp_value *value,
360    u_int sub, u_int idx __unused, enum snmp_op op)
361{
362	u_int i;
363
364	switch (op) {
365
366	  case SNMP_OP_GETNEXT:
367		abort();
368
369	  case SNMP_OP_GET:
370		break;
371
372	  case SNMP_OP_SET:
373		return (SNMP_ERR_NOT_WRITEABLE);
374
375	  case SNMP_OP_ROLLBACK:
376	  case SNMP_OP_COMMIT:
377		abort();
378	}
379
380	if (ipstat_tick < this_tick)
381		fetch_ipstat();
382
383	switch (value->var.subs[sub - 1]) {
384
385	  case LEAF_icmpInMsgs:
386		value->v.integer = 0;
387		for (i = 0; i <= ICMP_MAXTYPE; i++)
388			value->v.integer += icmpstat.icps_inhist[i];
389		value->v.integer += icmpstat.icps_tooshort +
390		    icmpstat.icps_checksum;
391		/* missing: bad type and packets on faith */
392		break;
393
394	  case LEAF_icmpInErrors:
395		value->v.integer = icmpstat.icps_tooshort +
396		    icmpstat.icps_checksum +
397		    icmpstat.icps_badlen +
398		    icmpstat.icps_badcode +
399		    icmpstat.icps_bmcastecho +
400		    icmpstat.icps_bmcasttstamp;
401		break;
402
403	  case LEAF_icmpInDestUnreachs:
404		value->v.integer = icmpstat.icps_inhist[ICMP_UNREACH];
405		break;
406
407	  case LEAF_icmpInTimeExcds:
408		value->v.integer = icmpstat.icps_inhist[ICMP_TIMXCEED];
409		break;
410
411	  case LEAF_icmpInParmProbs:
412		value->v.integer = icmpstat.icps_inhist[ICMP_PARAMPROB];
413		break;
414
415	  case LEAF_icmpInSrcQuenchs:
416		value->v.integer = icmpstat.icps_inhist[ICMP_SOURCEQUENCH];
417		break;
418
419	  case LEAF_icmpInRedirects:
420		value->v.integer = icmpstat.icps_inhist[ICMP_REDIRECT];
421		break;
422
423	  case LEAF_icmpInEchos:
424		value->v.integer = icmpstat.icps_inhist[ICMP_ECHO];
425		break;
426
427	  case LEAF_icmpInEchoReps:
428		value->v.integer = icmpstat.icps_inhist[ICMP_ECHOREPLY];
429		break;
430
431	  case LEAF_icmpInTimestamps:
432		value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMP];
433		break;
434
435	  case LEAF_icmpInTimestampReps:
436		value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMPREPLY];
437		break;
438
439	  case LEAF_icmpInAddrMasks:
440		value->v.integer = icmpstat.icps_inhist[ICMP_MASKREQ];
441		break;
442
443	  case LEAF_icmpInAddrMaskReps:
444		value->v.integer = icmpstat.icps_inhist[ICMP_MASKREPLY];
445		break;
446
447	  case LEAF_icmpOutMsgs:
448		value->v.integer = 0;
449		for (i = 0; i <= ICMP_MAXTYPE; i++)
450			value->v.integer += icmpstat.icps_outhist[i];
451		value->v.integer += icmpstat.icps_badaddr +
452		    icmpstat.icps_noroute;
453		break;
454
455	  case LEAF_icmpOutErrors:
456		value->v.integer = icmpstat.icps_badaddr +
457		    icmpstat.icps_noroute;
458		break;
459
460	  case LEAF_icmpOutDestUnreachs:
461		value->v.integer = icmpstat.icps_outhist[ICMP_UNREACH];
462		break;
463
464	  case LEAF_icmpOutTimeExcds:
465		value->v.integer = icmpstat.icps_outhist[ICMP_TIMXCEED];
466		break;
467
468	  case LEAF_icmpOutParmProbs:
469		value->v.integer = icmpstat.icps_outhist[ICMP_PARAMPROB];
470		break;
471
472	  case LEAF_icmpOutSrcQuenchs:
473		value->v.integer = icmpstat.icps_outhist[ICMP_SOURCEQUENCH];
474		break;
475
476	  case LEAF_icmpOutRedirects:
477		value->v.integer = icmpstat.icps_outhist[ICMP_REDIRECT];
478		break;
479
480	  case LEAF_icmpOutEchos:
481		value->v.integer = icmpstat.icps_outhist[ICMP_ECHO];
482		break;
483
484	  case LEAF_icmpOutEchoReps:
485		value->v.integer = icmpstat.icps_outhist[ICMP_ECHOREPLY];
486		break;
487
488	  case LEAF_icmpOutTimestamps:
489		value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMP];
490		break;
491
492	  case LEAF_icmpOutTimestampReps:
493		value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMPREPLY];
494		break;
495
496	  case LEAF_icmpOutAddrMasks:
497		value->v.integer = icmpstat.icps_outhist[ICMP_MASKREQ];
498		break;
499
500	  case LEAF_icmpOutAddrMaskReps:
501		value->v.integer = icmpstat.icps_outhist[ICMP_MASKREPLY];
502		break;
503	}
504	return (SNMP_ERR_NOERROR);
505}
506