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 = netif_list;
69     while (netif != NULL) {
70       num_netifs++;
71       netif = netif->next;
72     }
73 
74     *sint_ptr = num_netifs;
75     return sizeof(*sint_ptr);
76   }
77 
78   return 0;
79 }
80 
81 /* list of allowed value ranges for incoming OID */
82 static const struct snmp_oid_range interfaces_Table_oid_ranges[] = {
83   { 1, 0xff } /* netif->num is u8_t */
84 };
85 
86 static const u8_t iftable_ifOutQLen         = 0;
87 
88 static const u8_t iftable_ifOperStatus_up   = 1;
89 static const u8_t iftable_ifOperStatus_down = 2;
90 
91 static const u8_t iftable_ifAdminStatus_up             = 1;
92 static const u8_t iftable_ifAdminStatus_lowerLayerDown = 7;
93 static const u8_t iftable_ifAdminStatus_down           = 2;
94 
95 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)96 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)
97 {
98   u32_t ifIndex;
99   struct netif *netif;
100 
101   LWIP_UNUSED_ARG(column);
102 
103   /* check if incoming OID length and if values are in plausible range */
104   if (!snmp_oid_in_range(row_oid, row_oid_len, interfaces_Table_oid_ranges, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges))) {
105     return SNMP_ERR_NOSUCHINSTANCE;
106   }
107 
108   /* get netif index from incoming OID */
109   ifIndex = row_oid[0];
110 
111   /* find netif with index */
112   netif = netif_list;
113   while (netif != NULL) {
114     if (netif_to_num(netif) == ifIndex) {
115       /* store netif pointer for subsequent operations (get/test/set) */
116       cell_instance->reference.ptr = netif;
117       return SNMP_ERR_NOERROR;
118     }
119     netif = netif->next;
120   }
121 
122   /* not found */
123   return SNMP_ERR_NOSUCHINSTANCE;
124 }
125 
126 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)127 interfaces_Table_get_next_cell_instance(const u32_t* column, struct snmp_obj_id* row_oid, struct snmp_node_instance* cell_instance)
128 {
129   struct netif *netif;
130   struct snmp_next_oid_state state;
131   u32_t result_temp[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
132 
133   LWIP_UNUSED_ARG(column);
134 
135   /* init struct to search next oid */
136   snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges));
137 
138   /* iterate over all possible OIDs to find the next one */
139   netif = netif_list;
140   while (netif != NULL) {
141     u32_t test_oid[LWIP_ARRAYSIZE(interfaces_Table_oid_ranges)];
142     test_oid[0] = netif_to_num(netif);
143 
144     /* check generated OID: is it a candidate for the next one? */
145     snmp_next_oid_check(&state, test_oid, LWIP_ARRAYSIZE(interfaces_Table_oid_ranges), netif);
146 
147     netif = netif->next;
148   }
149 
150   /* did we find a next one? */
151   if (state.status == SNMP_NEXT_OID_STATUS_SUCCESS) {
152     snmp_oid_assign(row_oid, state.next_oid, state.next_oid_len);
153     /* store netif pointer for subsequent operations (get/test/set) */
154     cell_instance->reference.ptr = /* (struct netif*) */state.reference;
155     return SNMP_ERR_NOERROR;
156   }
157 
158   /* not found */
159   return SNMP_ERR_NOSUCHINSTANCE;
160 }
161 
162 static s16_t
interfaces_Table_get_value(struct snmp_node_instance * instance,void * value)163 interfaces_Table_get_value(struct snmp_node_instance* instance, void* value)
164 {
165   struct netif *netif = (struct netif*)instance->reference.ptr;
166   u32_t* value_u32 = (u32_t*)value;
167   s32_t* value_s32 = (s32_t*)value;
168   u16_t value_len;
169 
170   switch (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id))
171   {
172   case 1: /* ifIndex */
173     *value_s32 = netif_to_num(netif);
174     value_len = sizeof(*value_s32);
175     break;
176   case 2: /* ifDescr */
177     value_len = sizeof(netif->name);
178     MEMCPY(value, netif->name, value_len);
179     break;
180   case 3: /* ifType */
181     *value_s32 = netif->link_type;
182     value_len = sizeof(*value_s32);
183     break;
184   case 4: /* ifMtu */
185     *value_s32 = netif->mtu;
186     value_len = sizeof(*value_s32);
187     break;
188   case 5: /* ifSpeed */
189     *value_u32 = netif->link_speed;
190     value_len = sizeof(*value_u32);
191     break;
192   case 6: /* ifPhysAddress */
193     value_len = sizeof(netif->hwaddr);
194     MEMCPY(value, &netif->hwaddr, value_len);
195     break;
196   case 7: /* ifAdminStatus */
197     if (netif_is_up(netif)) {
198       *value_s32 = iftable_ifOperStatus_up;
199     } else {
200       *value_s32 = iftable_ifOperStatus_down;
201     }
202     value_len = sizeof(*value_s32);
203     break;
204   case 8: /* ifOperStatus */
205     if (netif_is_up(netif)) {
206       if (netif_is_link_up(netif)) {
207         *value_s32 = iftable_ifAdminStatus_up;
208       } else {
209         *value_s32 = iftable_ifAdminStatus_lowerLayerDown;
210       }
211     } else {
212       *value_s32 = iftable_ifAdminStatus_down;
213     }
214     value_len = sizeof(*value_s32);
215     break;
216   case 9: /* ifLastChange */
217     *value_u32 = netif->ts;
218     value_len = sizeof(*value_u32);
219     break;
220   case 10: /* ifInOctets */
221     *value_u32 = netif->mib2_counters.ifinoctets;
222     value_len = sizeof(*value_u32);
223     break;
224   case 11: /* ifInUcastPkts */
225     *value_u32 = netif->mib2_counters.ifinucastpkts;
226     value_len = sizeof(*value_u32);
227     break;
228   case 12: /* ifInNUcastPkts */
229     *value_u32 = netif->mib2_counters.ifinnucastpkts;
230     value_len = sizeof(*value_u32);
231     break;
232   case 13: /* ifInDiscards */
233     *value_u32 = netif->mib2_counters.ifindiscards;
234     value_len = sizeof(*value_u32);
235     break;
236   case 14: /* ifInErrors */
237     *value_u32 = netif->mib2_counters.ifinerrors;
238     value_len = sizeof(*value_u32);
239     break;
240   case 15: /* ifInUnkownProtos */
241     *value_u32 = netif->mib2_counters.ifinunknownprotos;
242     value_len = sizeof(*value_u32);
243     break;
244   case 16: /* ifOutOctets */
245     *value_u32 = netif->mib2_counters.ifoutoctets;
246     value_len = sizeof(*value_u32);
247     break;
248   case 17: /* ifOutUcastPkts */
249     *value_u32 = netif->mib2_counters.ifoutucastpkts;
250     value_len = sizeof(*value_u32);
251     break;
252   case 18: /* ifOutNUcastPkts */
253     *value_u32 = netif->mib2_counters.ifoutnucastpkts;
254     value_len = sizeof(*value_u32);
255     break;
256   case 19: /* ifOutDiscarts */
257     *value_u32 = netif->mib2_counters.ifoutdiscards;
258     value_len = sizeof(*value_u32);
259     break;
260   case 20: /* ifOutErrors */
261     *value_u32 = netif->mib2_counters.ifouterrors;
262     value_len = sizeof(*value_u32);
263     break;
264   case 21: /* ifOutQLen */
265     *value_u32 = iftable_ifOutQLen;
266     value_len = sizeof(*value_u32);
267     break;
268   /** @note returning zeroDotZero (0.0) no media specific MIB support */
269   case 22: /* ifSpecific */
270     value_len = snmp_zero_dot_zero.len * sizeof(u32_t);
271     MEMCPY(value, snmp_zero_dot_zero.id, value_len);
272     break;
273   default:
274     return 0;
275   }
276 
277   return value_len;
278 }
279 
280 #if !SNMP_SAFE_REQUESTS
281 
282 static snmp_err_t
interfaces_Table_set_test(struct snmp_node_instance * instance,u16_t len,void * value)283 interfaces_Table_set_test(struct snmp_node_instance* instance, u16_t len, void *value)
284 {
285   s32_t *sint_ptr = (s32_t*)value;
286 
287   /* stack should never call this method for another column,
288   because all other columns are set to readonly */
289   LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
290   LWIP_UNUSED_ARG(len);
291 
292   if (*sint_ptr == 1 || *sint_ptr == 2) {
293     return SNMP_ERR_NOERROR;
294   }
295 
296   return SNMP_ERR_WRONGVALUE;
297 }
298 
299 static snmp_err_t
interfaces_Table_set_value(struct snmp_node_instance * instance,u16_t len,void * value)300 interfaces_Table_set_value(struct snmp_node_instance* instance, u16_t len, void *value)
301 {
302   struct netif *netif = (struct netif*)instance->reference.ptr;
303   s32_t *sint_ptr = (s32_t*)value;
304 
305   /* stack should never call this method for another column,
306   because all other columns are set to readonly */
307   LWIP_ASSERT("Invalid column", (SNMP_TABLE_GET_COLUMN_FROM_OID(instance->instance_oid.id) == 7));
308   LWIP_UNUSED_ARG(len);
309 
310   if (*sint_ptr == 1) {
311     netif_set_up(netif);
312   } else if (*sint_ptr == 2) {
313     netif_set_down(netif);
314   }
315 
316   return SNMP_ERR_NOERROR;
317 }
318 
319 #endif /* SNMP_SAFE_REQUESTS */
320 
321 static const struct snmp_scalar_node interfaces_Number = SNMP_SCALAR_CREATE_NODE_READONLY(1, SNMP_ASN1_TYPE_INTEGER, interfaces_get_value);
322 
323 static const struct snmp_table_col_def interfaces_Table_columns[] = {
324   {  1, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifIndex */
325   {  2, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifDescr */
326   {  3, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifType */
327   {  4, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifMtu */
328   {  5, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifSpeed */
329   {  6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY }, /* ifPhysAddress */
330 #if !SNMP_SAFE_REQUESTS
331   {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_WRITE }, /* ifAdminStatus */
332 #else
333   {  7, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifAdminStatus */
334 #endif
335   {  8, SNMP_ASN1_TYPE_INTEGER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOperStatus */
336   {  9, SNMP_ASN1_TYPE_TIMETICKS,    SNMP_NODE_INSTANCE_READ_ONLY }, /* ifLastChange */
337   { 10, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInOctets */
338   { 11, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUcastPkts */
339   { 12, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInNUcastPkts */
340   { 13, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInDiscarts */
341   { 14, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInErrors */
342   { 15, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifInUnkownProtos */
343   { 16, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutOctets */
344   { 17, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutUcastPkts */
345   { 18, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutNUcastPkts */
346   { 19, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutDiscarts */
347   { 20, SNMP_ASN1_TYPE_COUNTER,      SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutErrors */
348   { 21, SNMP_ASN1_TYPE_GAUGE,        SNMP_NODE_INSTANCE_READ_ONLY }, /* ifOutQLen */
349   { 22, SNMP_ASN1_TYPE_OBJECT_ID,    SNMP_NODE_INSTANCE_READ_ONLY }  /* ifSpecific */
350 };
351 
352 #if !SNMP_SAFE_REQUESTS
353 static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
354   2, interfaces_Table_columns,
355   interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
356   interfaces_Table_get_value, interfaces_Table_set_test, interfaces_Table_set_value);
357 #else
358 static const struct snmp_table_node interfaces_Table = SNMP_TABLE_CREATE(
359   2, interfaces_Table_columns,
360   interfaces_Table_get_cell_instance, interfaces_Table_get_next_cell_instance,
361   interfaces_Table_get_value, NULL, NULL);
362 #endif
363 
364 /* the following nodes access variables in LWIP stack from SNMP worker thread and must therefore be synced to LWIP (TCPIP) thread */
365 CREATE_LWIP_SYNC_NODE(1, interfaces_Number)
366 CREATE_LWIP_SYNC_NODE(2, interfaces_Table)
367 
368 static const struct snmp_node* const interface_nodes[] = {
369   &SYNC_NODE_NAME(interfaces_Number).node.node,
370   &SYNC_NODE_NAME(interfaces_Table).node.node
371 };
372 
373 const struct snmp_tree_node snmp_mib2_interface_root = SNMP_CREATE_TREE_NODE(2, interface_nodes);
374 
375 #endif /* LWIP_SNMP && SNMP_LWIP_MIB2 */
376