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