1 /**
2 * @file
3 * Management Information Base II (RFC1213) INTERFACES objects and functions.
4 */
5
6 /*
7 * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without modification,
11 * are permitted provided that the following conditions are met:
12 *
13 * 1. Redistributions of source code must retain the above copyright notice,
14 * this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright notice,
16 * this list of conditions and the following disclaimer in the documentation
17 * and/or other materials provided with the distribution.
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
22 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
23 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
24 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
26 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
30 * OF SUCH DAMAGE.
31 *
32 * Author: Dirk Ziegelmeier <dziegel@gmx.de>
33 * Christiaan Simons <christiaan.simons@axon.tv>
34 */
35
36 #include "lwip/snmp.h"
37 #include "lwip/apps/snmp.h"
38 #include "lwip/apps/snmp_core.h"
39 #include "lwip/apps/snmp_mib2.h"
40 #include "lwip/apps/snmp_table.h"
41 #include "lwip/apps/snmp_scalar.h"
42 #include "lwip/netif.h"
43 #include "lwip/stats.h"
44
45 #include <string.h>
46
47 #if LWIP_SNMP && SNMP_LWIP_MIB2
48
49 #if SNMP_USE_NETCONN
50 #define SYNC_NODE_NAME(node_name) node_name ## _synced
51 #define CREATE_LWIP_SYNC_NODE(oid, node_name) \
52 static const struct snmp_threadsync_node node_name ## _synced = SNMP_CREATE_THREAD_SYNC_NODE(oid, &node_name.node, &snmp_mib2_lwip_locks);
53 #else
54 #define SYNC_NODE_NAME(node_name) node_name
55 #define CREATE_LWIP_SYNC_NODE(oid, node_name)
56 #endif
57
58
59 /* --- interfaces .1.3.6.1.2.1.2 ----------------------------------------------------- */
60
61 static s16_t
interfaces_get_value(struct snmp_node_instance * instance,void * value)62 interfaces_get_value(struct snmp_node_instance *instance, void *value)
63 {
64 if (instance->node->oid == 1) {
65 s32_t *sint_ptr = (s32_t *)value;
66 s32_t num_netifs = 0;
67
68 struct netif *netif;
69 NETIF_FOREACH(netif) {
70 num_netifs++;
71 }
72
73 *sint_ptr = num_netifs;
74 return sizeof(*sint_ptr);
75 }
76
77 return 0;
78 }
79
80 /* list of allowed value ranges for incoming OID */
81 static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
82 { 1, 0xff } /* netif->num is u8_t */
83 };
84
85 static const u8_t iftable_ifOutQLen = 0;
86
87 static const u8_t iftable_ifOperStatus_up = 1;
88 static const u8_t iftable_ifOperStatus_down = 2;
89
90 static const u8_t iftable_ifAdminStatus_up = 1;
91 static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
92 static const u8_t iftable_ifAdminStatus_down = 2;
93
94 static snmp_err_t
interfaces_Table_get_cell_instance(const u32_t * column,const u32_t * row_oid,u8_t row_oid_len,struct snmp_node_instance * cell_instance)95 interfaces_Table_get_cell_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
96 {
97 u32_t ifIndex;
98 struct netif *netif;
99
100 LWIP_UNUSED_ARG(column);
101
102 /* check if incoming OID length and if values are in plausible range */
103 if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
104 return SNMP_ERR_NOSUCHINSTANCE;
105 }
106
107 /* get netif index from incoming OID */
108 ifIndex = row_oid[0];
109
110 /* find netif with index */
111 NETIF_FOREACH(netif) {
112 if (netif_to_num(netif) == ifIndex) {
113 /* store netif pointer for subsequent operations (get/test/set) */
114 cell_instance->reference.ptr = netif;
115 return SNMP_ERR_NOERROR;
116 }
117 }
118
119 /* not found */
120 return SNMP_ERR_NOSUCHINSTANCE;
121 }
122
123 static snmp_err_t
interfaces_Table_get_next_cell_instance(const u32_t * column,struct snmp_obj_id * row_oid,struct snmp_node_instance * cell_instance)124 interfaces_Table_get_next_cell_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
125 {
126 struct netif *netif;
127 struct snmp_next_oid_state state;
128 u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
129
130 LWIP_UNUSED_ARG(column);
131
132 /* init struct to search next oid */
133 snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
134
135 /* iterate over all possible OIDs to find the next one */
136 NETIF_FOREACH(netif) {
137 u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
138 test_oid[0] = netif_to_num(netif);
139
140 /* check generated OID: is it a candidate for the next one? */
141 snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
142 }
143
144 /* did we find a next one? */
145 if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
146 snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
147 /* store netif pointer for subsequent operations (get/test/set) */
148 cell_instance->reference.ptr = /* (struct netif*) */state.reference;
149 return SNMP_ERR_NOERROR;
150 }
151
152 /* not found */
153 return SNMP_ERR_NOSUCHINSTANCE;
154 }
155
156 static s16_t
interfaces_Table_get_value(struct snmp_node_instance * instance,void * value)157 interfaces_Table_get_value(struct snmp_node_instance *instance, void *value)
158 {
159 struct netif *netif = (struct netif *)instance->reference.ptr;
160 u32_t *value_u32 = (u32_t *)value;
161 s32_t *value_s32 = (s32_t *)value;
162 u16_t value_len;
163
164 switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id)) {
165 case 1: /* ifIndex */
166 *value_s32 = netif_to_num(netif);
167 value_len = sizeof(*value_s32);
168 break;
169 case 2: /* ifDescr */
170 value_len = sizeof(netif->name);
171 MEMCPY(value, netif->name, value_len);
172 break;
173 case 3: /* ifType */
174 *value_s32 = netif->link_type;
175 value_len = sizeof(*value_s32);
176 break;
177 case 4: /* ifMtu */
178 *value_s32 = netif->mtu;
179 value_len = sizeof(*value_s32);
180 break;
181 case 5: /* ifSpeed */
182 *value_u32 = netif->link_speed;
183 value_len = sizeof(*value_u32);
184 break;
185 case 6: /* ifPhysAddress */
186 value_len = sizeof(netif->hwaddr);
187 MEMCPY(value, &netif->hwaddr, value_len);
188 break;
189 case 7: /* ifAdminStatus */
190 if (netif_is_up(netif)) {
191 *value_s32 = iftable_ifOperStatus_up;
192 } else {
193 *value_s32 = iftable_ifOperStatus_down;
194 }
195 value_len = sizeof(*value_s32);
196 break;
197 case 8: /* ifOperStatus */
198 if (netif_is_up(netif)) {
199 if (netif_is_link_up(netif)) {
200 *value_s32 = iftable_ifAdminStatus_up;
201 } else {
202 *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
203 }
204 } else {
205 *value_s32 = iftable_ifAdminStatus_down;
206 }
207 value_len = sizeof(*value_s32);
208 break;
209 case 9: /* ifLastChange */
210 *value_u32 = netif->ts;
211 value_len = sizeof(*value_u32);
212 break;
213 case 10: /* ifInOctets */
214 *value_u32 = netif->mib2_counters.ifinoctets;
215 value_len = sizeof(*value_u32);
216 break;
217 case 11: /* ifInUcastPkts */
218 *value_u32 = netif->mib2_counters.ifinucastpkts;
219 value_len = sizeof(*value_u32);
220 break;
221 case 12: /* ifInNUcastPkts */
222 *value_u32 = netif->mib2_counters.ifinnucastpkts;
223 value_len = sizeof(*value_u32);
224 break;
225 case 13: /* ifInDiscards */
226 *value_u32 = netif->mib2_counters.ifindiscards;
227 value_len = sizeof(*value_u32);
228 break;
229 case 14: /* ifInErrors */
230 *value_u32 = netif->mib2_counters.ifinerrors;
231 value_len = sizeof(*value_u32);
232 break;
233 case 15: /* ifInUnkownProtos */
234 *value_u32 = netif->mib2_counters.ifinunknownprotos;
235 value_len = sizeof(*value_u32);
236 break;
237 case 16: /* ifOutOctets */
238 *value_u32 = netif->mib2_counters.ifoutoctets;
239 value_len = sizeof(*value_u32);
240 break;
241 case 17: /* ifOutUcastPkts */
242 *value_u32 = netif->mib2_counters.ifoutucastpkts;
243 value_len = sizeof(*value_u32);
244 break;
245 case 18: /* ifOutNUcastPkts */
246 *value_u32 = netif->mib2_counters.ifoutnucastpkts;
247 value_len = sizeof(*value_u32);
248 break;
249 case 19: /* ifOutDiscarts */
250 *value_u32 = netif->mib2_counters.ifoutdiscards;
251 value_len = sizeof(*value_u32);
252 break;
253 case 20: /* ifOutErrors */
254 *value_u32 = netif->mib2_counters.ifouterrors;
255 value_len = sizeof(*value_u32);
256 break;
257 case 21: /* ifOutQLen */
258 *value_u32 = iftable_ifOutQLen;
259 value_len = sizeof(*value_u32);
260 break;
261 /** @note returning zeroDotZero (0.0) no media specific MIB support */
262 case 22: /* ifSpecific */
263 value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
264 MEMCPY(value, snmp_zero_dot_zero.id, value_len);
265 break;
266 default:
267 return 0;
268 }
269
270 return value_len;
271 }
272
273 #if !SNMP_SAFE_REQUESTS
274
275 static snmp_err_t
interfaces_Table_set_test(struct snmp_node_instance * instance,u16_t len,void * value)276 interfaces_Table_set_test(struct snmp_node_instance *instance, u16_t len, void *value)
277 {
278 s32_t *sint_ptr = (s32_t *)value;
279
280 /* stack should never call this method for another column,
281 because all other columns are set to readonly */
282 LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
283 LWIP_UNUSED_ARG(len);
284
285 if (*sint_ptr == 1 || *sint_ptr == 2) {
286 return SNMP_ERR_NOERROR;
287 }
288
289 return SNMP_ERR_WRONGVALUE;
290 }
291
292 static snmp_err_t
interfaces_Table_set_value(struct snmp_node_instance * instance,u16_t len,void * value)293 interfaces_Table_set_value(struct snmp_node_instance *instance, u16_t len, void *value)
294 {
295 struct netif *netif = (struct netif *)instance->reference.ptr;
296 s32_t *sint_ptr = (s32_t *)value;
297
298 /* stack should never call this method for another column,
299 because all other columns are set to readonly */
300 LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
301 LWIP_UNUSED_ARG(len);
302
303 if (*sint_ptr == 1) {
304 netif_set_up(netif);
305 } else if (*sint_ptr == 2) {
306 netif_set_down(netif);
307 }
308
309 return SNMP_ERR_NOERROR;
310 }
311
312 #endif /* SNMP_SAFE_REQUESTS */
313
314 static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
315
316 static const struct snmp_table_col_def interfaces_Table_columns[] = {
317 { 1, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
318 { 2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
319 { 3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
320 { 4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
321 { 5, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
322 { 6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
323 #if !SNMP_SAFE_REQUESTS
324 { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
325 #else
326 { 7, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
327 #endif
328 { 8, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
329 { 9, SNMP_ASN1_TYPE_TIMETICKS, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
330 { 10, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
331 { 11, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
332 { 12, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
333 { 13, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
334 { 14, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
335 { 15, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
336 { 16, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
337 { 17, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
338 { 18, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
339 { 19, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
340 { 20, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
341 { 21, SNMP_ASN1_TYPE_GAUGE, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
342 { 22, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY } /* ifSpecific */
343 };
344
345 #if !SNMP_SAFE_REQUESTS
346 static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
347 2, interfaces_Table_columns,
348 interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
349 interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
350 #else
351 static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
352 2, interfaces_Table_columns,
353 interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
354 interfaces_Table_get_value, NULL, NULL);
355 #endif
356
357 /* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
358 CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
359 CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
360
361 static const struct snmp_node *const interface_nodes[] = {
362 &SYNC_NODE_NAME(interfaces_Number).node.node,
363 &SYNC_NODE_NAME(interfaces_Table).node.node
364 };
365
366 const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
367
368 #endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
369