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
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
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
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
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   {
166   case 1: /* ifIndex */
167     *value_s32 = netif_to_num(netif);
168     value_len = sizeof(*value_s32);
169     break;
170   case 2: /* ifDescr */
171     value_len = sizeof(netif->name);
172     MEMCPY(value, netif->name, value_len);
173     break;
174   case 3: /* ifType */
175     *value_s32 = netif->link_type;
176     value_len = sizeof(*value_s32);
177     break;
178   case 4: /* ifMtu */
179     *value_s32 = netif->mtu;
180     value_len = sizeof(*value_s32);
181     break;
182   case 5: /* ifSpeed */
183     *value_u32 = netif->link_speed;
184     value_len = sizeof(*value_u32);
185     break;
186   case 6: /* ifPhysAddress */
187     value_len = sizeof(netif->hwaddr);
188     MEMCPY(value, &netif->hwaddr, value_len);
189     break;
190   case 7: /* ifAdminStatus */
191     if (netif_is_up(netif)) {
192       *value_s32 = iftable_ifOperStatus_up;
193     } else {
194       *value_s32 = iftable_ifOperStatus_down;
195     }
196     value_len = sizeof(*value_s32);
197     break;
198   case 8: /* ifOperStatus */
199     if (netif_is_up(netif)) {
200       if (netif_is_link_up(netif)) {
201         *value_s32 = iftable_ifAdminStatus_up;
202       } else {
203         *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
204       }
205     } else {
206       *value_s32 = iftable_ifAdminStatus_down;
207     }
208     value_len = sizeof(*value_s32);
209     break;
210   case 9: /* ifLastChange */
211     *value_u32 = netif->ts;
212     value_len = sizeof(*value_u32);
213     break;
214   case 10: /* ifInOctets */
215     *value_u32 = netif->mib2_counters.ifinoctets;
216     value_len = sizeof(*value_u32);
217     break;
218   case 11: /* ifInUcastPkts */
219     *value_u32 = netif->mib2_counters.ifinucastpkts;
220     value_len = sizeof(*value_u32);
221     break;
222   case 12: /* ifInNUcastPkts */
223     *value_u32 = netif->mib2_counters.ifinnucastpkts;
224     value_len = sizeof(*value_u32);
225     break;
226   case 13: /* ifInDiscards */
227     *value_u32 = netif->mib2_counters.ifindiscards;
228     value_len = sizeof(*value_u32);
229     break;
230   case 14: /* ifInErrors */
231     *value_u32 = netif->mib2_counters.ifinerrors;
232     value_len = sizeof(*value_u32);
233     break;
234   case 15: /* ifInUnkownProtos */
235     *value_u32 = netif->mib2_counters.ifinunknownprotos;
236     value_len = sizeof(*value_u32);
237     break;
238   case 16: /* ifOutOctets */
239     *value_u32 = netif->mib2_counters.ifoutoctets;
240     value_len = sizeof(*value_u32);
241     break;
242   case 17: /* ifOutUcastPkts */
243     *value_u32 = netif->mib2_counters.ifoutucastpkts;
244     value_len = sizeof(*value_u32);
245     break;
246   case 18: /* ifOutNUcastPkts */
247     *value_u32 = netif->mib2_counters.ifoutnucastpkts;
248     value_len = sizeof(*value_u32);
249     break;
250   case 19: /* ifOutDiscarts */
251     *value_u32 = netif->mib2_counters.ifoutdiscards;
252     value_len = sizeof(*value_u32);
253     break;
254   case 20: /* ifOutErrors */
255     *value_u32 = netif->mib2_counters.ifouterrors;
256     value_len = sizeof(*value_u32);
257     break;
258   case 21: /* ifOutQLen */
259     *value_u32 = iftable_ifOutQLen;
260     value_len = sizeof(*value_u32);
261     break;
262   /** @note returning zeroDotZero (0.0) no media specific MIB support */
263   case 22: /* ifSpecific */
264     value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
265     MEMCPY(value, snmp_zero_dot_zero.id, value_len);
266     break;
267   default:
268     return 0;
269   }
270 
271   return value_len;
272 }
273 
274 #if !SNMP_SAFE_REQUESTS
275 
276 static snmp_err_t
277 interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
278 {
279   s32_t *sint_ptr = (s32_t*)value;
280 
281   /* stack should never call this method for another column,
282   because all other columns are set to readonly */
283   LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
284   LWIP_UNUSED_ARG(len);
285 
286   if (*sint_ptr == 1 || *sint_ptr == 2) {
287     return SNMP_ERR_NOERROR;
288   }
289 
290   return SNMP_ERR_WRONGVALUE;
291 }
292 
293 static snmp_err_t
294 interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
295 {
296   struct netif *netif = (struct netif*)instance->reference.ptr;
297   s32_t *sint_ptr = (s32_t*)value;
298 
299   /* stack should never call this method for another column,
300   because all other columns are set to readonly */
301   LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
302   LWIP_UNUSED_ARG(len);
303 
304   if (*sint_ptr == 1) {
305     netif_set_up(netif);
306   } else if (*sint_ptr == 2) {
307     netif_set_down(netif);
308   }
309 
310   return SNMP_ERR_NOERROR;
311 }
312 
313 #endif /* SNMP_SAFE_REQUESTS */
314 
315 static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
316 
317 static const struct snmp_table_col_def interfaces_Table_columns[] = {
318   {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
319   {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
320   {  3, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
321   {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
322   {  5, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
323   {  6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
324 #if !SNMP_SAFE_REQUESTS
325   {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
326 #else
327   {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
328 #endif
329   {  8, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
330   {  9, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
331   { 10, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
332   { 11, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
333   { 12, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
334   { 13, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
335   { 14, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
336   { 15, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
337   { 16, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
338   { 17, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
339   { 18, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
340   { 19, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
341   { 20, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
342   { 21, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
343   { 22, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY }  /* ifSpecific */
344 };
345 
346 #if !SNMP_SAFE_REQUESTS
347 static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
348   2, interfaces_Table_columns,
349   interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
350   interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
351 #else
352 static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
353   2, interfaces_Table_columns,
354   interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
355   interfaces_Table_get_value, NULL, NULL);
356 #endif
357 
358 /* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
359 CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
360 CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
361 
362 static const struct snmp_node* const interface_nodes[] = {
363   &SYNC_NODE_NAME(interfaces_Number).node.node,
364   &SYNC_NODE_NAME(interfaces_Table).node.node
365 };
366 
367 const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
368 
369 #endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
370