├── .coveralls.yml ├── AUTHORS ├── TODO.md ├── .gitignore ├── tests ├── snmp_daemon.sh ├── agentx_daemon.sh ├── test_all.py ├── test_snmpv2c.py ├── snmpd.conf ├── test_snmpv3_noauth.py ├── test_agentxv2c.py ├── test_agentxv3_noauth.py ├── test_agentxv3_authonly.py ├── test_agentxv3_authpriv.py ├── netsnmp_build.sh ├── smartsnmp_testcase.py ├── test_utils.lua └── testcase.sh ├── scons_tools ├── endian_probe.py ├── select_probe.py ├── kqueue_probe.py └── epoll_probe.py ├── config ├── agentx.conf └── snmp.conf ├── doc ├── coverage.md └── api.md ├── mibs ├── dummy.lua ├── three_cascaded_index_table.lua ├── two_cascaded_index_table.lua ├── icmp.lua ├── system.lua ├── interfaces.lua ├── udp.lua └── tcp.lua ├── core ├── transport.h ├── ev_loop.h ├── protocol.h ├── ev_select.h ├── ev_kqueue.h ├── snmp.c ├── ev_epoll.h ├── asn1.h ├── agentx_encoder.c ├── agentx_tcp_evloop_transport.c ├── snmp_udp_uloop_transport.c ├── snmp_udp_evloop_transport.c ├── agentx_decoder.c ├── ev_loop.c ├── util.h ├── snmp_udp_libevent_transport.c ├── snmp_decoder.c ├── mib.h ├── snmp.h ├── list.h ├── agentx.c ├── agentx.h ├── smartsnmp.c ├── agentx_msg_proc.c ├── snmp_encoder.c └── snmp_msg_out.c ├── .travis.yml ├── README.md ├── bin └── smartsnmpd ├── lualib └── smartsnmp │ └── utils.lua └── SConstruct /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Leo Ma 2 | Xiongfei Guo 3 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | - Installation script. 4 | - SNMPv3 encryption. 5 | - Trap for SNMP and notification for AgentX. 6 | - ASN.1 compiler or interpretor. 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # object files 2 | *.o 3 | *.os 4 | 5 | # libraries 6 | *.a 7 | *.so 8 | 9 | # log files 10 | *.log 11 | 12 | # scons files 13 | *.scon* 14 | 15 | # python files 16 | *.pyc 17 | -------------------------------------------------------------------------------- /tests/snmp_daemon.sh: -------------------------------------------------------------------------------- 1 | if [ -n "$LUACOV" ]; then 2 | export SYS_LUA_PATH=`lua -e "print(package.path)"`; 3 | export LUACOV_OPT=-lluacov 4 | fi 5 | LUA_PATH="lualib/?/init.lua;lualib/?.lua;./?.lua;$SYS_LUA_PATH" LUA_CPATH="build/?.so" lua $LUACOV_OPT ./bin/smartsnmpd -c config/snmp.conf 6 | -------------------------------------------------------------------------------- /tests/agentx_daemon.sh: -------------------------------------------------------------------------------- 1 | if [ -n "$LUACOV" ]; then 2 | export SYS_LUA_PATH=`lua -e "print(package.path)"`; 3 | export LUACOV_OPT=-lluacov 4 | fi 5 | LUA_PATH="lualib/?/init.lua;lualib/?.lua;./?.lua;$SYS_LUA_PATH" LUA_CPATH="build/?.so" lua $LUACOV_OPT ./bin/smartsnmpd -c config/agentx.conf 6 | -------------------------------------------------------------------------------- /tests/test_all.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sys, os, glob 3 | 4 | project_root = os.getcwd() 5 | test_root = os.path.dirname(os.path.abspath(__file__)) 6 | test_files = glob.glob(os.path.join(test_root, 'test_*.py')) 7 | 8 | os.chdir(test_root) 9 | test_names = [os.path.basename(name)[:-3] for name in test_files] 10 | os.chdir(project_root) 11 | 12 | suite = unittest.defaultTestLoader.loadTestsFromNames(test_names) 13 | 14 | if __name__ == "__main__": 15 | from pprint import pprint 16 | runner = unittest.TextTestRunner() 17 | result = runner.run(suite) 18 | if result.wasSuccessful() == True: 19 | exit(0) 20 | else: 21 | exit(1) 22 | -------------------------------------------------------------------------------- /tests/test_snmpv2c.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from smartsnmp_testcase import * 3 | 4 | class SNMPv2cTestCase(unittest.TestCase, SmartSNMPTestFramework, SmartSNMPTestCase): 5 | def setUp(self): 6 | self.snmp_setup("config/snmp.conf") 7 | self.version = "2c" 8 | self.community = "private" 9 | self.ip = "127.0.0.1" 10 | self.port = 161 11 | if self.snmp.isalive() == False: 12 | self.snmp.read() 13 | raise Exception("SNMP daemon start error!") 14 | 15 | def tearDown(self): 16 | if self.snmp.isalive() == False: 17 | self.snmp.read() 18 | raise Exception("SNMP daemon start error!") 19 | self.snmp_teardown() 20 | 21 | if __name__ == '__main__': 22 | unittest.main() 23 | -------------------------------------------------------------------------------- /scons_tools/endian_probe.py: -------------------------------------------------------------------------------- 1 | endian_test = r""" 2 | #include 3 | 4 | int main(void) 5 | { 6 | union endian_check { 7 | int i; 8 | char c; 9 | } endian; 10 | 11 | endian.i = 1; 12 | 13 | if (endian.c) { 14 | printf("L"); 15 | } else { 16 | printf("B"); 17 | } 18 | 19 | return 0; 20 | } 21 | """ 22 | def CheckEndian(context): 23 | context.Message("Checking for Endian...") 24 | result = context.TryRun(endian_test, '.c') 25 | if result[0] == 1 and result[1] == "L": 26 | endian = 'Little' 27 | elif result[0] == 1 and result[1] == "B": 28 | endian = 'Big' 29 | else: 30 | endian = 'Unkown' 31 | context.Result(endian) 32 | return endian 33 | -------------------------------------------------------------------------------- /config/agentx.conf: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | -- SmartSNMP Configuration File 3 | ------------------------------------------------------------------------------- 4 | 5 | protocol = 'agentx' 6 | port = 705 7 | 8 | mib_module_path = 'mibs' 9 | 10 | mib_modules = { 11 | ["1.3.6.1.2.1.1"] = 'system', 12 | ["1.3.6.1.2.1.2"] = 'interfaces', 13 | ["1.3.6.1.2.1.4"] = 'ip', 14 | ["1.3.6.1.2.1.6"] = 'tcp', 15 | ["1.3.6.1.2.1.7"] = 'udp', 16 | ["1.3.6.1.4.1.9999.1"] = 'two_cascaded_index_table', 17 | ["1.3.6.1.4.1.9999.2"] = 'three_cascaded_index_table', 18 | ["1.3.6.1.1"] = 'dummy', 19 | ["1.3.6.1.2.1.5"] = 'icmp', 20 | } 21 | -------------------------------------------------------------------------------- /doc/coverage.md: -------------------------------------------------------------------------------- 1 | Generate Coverage Report for SmartSNMP 2 | ====================================== 3 | 4 | Get the report from coveralls.io 5 | -------------------------------- 6 | 7 | [Click Here](https://coveralls.io/r/credosemi/smartsnmp) to get the report on coveralls.io. 8 | 9 | For C Code 10 | ---------- 11 | 12 | Add `--gcov=yes` to compile the source code with _gcov_ support. 13 | 14 | Later, after you run the smartsnmp, you can use _gcov_ to analyze the report. 15 | 16 | For Lua Code 17 | ------------ 18 | 19 | You will need to install luacov package by luarocks or your own way. 20 | 21 | Add `LUACOV=1` when run snmp agent or subagent, like 22 | 23 | sudo LUACOV=1 ./tests/snmp_daemon.sh 24 | 25 | or 26 | 27 | sudo LUACOV=1 ./tests/agentx_daemon.sh 28 | -------------------------------------------------------------------------------- /tests/snmpd.conf: -------------------------------------------------------------------------------- 1 | createUser rwNoAuthUser 2 | createUser rwAuthOnlyUser MD5 "rwAuthOnlyUser" 3 | createUser rwAuthPrivUser MD5 "rwAuthPrivUser" DES "rwAuthPrivUser" 4 | createUser roNoAuthUser 5 | createUser roAuthOnlyUser MD5 "roAuthOnlyUser" 6 | createUser roAuthPrivUser MD5 "roAuthPrivUser" DES "roAuthPrivUser" 7 | 8 | view internet included .1.3.6.1 9 | 10 | rocommunity public default -V internet 11 | rwcommunity private default -V internet 12 | 13 | rwuser rwNoAuthUser noauth -V internet 14 | rwuser rwAuthOnlyUser auth .1.3.6.1 15 | rwuser rwAuthPrivUser priv 16 | 17 | rouser roNoAuthUser noauth -V internet 18 | rouser roAuthOnlyUser auth .1.3.6.1 19 | rouser roAuthPrivUser priv 20 | 21 | master agentx 22 | agentXSocket tcp:localhost:705 23 | -------------------------------------------------------------------------------- /tests/test_snmpv3_noauth.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from smartsnmp_testcase import * 3 | 4 | class SNMPv3TestCase(unittest.TestCase, SmartSNMPTestFramework, SmartSNMPTestCase): 5 | def setUp(self): 6 | self.snmp_setup("config/snmp.conf") 7 | self.version = "3" 8 | self.user = "rwNoAuthUser" 9 | self.level = "noAuthNoPriv" 10 | self.auth_protocol = "" 11 | self.auth_key = "" 12 | self.priv_protocol = "" 13 | self.priv_key = "" 14 | self.ip = "127.0.0.1" 15 | self.port = 161 16 | if self.snmp.isalive() == False: 17 | self.snmp.read() 18 | raise Exception("SNMP daemon start error!") 19 | 20 | def tearDown(self): 21 | if self.snmp.isalive() == False: 22 | self.snmp.read() 23 | raise Exception("SNMP daemon start error!") 24 | self.snmp_teardown() 25 | 26 | if __name__ == '__main__': 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /tests/test_agentxv2c.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from smartsnmp_testcase import * 3 | 4 | class AgentXv2cTestCase(unittest.TestCase, SmartSNMPTestFramework, SmartSNMPTestCase): 5 | def setUp(self): 6 | self.agentx_setup("config/agentx.conf") 7 | self.version = "2c" 8 | self.community = "private" 9 | self.ip = "127.0.0.1" 10 | self.port = 161 11 | if self.netsnmp.isalive() == False: 12 | self.netsnmp.read() 13 | raise Exception("NetSNMP daemon start error!") 14 | if self.agentx.isalive() == False: 15 | self.agentx.read() 16 | raise Exception("AgentX daemon start error!") 17 | 18 | def tearDown(self): 19 | if self.netsnmp.isalive() == False: 20 | self.netsnmp.read() 21 | raise Exception("NetSNMP daemon start error!") 22 | if self.agentx.isalive() == False: 23 | self.agentx.read() 24 | raise Exception("AgentX daemon start error!") 25 | self.agentx_teardown() 26 | 27 | if __name__ == '__main__': 28 | unittest.main() 29 | -------------------------------------------------------------------------------- /mibs/dummy.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | 22 | local dummy = {} 23 | 24 | return dummy 25 | -------------------------------------------------------------------------------- /config/snmp.conf: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------- 2 | -- SmartSNMP Configuration File 3 | ------------------------------------------------------------------------------- 4 | 5 | protocol = 'snmp' 6 | port = 161 7 | 8 | communities = { 9 | { community = 'public', views = { ["."] = 'ro' } }, 10 | { community = 'private', views = { ["."] = 'rw' } }, 11 | } 12 | 13 | users = { 14 | { user = 'roNoAuthUser', views = { ["."] = 'ro' } }, 15 | { user = 'rwNoAuthUser', views = { ["."] = 'rw' } }, 16 | } 17 | 18 | mib_module_path = 'mibs' 19 | 20 | mib_modules = { 21 | ["1.3.6.1.2.1.1"] = 'system', 22 | ["1.3.6.1.2.1.2"] = 'interfaces', 23 | ["1.3.6.1.2.1.4"] = 'ip', 24 | ["1.3.6.1.2.1.6"] = 'tcp', 25 | ["1.3.6.1.2.1.7"] = 'udp', 26 | ["1.3.6.1.4.1.9999.1"] = 'two_cascaded_index_table', 27 | ["1.3.6.1.4.1.9999.2"] = 'three_cascaded_index_table', 28 | ["1.3.6.1.1"] = 'dummy', 29 | ["1.3.6.1.2.1.5"] = 'icmp', 30 | } 31 | -------------------------------------------------------------------------------- /scons_tools/select_probe.py: -------------------------------------------------------------------------------- 1 | select_test = """ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) 10 | { 11 | fd_set rfds, wfds; 12 | struct timeval tv; 13 | int i, maxfd, pfd[2]; 14 | const char *str = "Hello world"; 15 | char buf[100]; 16 | 17 | if (pipe(pfd) < 0) 18 | exit(-1); 19 | 20 | maxfd = pfd[0] < pfd[1] ? pfd[1] : pfd[0]; 21 | 22 | FD_SET(pfd[0], &rfds); 23 | FD_SET(pfd[1], &wfds); 24 | 25 | i = write(pfd[1], str, strlen(str)); 26 | assert(i == strlen(str)); 27 | i = read(pfd[0], buf, sizeof(buf)); 28 | assert(i == strlen(str)); 29 | 30 | tv.tv_sec = 0; 31 | tv.tv_usec = 0; 32 | int nfds = select(maxfd + 1, &rfds, &wfds, NULL, &tv); 33 | if (nfds <= 0) 34 | exit(-1); 35 | 36 | return 0; 37 | } 38 | """ 39 | def CheckSelect(context): 40 | context.Message("Checking for select...") 41 | result = context.TryLink(select_test, '.c') 42 | context.Result(result) 43 | return result 44 | -------------------------------------------------------------------------------- /scons_tools/kqueue_probe.py: -------------------------------------------------------------------------------- 1 | kqueue_test = """ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) 10 | { 11 | int i, kqfd, pfd[2]; 12 | struct kevent ke, kev[2]; 13 | const char *str = "Hello world"; 14 | char buf[100]; 15 | 16 | if (pipe(pfd) < 0) 17 | exit(-1); 18 | 19 | kqfd = kqueue(); 20 | if (kqfd < 0) 21 | exit(-1); 22 | 23 | EV_SET(&ke, pfd[0], EVFILT_READ, EV_ADD, 0, 0, NULL); 24 | EV_SET(&ke, pfd[1], EVFILT_WRITE, EV_ADD, 0, 0, NULL); 25 | 26 | i = write(pfd[1], str, strlen(str)); 27 | assert(i == strlen(str)); 28 | i = read(pfd[0], buf, sizeof(buf)); 29 | assert(i == strlen(str)); 30 | 31 | int nfds = kevent(kqfd, NULL, 0, kev, 2, NULL); 32 | if (nfds <= 0) 33 | exit(-1); 34 | 35 | close(kqfd); 36 | 37 | return 0; 38 | } 39 | """ 40 | def CheckKqueue(context): 41 | context.Message("Checking for kqueue...") 42 | result = context.TryLink(kqueue_test, '.c') 43 | context.Result(result) 44 | return result 45 | -------------------------------------------------------------------------------- /tests/test_agentxv3_noauth.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from smartsnmp_testcase import * 3 | 4 | class AgentXv3TestCase(unittest.TestCase, SmartSNMPTestFramework, SmartSNMPTestCase): 5 | def setUp(self): 6 | self.agentx_setup("config/agentx.conf") 7 | self.version = "3" 8 | self.user = "rwNoAuthUser" 9 | self.level = "noAuthNoPriv" 10 | self.auth_protocol = "" 11 | self.auth_key = "" 12 | self.priv_protocol = "" 13 | self.priv_key = "" 14 | self.ip = "127.0.0.1" 15 | self.port = 161 16 | if self.netsnmp.isalive() == False: 17 | self.netsnmp.read() 18 | raise Exception("NetSNMP daemon start error!") 19 | if self.agentx.isalive() == False: 20 | self.agentx.read() 21 | raise Exception("AgentX daemon start error!") 22 | 23 | def tearDown(self): 24 | if self.netsnmp.isalive() == False: 25 | self.netsnmp.read() 26 | raise Exception("NetSNMP daemon start error!") 27 | if self.agentx.isalive() == False: 28 | self.agentx.read() 29 | raise Exception("AgentX daemon start error!") 30 | self.agentx_teardown() 31 | 32 | if __name__ == '__main__': 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /tests/test_agentxv3_authonly.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from smartsnmp_testcase import * 3 | 4 | class AgentXv3TestCase(unittest.TestCase, SmartSNMPTestFramework, SmartSNMPTestCase): 5 | def setUp(self): 6 | self.agentx_setup("config/agentx.conf") 7 | self.version = "3" 8 | self.user = "rwAuthOnlyUser" 9 | self.level = "authNoPriv" 10 | self.auth_protocol = "MD5" 11 | self.auth_key = "rwAuthOnlyUser" 12 | self.priv_protocol = "" 13 | self.priv_key = "" 14 | self.ip = "127.0.0.1" 15 | self.port = 161 16 | if self.netsnmp.isalive() == False: 17 | self.netsnmp.read() 18 | raise Exception("NetSNMP daemon start error!") 19 | if self.agentx.isalive() == False: 20 | self.agentx.read() 21 | raise Exception("AgentX daemon start error!") 22 | 23 | def tearDown(self): 24 | if self.netsnmp.isalive() == False: 25 | self.netsnmp.read() 26 | raise Exception("NetSNMP daemon start error!") 27 | if self.agentx.isalive() == False: 28 | self.agentx.read() 29 | raise Exception("AgentX daemon start error!") 30 | self.agentx_teardown() 31 | 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /tests/test_agentxv3_authpriv.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from smartsnmp_testcase import * 3 | 4 | class AgentXv3TestCase(unittest.TestCase, SmartSNMPTestFramework, SmartSNMPTestCase): 5 | def setUp(self): 6 | self.agentx_setup("config/agentx.conf") 7 | self.version = "3" 8 | self.user = "rwAuthPrivUser" 9 | self.level = "authPriv" 10 | self.auth_protocol = "MD5" 11 | self.auth_key = "rwAuthPrivUser" 12 | self.priv_protocol = "DES" 13 | self.priv_key = "rwAuthPrivUser" 14 | self.ip = "127.0.0.1" 15 | self.port = 161 16 | if self.netsnmp.isalive() == False: 17 | self.netsnmp.read() 18 | raise Exception("NetSNMP daemon start error!") 19 | if self.agentx.isalive() == False: 20 | self.agentx.read() 21 | raise Exception("AgentX daemon start error!") 22 | 23 | def tearDown(self): 24 | if self.netsnmp.isalive() == False: 25 | self.netsnmp.read() 26 | raise Exception("NetSNMP daemon start error!") 27 | if self.agentx.isalive() == False: 28 | self.agentx.read() 29 | raise Exception("AgentX daemon start error!") 30 | self.agentx_teardown() 31 | 32 | if __name__ == '__main__': 33 | unittest.main() 34 | -------------------------------------------------------------------------------- /scons_tools/epoll_probe.py: -------------------------------------------------------------------------------- 1 | epoll_test = """ 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | int main(void) 10 | { 11 | int i, epfd, pfd[2]; 12 | struct epoll_event ee[2], ee2[2]; 13 | const char *str = "Hello world"; 14 | char buf[100]; 15 | 16 | if (pipe(pfd) < 0) 17 | exit(-1); 18 | 19 | epfd = epoll_create(5); 20 | if (epfd < 0) 21 | exit(-1); 22 | 23 | ee[0].events = EPOLLIN; 24 | ee[0].data.fd = pfd[0]; 25 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[0], &ee[0]) < 0) 26 | exit(-1); 27 | 28 | ee[1].events = EPOLLOUT; 29 | ee[1].data.fd = pfd[1]; 30 | if (epoll_ctl(epfd, EPOLL_CTL_ADD, pfd[1], &ee[1]) < 0) 31 | exit(-1); 32 | 33 | i = write(pfd[1], str, strlen(str)); 34 | assert(i == strlen(str)); 35 | i = read(pfd[0], buf, sizeof(buf)); 36 | assert(i == strlen(str)); 37 | 38 | int nfds = epoll_wait(epfd, ee2, 2, 0); 39 | if (nfds <= 0) 40 | exit(-1); 41 | 42 | close(epfd); 43 | 44 | return 0; 45 | } 46 | """ 47 | def CheckEpoll(context): 48 | context.Message("Checking for epoll...") 49 | result = context.TryLink(epoll_test, '.c') 50 | context.Result(result) 51 | return result 52 | -------------------------------------------------------------------------------- /core/transport.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _TRANSPORT_H_ 22 | #define _TRANSPORT_H_ 23 | 24 | #include 25 | 26 | #define TRANS_BUF_SIZ (65536) 27 | 28 | struct transport_operation { 29 | const char *name; 30 | int (*init)(int port); 31 | void (*running)(void); 32 | void (*stop)(void); 33 | void (*send)(uint8_t *buf, int len); 34 | }; 35 | 36 | extern struct transport_operation snmp_trans_ops; 37 | extern struct transport_operation agentx_trans_ops; 38 | 39 | #endif /* _TRANSPORT_H_ */ 40 | -------------------------------------------------------------------------------- /tests/netsnmp_build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export NET_SNMP_VERSION=5.7.2.1 4 | export NET_SNMP_SRC_URL=https://sourceforge.net/projects/net-snmp/files/net-snmp/${NET_SNMP_VERSION}/net-snmp-${NET_SNMP_VERSION}.tar.gz 5 | export NET_SNMP_ROOT_DIR=`pwd`/tests 6 | 7 | # Tarball 8 | echo Downloading Net-SNMP ${NET_SNMP_VERSION} 9 | if [ -f ${NET_SNMP_ROOT_DIR}/net-snmp-${NET_SNMP_VERSION}.tar.gz ]; then 10 | echo "Tarball has been downloaded" 11 | else 12 | wget -P${NET_SNMP_ROOT_DIR} ${NET_SNMP_SRC_URL} 13 | fi 14 | 15 | # Directory 16 | mkdir -p ${NET_SNMP_ROOT_DIR}/net-snmp-src 17 | mkdir -p ${NET_SNMP_ROOT_DIR}/net-snmp-release 18 | tar xzvf ${NET_SNMP_ROOT_DIR}/net-snmp-${NET_SNMP_VERSION}.tar.gz -C ${NET_SNMP_ROOT_DIR}/net-snmp-src 19 | cd ${NET_SNMP_ROOT_DIR}/net-snmp-src/net-snmp-${NET_SNMP_VERSION} 20 | 21 | # Configure, make and install 22 | ./configure --with-default-snmp-version="3" --with-sys-contact="contact" --with-sys-location="location" --with-logfile="/var/log/snmpd.log" --with-persistent-directory="/var/net-snmp" --prefix=${NET_SNMP_ROOT_DIR}/net-snmp-release --disable-applications --disable-manuals --disable-scripts --disable-mibs --disable-embedded-perl --disable-deprecated --without-perl-modules --with-out-mib-modules="mibII ucd_snmp agent_mibs notification notification-log-mib target utilities disman/event disman/schedule host" --with-mib_modules="mibII/vacm_vars" 23 | make 24 | make install 25 | -------------------------------------------------------------------------------- /core/ev_loop.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _SNMP_EVENT_LOOP_H_ 22 | #define _SNMP_EVENT_LOOP_H_ 23 | 24 | #define SNMP_EV_NONE 0 25 | #define SNMP_EV_READ 1 26 | #define SNMP_EV_WRITE 2 27 | 28 | typedef void (*transport_handler)(int sock, unsigned char flag, void *ud); 29 | 30 | void snmp_event_init(void); 31 | void snmp_event_done(void); 32 | void snmp_event_run(void); 33 | int snmp_event_add(int fd, unsigned char flag, transport_handler cb, void *ud); 34 | void snmp_event_remove(int fd, unsigned char flag); 35 | 36 | #endif /* _SNMP_EVENT_LOOP_H_ */ 37 | -------------------------------------------------------------------------------- /core/protocol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _PROTOCOL_H_ 22 | #define _PROTOCOL_H_ 23 | 24 | #include 25 | #include "asn1.h" 26 | 27 | struct protocol_operation { 28 | const char *name; 29 | int (*init)(int port); 30 | int (*open)(void); 31 | int (*close)(void); 32 | void (*run)(void); 33 | int (*reg)(const oid_t *grp_id, int id_len, int grp_cb); 34 | int (*unreg)(const oid_t *grp_id, int id_len); 35 | void (*receive)(uint8_t *buf, int len); 36 | void (*send)(uint8_t *buf, int len); 37 | }; 38 | 39 | extern struct protocol_operation snmp_prot_ops; 40 | extern struct protocol_operation agentx_prot_ops; 41 | 42 | #endif /* _PROTOCOL_H_ */ 43 | -------------------------------------------------------------------------------- /doc/api.md: -------------------------------------------------------------------------------- 1 | SmartSNMP API 2 | ============= 3 | 4 | In general, if you want to embed **SmartSNMP** in your application, then you 5 | need to use SmartSNMP as a lua library instead of using `bin/snmpd`. 6 | 7 | Use SmartSNMP as A Lua Library 8 | ------------------------------ 9 | 10 | First, you need to import SmartSNMP library like this. 11 | 12 | local smartsnmp = require "smartsnmp" 13 | 14 | And now, SmartSNMP provide following API. 15 | 16 | - `smartsnmp.init(protocol, port)` : initialize agent with specified protocol and port number. 17 | - `protocol` : protocol name, eg: 'snmp'; 18 | - `port` : port number, eg: 161. 19 | - `smartsnmp.open()` : open the agent. 20 | - `smartsnmp.start() : start to run the agent. 21 | - `smartsnmp.set_ro_community(community, oid)` : set read only community. 22 | - `community` : read only community string, eg: 'public'; 23 | - `oid` : oid view to be registered, eg: `{1,3,6,1,2,1,1}`. 24 | - `smartsnmp.set_rw_community(community, oid)` : set read/write community. 25 | - `community` : read write community string, eg: 'private'; 26 | - `oid` : oid view to be registered, eg: `{1,3,6,1,2,1,4}`. 27 | - `smartsnmp.set_ro_user(user, oid)` : set read only user. 28 | - `user` : read only user name, eg: 'Jack'; 29 | - `oid` : oid view to be registered, eg: `{1,3,6,1,2,1,1}`. 30 | - `smartsnmp.set_rw_user(user, oid)` : set read/write user. 31 | - `user` : read write user name, eg: 'Jack'; 32 | - `oid` : oid view to be registered, eg: `{1,3,6,1,2,1,4}`. 33 | - `smartsnmp.register_mib_group(oid, mib_group, name)` : register mib group into core. 34 | - `oid` : group oid to be registered, eg: `{1,3,6,1,2,1,1}`; 35 | - `mib_group` : generated by SmartSNMP group generator; 36 | - `name` : mib group name. 37 | - `smartsnmp.unregister_mib_group(mib_oid)` : unregister mib group. 38 | - `oid` : group oid to be unregistered, eg: `{1,3,6,1,2,1,1}`. 39 | - `smartsnmp.group_index_table_check(mib_group, name)` : Check if the mib group can be traversed in lexicographical order. 40 | - `mib_group` : object generated by SmartSNMP group generator; 41 | - `name` : mib group name. 42 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | 3 | env: 4 | matrix: 5 | - "LUA=lua5.1 TRANSPORT=built-in EVLOOP=epoll" 6 | - "LUA=lua5.1 TRANSPORT=built-in EVLOOP=select" 7 | - "LUA=lua5.1 TRANSPORT=libevent" 8 | - "LUA=lua5.1 TRANSPORT=uloop" 9 | 10 | install: 11 | - "sudo apt-get install -yq $LUA $lib$LUA-0-dev python-pexpect snmp luarocks" 12 | - "sudo -H pip -q install PyYAML cpp-coveralls" 13 | - "sudo luarocks install Lua-cURL" 14 | - "sudo luarocks install luacov-coveralls" 15 | - "sudo luarocks install lunitx" 16 | - "if [ $TRANSPORT = 'libevent' ]; then 17 | sudo apt-get install -yq libevent-dev; 18 | fi" 19 | - "if [ $TRANSPORT = 'uloop' ]; then 20 | ( 21 | rm -rf json-c && 22 | git clone https://github.com/json-c/json-c.git && 23 | cd json-c && 24 | sh autogen.sh && 25 | ./configure && 26 | make && 27 | sudo make install 28 | ) && 29 | ( 30 | rm -rf libubox && 31 | git clone http://git.openwrt.org/project/libubox.git && 32 | cd libubox && 33 | cmake . && 34 | make && 35 | cp libubox.so .. 36 | ); 37 | fi" 38 | - "wget -q https://raw.githubusercontent.com/credosemi/pub/master/net-snmp-pre-built.tar.gz" 39 | - "tar xzf net-snmp-pre-built.tar.gz" 40 | 41 | script: 42 | - "if [ $TRANSPORT = 'uloop' ]; then 43 | export LDFLAGS='-L./libubox' && 44 | export CFLAGS='-I.'; 45 | fi" 46 | - "if [ $TRANSPORT = 'built-in' ]; then 47 | scons --transport=$TRANSPORT --evloop=$EVLOOP --gcov=yes; 48 | else 49 | scons --transport=$TRANSPORT --gcov=yes; 50 | fi" 51 | - "sudo LUACOV=1 SYS_LUA_PATH=`$LUA -e 'print(package.path)'` LUA=$LUA python ./tests/test_all.py" 52 | - "sudo chmod 666 luacov.stats.out" 53 | - "$LUA -lluacov tests/test_utils.lua" 54 | 55 | after_success: 56 | - "cpp-coveralls -b . -e .sconf_temp `ls core/*.gcno | sed 's/\\.gcno$/.c/' | sed 's/^/-i /'` --dump c.report.json" 57 | - "luacov-coveralls -j c.report.json -i bin/smartsnmpd -i lualib/smartsnmp/init.lua -i lualib/smartsnmp/utils.lua" 58 | 59 | after_failure: 60 | - "cat config.log" 61 | - "cat tests/test.log" 62 | 63 | -------------------------------------------------------------------------------- /tests/smartsnmp_testcase.py: -------------------------------------------------------------------------------- 1 | from smartsnmp_testframework import * 2 | 3 | class SmartSNMPTestCase: 4 | def test_snmpget(self): 5 | self.snmpget_expect(".1.3.6.1.2.1.2.1.0", Integer(5)) 6 | self.snmpget_expect(".", SNMPNoSuchObject()) 7 | self.snmpget_expect(".0", SNMPNoSuchObject()) 8 | self.snmpget_expect(".1.3", SNMPNoSuchObject()) 9 | self.snmpget_expect(".1.4", SNMPNoSuchObject()) 10 | self.snmpget_expect(".1.3.6.1.2.1.1.9.1.2.1", Oid(".1.3.6.1.2.1.4")) 11 | self.snmpget_expects(((".1.3.6.1.2.1.1.9.1.0", SNMPNoSuchObject()), (".1.3.6.1.2.1.1.2.0", Oid(".1.3.6.1.2.1.1")))) 12 | self.snmpget_expect(".1.3.6.1.2.1.1.9.1.1", SNMPNoSuchInstance()) 13 | self.snmpget_expect(".1.3.6.1.2.1.1.9.1.2", SNMPNoSuchInstance()) 14 | self.snmpget_expect(".1.3.6.1.2.1.1.9.1.5", SNMPNoSuchObject()) 15 | self.snmpget_expect(".1.3.6.1.2.1.1.0", SNMPNoSuchObject()) 16 | 17 | # a correct request for MIB-II/UDP should be .1.3.6.1.2.1.7.5.1.2.4.x.x.x.x.p 18 | self.snmpget_expect(".1.3.6.1.2.1.7.5.1.2.4", SNMPNoSuchInstance()) 19 | self.snmpget_expect(".1.3.6.1.2.1.7.5.1.2.4.1.1", SNMPNoSuchInstance()) 20 | 21 | def test_snmpgetnext(self): 22 | self.snmpgetnext_expect(".", ".1.3.6.1.2.1.1.1.0", OctStr(r".*")) 23 | self.snmpgetnext_expect(".0", ".1.3.6.1.2.1.1.1.0", OctStr(r".*")) 24 | self.snmpgetnext_expect(".1.3", ".1.3.6.1.2.1.1.1.0", OctStr(r".*")) 25 | self.snmpgetnext_expect(".1.4", ".1.4", SNMPEndOfMib()) 26 | self.snmpgetnext_expect(".1.5.6.7.8.100", ".1.5.6.7.8.100", SNMPEndOfMib()) 27 | 28 | def test_snmpset(self): 29 | self.snmpset_expect(".1.3.6.1.2.1.1.9.1.1", Integer(1), SNMPNoAccess()) 30 | self.snmpset_expect(".1.3.6.1.2.1.4.1.0", OctStr("SmartSNMP"), SNMPWrongType()) 31 | self.snmpset_expect(".1.3.6.1.2.1.4.1.0", Integer(8888), Integer(8888)) 32 | self.snmpset_expect(".1.3.6.1.2.1.4.0", Integer(8888), SNMPNotWritable()) 33 | 34 | if self.version == '3': 35 | self.user = self.user.replace('rw', 'ro') 36 | self.auth_key = self.auth_key.replace('rw', 'ro') 37 | self.priv_key = self.priv_key.replace('rw', 'ro') 38 | else: 39 | self.community = "public" 40 | 41 | self.snmpset_expect(".1.3.6.1.2.1.1.9.1.1", Integer(1), SNMPNoAccess()) 42 | self.snmpset_expect(".1.3.6.1.2.1.4.1.0", OctStr("SmartSNMP"), SNMPNoAccess()) 43 | self.snmpset_expect(".1.3.6.1.2.1.4.1.0", Integer(8888), SNMPNoAccess()) 44 | self.snmpset_expect(".1.3.6.1.2.1.4.0", Integer(8888), SNMPNoAccess()) 45 | 46 | def test_snmpwalk(self): 47 | self.snmpwalk_expect(".") 48 | -------------------------------------------------------------------------------- /core/ev_select.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | struct select_env { 25 | fd_set rfds, wfds; 26 | fd_set rfds_, wfds_; 27 | }; 28 | 29 | static struct select_env env; 30 | 31 | static int 32 | __ev_init(void) 33 | { 34 | FD_ZERO(&env.rfds); 35 | FD_ZERO(&env.wfds); 36 | return 0; 37 | } 38 | 39 | static void 40 | __ev_done(void) 41 | { 42 | return; 43 | } 44 | 45 | static void 46 | __ev_add(struct snmp_event *event, unsigned char flag) 47 | { 48 | if (flag & SNMP_EV_READ) { 49 | FD_SET(event->fd, &env.rfds); 50 | } 51 | if (flag & SNMP_EV_WRITE) { 52 | FD_SET(event->fd, &env.wfds); 53 | } 54 | } 55 | 56 | static void 57 | __ev_remove(struct snmp_event *event, unsigned char flag) 58 | { 59 | if (flag & SNMP_EV_READ) { 60 | FD_CLR(event->fd, &env.rfds); 61 | } 62 | if (flag & SNMP_EV_WRITE) { 63 | FD_CLR(event->fd, &env.wfds); 64 | } 65 | } 66 | 67 | static int 68 | __ev_poll(struct snmp_event_loop *ev_loop) 69 | { 70 | int i; 71 | 72 | memcpy(&env.rfds_, &env.rfds, sizeof(fd_set)); 73 | memcpy(&env.wfds_, &env.wfds, sizeof(fd_set)); 74 | 75 | int nfds = select(ev_loop->max_fd + 1, &env.rfds_, &env.wfds_, NULL, NULL); 76 | if (nfds > 0) { 77 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 78 | struct snmp_event *event = &ev_loop->event[i]; 79 | if (event->flag == SNMP_EV_NONE) { 80 | continue; 81 | } 82 | if (event->flag & SNMP_EV_READ && FD_ISSET(event->fd, &env.rfds_)) { 83 | event->read = 1; 84 | } 85 | if (event->flag & SNMP_EV_WRITE && FD_ISSET(event->fd, &env.wfds_)) { 86 | event->write = 1; 87 | } 88 | } 89 | } 90 | 91 | return nfds; 92 | } 93 | -------------------------------------------------------------------------------- /tests/test_utils.lua: -------------------------------------------------------------------------------- 1 | local lunit = require "lunit" 2 | 3 | package.path = 'lualib/?/init.lua;lualib/?.lua;'..package.path 4 | 5 | local utils = require "smartsnmp.utils" 6 | 7 | module("test_utils", lunit.testcase, package.seeall) 8 | 9 | test_iter1 = function() 10 | -- test iter 11 | local i = 0 12 | for v in utils.iter({1, 2, 3, 4, 5}) do 13 | i = i + 1 14 | assert_true(v == i) 15 | end 16 | assert_true(i == 5) 17 | end 18 | 19 | test_iter2 = function() 20 | -- test iter 21 | local i = utils.iter(function() return nil end) 22 | 23 | local x = true 24 | for v in i do 25 | x = false 26 | end 27 | assert_true(x) 28 | end 29 | 30 | -- helper: compare 2 array table 31 | local array_compare = function(ta, tb) 32 | if #ta ~= #tb then 33 | return false 34 | end 35 | for i, a in ipairs(ta) do 36 | if a ~= tb[i] then 37 | return false 38 | end 39 | end 40 | return true 41 | end 42 | 43 | -- test map 44 | test_map = function() 45 | assert_true(array_compare( 46 | utils.map( 47 | function (x) 48 | return x + 1 49 | end, 50 | {1, 2, 3, 4, 5} 51 | ), 52 | {2, 3, 4, 5, 6} 53 | )) 54 | end 55 | 56 | -- test filter 57 | test_filter = function() 58 | assert_true(array_compare( 59 | utils.filter( 60 | function (x) 61 | return (x % 2) == 1 62 | end, 63 | {1, 2, 3, 4, 5} 64 | ), 65 | {1, 3, 5} 66 | )) 67 | end 68 | 69 | -- test reduce w/o init 70 | test_reduce1 = function() 71 | assert_true( 72 | utils.reduce( 73 | function (x, y) 74 | return x * y 75 | end, 76 | {1, 2, 3, 4, 5} 77 | ) == 120 78 | ) 79 | end 80 | 81 | -- test reduce with init 82 | test_reduce2 = function() 83 | assert_true( 84 | utils.reduce( 85 | function (x, y) 86 | return x * y 87 | end, 88 | {1, 2, 3, 4, 5}, 89 | 0 90 | ) == 0 91 | ) 92 | end 93 | 94 | -- test str2mac 95 | test_str2mac = function() 96 | assert_true(array_compare(utils.str2mac("00:00:00:00:00:00"), {0, 0, 0, 0, 0, 0})) 97 | assert_true(array_compare(utils.str2mac("ff:ff:ff:ff:ff:ff"), {255, 255, 255, 255, 255, 255})) 98 | assert_true(array_compare(utils.str2mac("FF:FF:FF:FF:FF:FF"), {255, 255, 255, 255, 255, 255})) 99 | assert_true(array_compare(utils.str2mac("11:22:33:44:55:66"), {0x11, 0x22, 0x33, 0x44, 0x55, 0x66})) 100 | end 101 | 102 | -- test mac2oct 103 | test_mac2oct = function() 104 | assert_true(utils.mac2oct({0x61, 0x62, 0x63, 0x64, 0x65, 0x66}) == 'abcdef') 105 | assert_true(utils.mac2oct("61:62:63:64:65:66") == 'abcdef') 106 | end 107 | 108 | lunit.main() 109 | -------------------------------------------------------------------------------- /core/ev_kqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | struct kqueue_env { 25 | int kqfd; 26 | struct kevent event[SNMP_MAX_EVENTS]; 27 | }; 28 | 29 | static struct kqueue_env env; 30 | 31 | static int 32 | __ev_init(void) 33 | { 34 | env.kqfd = kqueue(); 35 | return env.kqfd; 36 | } 37 | 38 | static void 39 | __ev_done(void) 40 | { 41 | close(env.kqfd); 42 | } 43 | 44 | static void 45 | __ev_add(struct snmp_event *event, unsigned char flag) 46 | { 47 | struct kevent ke; 48 | 49 | if (flag & SNMP_EV_READ) { 50 | EV_SET(&ke, event->fd, EVFILT_READ, EV_ADD, 0, 0, NULL); 51 | } 52 | if (flag & SNMP_EV_WRITE) { 53 | EV_SET(&ke, event->fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL); 54 | } 55 | kevent(env.kqfd, &ke, 1, NULL, 0, NULL); 56 | } 57 | 58 | static void 59 | __ev_remove(struct snmp_event *event, unsigned char flag) 60 | { 61 | struct kevent ke; 62 | 63 | if (flag & SNMP_EV_READ) { 64 | EV_SET(&ke, event->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); 65 | } 66 | if (flag & SNMP_EV_WRITE) { 67 | EV_SET(&ke, event->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); 68 | } 69 | kevent(state->kqfd, &ke, 1, NULL, 0, NULL); 70 | } 71 | 72 | static int 73 | __ev_poll(struct snmp_event_loop *ev_loop) 74 | { 75 | int i; 76 | 77 | int nfds = kevent(kqfd, NULL, 0, env.event, SNMP_MAX_EVENTS, NULL); 78 | if (nfds > 0) { 79 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 80 | struct kevent *ke = &env.event[i]; 81 | struct snmp_event *event = &ev_loop->event[i]; 82 | if (ke->filter == EVFILT_READ) { 83 | event->read = 1; 84 | } 85 | if (ke->filter == EVFILT_WRITE) { 86 | event->write = 1; 87 | } 88 | } 89 | } 90 | 91 | return nfds; 92 | } 93 | -------------------------------------------------------------------------------- /core/snmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "snmp.h" 32 | #include "transport.h" 33 | #include "protocol.h" 34 | #include "mib.h" 35 | #include "util.h" 36 | 37 | struct snmp_datagram snmp_datagram; 38 | 39 | /* Receive SNMP request datagram from transport layer */ 40 | static void 41 | snmpd_receive(uint8_t *buf, int len) 42 | { 43 | snmpd_recv(buf, len); 44 | } 45 | 46 | /* Send SNMP response datagram to transport layer */ 47 | static void 48 | snmpd_send(uint8_t *buf, int len) 49 | { 50 | snmp_trans_ops.send(buf, len); 51 | } 52 | 53 | /* Register mib group node */ 54 | static int 55 | snmpd_mib_node_reg(const oid_t *grp_id, int id_len, int grp_cb) 56 | { 57 | return mib_node_reg(grp_id, id_len, grp_cb); 58 | } 59 | 60 | /* Unregister mib group nodes */ 61 | static int 62 | snmpd_mib_node_unreg(const oid_t *grp_id, int id_len) 63 | { 64 | mib_node_unreg(grp_id, id_len); 65 | return 0; 66 | } 67 | 68 | static int 69 | snmpd_init(int port) 70 | { 71 | INIT_LIST_HEAD(&snmp_datagram.vb_in_list); 72 | INIT_LIST_HEAD(&snmp_datagram.vb_out_list); 73 | return snmp_trans_ops.init(port); 74 | } 75 | 76 | static int 77 | snmpd_open(void) 78 | { 79 | /* dummy */ 80 | return 0; 81 | } 82 | 83 | static int 84 | snmpd_close(void) 85 | { 86 | snmp_trans_ops.stop(); 87 | return 0; 88 | } 89 | 90 | static void 91 | snmpd_run(void) 92 | { 93 | return snmp_trans_ops.running(); 94 | } 95 | 96 | struct protocol_operation snmp_prot_ops = { 97 | "snmp", 98 | snmpd_init, 99 | snmpd_open, 100 | snmpd_close, 101 | snmpd_run, 102 | snmpd_mib_node_reg, 103 | snmpd_mib_node_unreg, 104 | snmpd_receive, 105 | snmpd_send, 106 | }; 107 | -------------------------------------------------------------------------------- /core/ev_epoll.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | struct epoll_env { 25 | int epfd; 26 | struct epoll_event event[SNMP_MAX_EVENTS]; 27 | }; 28 | 29 | static struct epoll_env env; 30 | 31 | static int 32 | __ev_init(void) 33 | { 34 | env.epfd = epoll_create(32); 35 | return env.epfd; 36 | } 37 | 38 | static void 39 | __ev_done(void) 40 | { 41 | close(env.epfd); 42 | } 43 | 44 | static void 45 | __ev_add(struct snmp_event *event, unsigned char flag) 46 | { 47 | struct epoll_event ee; 48 | int op = event->flag == SNMP_EV_NONE ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; 49 | 50 | ee.events = 0; 51 | ee.data.fd = event->fd; 52 | if (flag & SNMP_EV_READ) { 53 | ee.events |= EPOLLIN; 54 | } 55 | if (flag & SNMP_EV_WRITE) { 56 | ee.events |= EPOLLOUT; 57 | } 58 | epoll_ctl(env.epfd, op, event->fd, &ee); 59 | } 60 | 61 | static void 62 | __ev_remove(struct snmp_event *event, unsigned char flag) 63 | { 64 | struct epoll_event ee; 65 | 66 | ee.events = EPOLLIN | EPOLLOUT; 67 | ee.data.fd = event->fd; 68 | if (flag & SNMP_EV_READ) { 69 | ee.events &= ~EPOLLIN; 70 | } 71 | if (flag & SNMP_EV_WRITE) { 72 | ee.events &= ~EPOLLOUT; 73 | } 74 | if (ee.events == 0) { 75 | epoll_ctl(env.epfd, EPOLL_CTL_DEL, event->fd, &ee); 76 | } else { 77 | epoll_ctl(env.epfd, EPOLL_CTL_MOD, event->fd, &ee); 78 | } 79 | } 80 | 81 | static int 82 | __ev_poll(struct snmp_event_loop *ev_loop) 83 | { 84 | int i; 85 | 86 | int nfds = epoll_wait(env.epfd, env.event, SNMP_MAX_EVENTS, -1); 87 | if (nfds > 0) { 88 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 89 | struct epoll_event *ee = &env.event[i]; 90 | struct snmp_event *event = &ev_loop->event[i]; 91 | if (ee->events & EPOLLIN) { 92 | event->read = 1; 93 | } 94 | if (ee->events & EPOLLOUT) { 95 | event->write = 1; 96 | } 97 | } 98 | } 99 | 100 | return nfds; 101 | } 102 | -------------------------------------------------------------------------------- /mibs/three_cascaded_index_table.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | 22 | local ThreeIndexTableIdxA = 1 23 | local ThreeIndexTableIdxB = 2 24 | local ThreeIndexTableIdxC = 3 25 | 26 | -- cascade multi-index 27 | local three_dim_cache = { 28 | cascade = true, 29 | { 1, 3, 5, 7}, 30 | { 2, 3 }, 31 | { 32, 123, 87 }, 32 | } 33 | 34 | local ThreeIndexTable = { 35 | [1] = { 36 | [1] = { 37 | indexes = three_dim_cache, 38 | [ThreeIndexTableIdxA] = mib.ConstInt(function (sub_oid) 39 | for i, card_index in ipairs(three_dim_cache[1]) do 40 | if sub_oid[1] == card_index then 41 | return card_index 42 | end 43 | end 44 | return nil 45 | end), 46 | [ThreeIndexTableIdxB] = mib.ConstInt(function (sub_oid) 47 | for i, cnu_index in ipairs(three_dim_cache[2]) do 48 | if sub_oid[2] == cnu_index then 49 | return cnu_index 50 | end 51 | end 52 | return nil 53 | end), 54 | [ThreeIndexTableIdxC] = mib.ConstInt(function (sub_oid) 55 | for i, port_index in ipairs(three_dim_cache[3]) do 56 | if sub_oid[3] == port_index then 57 | return port_index 58 | end 59 | end 60 | return nil 61 | end), 62 | } 63 | }, 64 | } 65 | 66 | return ThreeIndexTable 67 | -------------------------------------------------------------------------------- /core/asn1.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _ANS1_H_ 22 | #define _ANS1_H_ 23 | 24 | #include 25 | 26 | #define MIB_OID_MAX_LEN 64 27 | #define MIB_VALUE_MAX_LEN (1024) 28 | #define MIB_TAG_VALID(tag) ((tag) < ASN1_TAG_NO_SUCH_OBJ) 29 | 30 | /* ASN1 variable type */ 31 | enum asn1_variable_type { 32 | ASN1_TAG_BOOL = 0x01, 33 | ASN1_TAG_INT = 0x02, 34 | ASN1_TAG_BITSTR = 0x03, 35 | ASN1_TAG_OCTSTR = 0x04, 36 | ASN1_TAG_NUL = 0x05, 37 | ASN1_TAG_OBJID = 0x06, 38 | ASN1_TAG_SEQ = 0x30, 39 | ASN1_TAG_IPADDR = 0x40, 40 | ASN1_TAG_CNT = 0x41, 41 | ASN1_TAG_GAU = 0x42, 42 | ASN1_TAG_TIMETICKS = 0x43, 43 | ASN1_TAG_OPAQ = 0x44, 44 | ASN1_TAG_CNT64 = 0x46, 45 | ASN1_TAG_NO_SUCH_OBJ = 0x80, 46 | ASN1_TAG_NO_SUCH_INST = 0x81, 47 | ASN1_TAG_END_OF_MIB_VIEW = 0x82, 48 | }; 49 | 50 | /* A map of simple data type syntax between ANSI C and ASN.1 */ 51 | typedef int integer_t; 52 | typedef char octstr_t; 53 | typedef char opaq_t; 54 | typedef unsigned char ipaddr_t; 55 | typedef unsigned int oid_t; 56 | typedef unsigned int count_t; 57 | typedef unsigned int count32_t; 58 | typedef unsigned long count64_t; 59 | typedef unsigned int gauge_t; 60 | typedef unsigned int timeticks_t; 61 | 62 | /* variable as TLV */ 63 | typedef struct { 64 | uint8_t tag; 65 | /* Number of elements according to tag */ 66 | uint16_t len; 67 | union { 68 | integer_t i; 69 | count_t c; 70 | count32_t c32; 71 | count64_t c64; 72 | ipaddr_t ip[6]; 73 | octstr_t s[MIB_VALUE_MAX_LEN]; 74 | opaq_t o[MIB_VALUE_MAX_LEN]; 75 | oid_t id[MIB_OID_MAX_LEN]; 76 | gauge_t g; 77 | timeticks_t t; 78 | } value; 79 | } Variable; 80 | 81 | #define tag(var) ((var)->tag) 82 | #define length(var) ((var)->len) 83 | #define value(var) ((void *)&((var)->value)) 84 | #define integer(var) ((var)->value.i) 85 | #define opaque(var) ((var)->value.o) 86 | #define octstr(var) ((var)->value.s) 87 | #define count(var) ((var)->value.c) 88 | #define count32(var) ((var)->value.c32) 89 | #define count64(var) ((var)->value.c64) 90 | #define oid(var) ((var)->value.id) 91 | #define ipaddr(var) ((var)->value.ip) 92 | #define gauge(var) ((var)->value.g) 93 | #define timeticks(var) ((var)->value.t) 94 | 95 | #endif /* _ANS1_H_ */ 96 | -------------------------------------------------------------------------------- /core/agentx_encoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "asn1.h" 26 | 27 | /* Input: value pointer, number of elements, value type 28 | * Output: none 29 | * Return: byte length. 30 | */ 31 | uint32_t 32 | agentx_value_enc_try(uint32_t len, uint8_t type) 33 | { 34 | uint32_t ret; 35 | 36 | switch (type) { 37 | case ASN1_TAG_INT: 38 | case ASN1_TAG_CNT: 39 | case ASN1_TAG_GAU: 40 | case ASN1_TAG_TIMETICKS: 41 | ret = sizeof(uint32_t); 42 | break; 43 | case ASN1_TAG_CNT64: 44 | ret = sizeof(uint64_t); 45 | break; 46 | case ASN1_TAG_OBJID: 47 | ret = len * sizeof(uint32_t); 48 | break; 49 | case ASN1_TAG_OCTSTR: 50 | case ASN1_TAG_IPADDR: 51 | ret = len; 52 | break; 53 | default: 54 | ret = 0; 55 | break; 56 | } 57 | 58 | return ret; 59 | } 60 | 61 | /* Input: value pointer, number of elements, value type 62 | * Output: buffer 63 | * Return: byte length. 64 | */ 65 | uint32_t 66 | agentx_value_enc(const void *value, uint32_t len, uint8_t type, uint8_t *buf) 67 | { 68 | uint32_t i, ret; 69 | uint8_t *str; 70 | oid_t *oid_src, *oid_dest; 71 | uint32_t *inter_src, *inter_dest; 72 | uint64_t *long_src, *long_dest; 73 | 74 | switch (type) { 75 | case ASN1_TAG_INT: 76 | case ASN1_TAG_CNT: 77 | case ASN1_TAG_GAU: 78 | case ASN1_TAG_TIMETICKS: 79 | inter_src = (uint32_t *)value; 80 | inter_dest = (uint32_t *)buf; 81 | *inter_dest = *inter_src; 82 | ret = sizeof(uint32_t); 83 | break; 84 | case ASN1_TAG_CNT64: 85 | long_src = (uint64_t *)value; 86 | long_dest = (uint64_t *)buf; 87 | *long_dest = *long_src; 88 | ret = sizeof(uint64_t); 89 | break; 90 | case ASN1_TAG_OBJID: 91 | oid_src = (oid_t *)value; 92 | oid_dest = (oid_t *)buf; 93 | for (i = 0; i < len; i++) { 94 | oid_dest[i] = oid_src[i]; 95 | } 96 | ret = len * sizeof(oid_t); 97 | break; 98 | case ASN1_TAG_OCTSTR: 99 | case ASN1_TAG_IPADDR: 100 | str = (uint8_t *)value; 101 | memcpy(buf, str, len); 102 | ret = len; 103 | break; 104 | default: 105 | ret = 0; 106 | break; 107 | } 108 | 109 | return ret; 110 | } 111 | -------------------------------------------------------------------------------- /mibs/two_cascaded_index_table.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | 22 | local TwoIndexTableIdxA = 1 23 | local TwoIndexTableIdxB = 2 24 | local TwoIndexTableDescr = 3 25 | 26 | -- cascade multi-index 27 | local two_dim_entry_cache = { 28 | cascade = true, 29 | { 1, 2 }, 30 | { 2, 3 }, 31 | } 32 | 33 | local two_dim_cache = { 34 | [1] = { 35 | [2] = { desc = "A12" }, 36 | [3] = { desc = "B13" }, 37 | }, 38 | [2] = { 39 | [2] = { desc = "C21" }, 40 | [3] = { desc = "D22" }, 41 | }, 42 | } 43 | 44 | local TwoIndexTableGroup = { 45 | [1] = { 46 | [1] = { 47 | indexes = two_dim_entry_cache, 48 | [TwoIndexTableIdxA] = mib.ConstInt(function (sub_oid) 49 | for i, card_index in ipairs(two_dim_entry_cache[1]) do 50 | if sub_oid[1] == card_index then 51 | return card_index 52 | end 53 | end 54 | return nil 55 | end), 56 | [TwoIndexTableIdxB] = mib.ConstInt(function (sub_oid) 57 | for i, mode_index in ipairs(two_dim_entry_cache[2]) do 58 | if sub_oid[2] == mode_index then 59 | return mode_index 60 | end 61 | end 62 | return nil 63 | end), 64 | [TwoIndexTableDescr] = mib.ConstOctString(function (sub_oid) 65 | local i = sub_oid[1] 66 | local j = sub_oid[2] 67 | if two_dim_cache[i][j] then 68 | return two_dim_cache[i][j].desc 69 | end 70 | end), 71 | } 72 | }, 73 | } 74 | 75 | return TwoIndexTableGroup 76 | -------------------------------------------------------------------------------- /tests/testcase.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SNMPv2 cases 4 | 5 | snmpget -v2c -cpublic localhost .0 6 | snmpget -v2c -cpublic localhost .1.3 7 | snmpget -v2c -cpublic localhost .1.4 8 | snmpget -v2c -cpublic localhost .1.5.6.7.8.100 9 | 10 | snmpget -v2c -cpublic localhost .1.3.6.1.2.1.1.9.1.2.1 11 | snmpget -v2c -cpublic localhost .1.3.6.1.2.1.1.1.0 .1.3.6.1.2.1.1.2.0 12 | snmpget -v2c -cpublic localhost .1.3.6.1.2.1.1.9.1.1 13 | snmpget -v2c -cpublic localhost .1.3.6.1.2.1.1.9.1.2 14 | snmpget -v2c -cpublic localhost .1.3.6.1.2.1.1.9.1.5 15 | snmpget -v2c -cpublic localhost .1.3.6.1.2.1.1.0 16 | 17 | snmpgetnext -v2c -cpublic localhost .0 18 | snmpgetnext -v2c -cpublic localhost .1.3 19 | snmpgetnext -v2c -cpublic localhost .1.4 20 | snmpgetnext -v2c -cpublic localhost .1.5.6.7.8.100 21 | 22 | snmpbulkget -v2c -cpublic localhost .1.3.6.1.2.1.1 23 | 24 | # Error test (no access) 25 | snmpset -v2c -cpublic localhost .1.3.6.1.2.1.1.9.1.1 i 1 26 | # Error test (no access) 27 | snmpset -v2c -cpublic localhost .1.3.6.1.2.1.4.1.0 s "This agent is really smart!" 28 | # Error test (no access) 29 | snmpset -v2c -cpublic localhost .1.3.6.1.2.1.4.1.0 i 8888 30 | 31 | # Error test (no access) 32 | snmpset -v2c -cprivate localhost .1.3.6.1.2.1.1.9.1.1 i 1 33 | # Error test (wrong type) 34 | snmpset -v2c -cprivate localhost .1.3.6.1.2.1.4.1.0 s "This agent is really smart!" 35 | # OK 36 | snmpset -v2c -cprivate localhost .1.3.6.1.2.1.4.1.0 i 8888 37 | 38 | snmpwalk -v2c -cpublic localhost .1.3.6.1.2.1.1 39 | snmpwalk -v2c -cpublic localhost .1.3.6.1 40 | 41 | # SNMPv3 cases 42 | 43 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .0 44 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.3 45 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.4 46 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.5.6.7.8.100 47 | 48 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.9.1.2.1 49 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.1.0 .1.3.6.1.2.1.1.2.0 50 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.9.1.1 51 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.9.1.2 52 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.9.1.5 53 | snmpget -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.0 54 | 55 | snmpgetnext -u roNoAuthUser -l noAuthNoPriv localhost .0 56 | snmpgetnext -u roNoAuthUser -l noAuthNoPriv localhost .1.3 57 | snmpgetnext -u roNoAuthUser -l noAuthNoPriv localhost .1.4 58 | snmpgetnext -u roNoAuthUser -l noAuthNoPriv localhost .1.5.6.7.8.100 59 | 60 | snmpbulkget -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1 61 | 62 | # Error test (no access) 63 | snmpset -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.9.1.1 i 1 64 | # Error test (no access) 65 | snmpset -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.4.1.0 s "This agent is really smart!" 66 | # Error test (no access) 67 | snmpset -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.4.1.0 i 8888 68 | 69 | # Error test (no access) 70 | snmpset -u rwNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1.9.1.1 i 1 71 | # Error test (wrong type) 72 | snmpset -u rwNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.4.1.0 s "This agent is really smart!" 73 | # OK 74 | snmpset -u rwNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.4.1.0 i 8888 75 | 76 | snmpwalk -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1.2.1.1 77 | snmpwalk -u roNoAuthUser -l noAuthNoPriv localhost .1.3.6.1 78 | -------------------------------------------------------------------------------- /core/agentx_tcp_evloop_transport.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "agentx.h" 31 | #include "transport.h" 32 | #include "protocol.h" 33 | #include "ev_loop.h" 34 | #include "util.h" 35 | 36 | struct agentx_data_entry { 37 | int sock; 38 | uint8_t *buf; 39 | int len; 40 | }; 41 | 42 | static struct agentx_data_entry agentx_entry; 43 | 44 | static void 45 | agentx_write_handler(int sock, unsigned char flag, void *ud) 46 | { 47 | struct agentx_data_entry *entry = ud; 48 | 49 | if (send(sock, entry->buf, entry->len, 0) == -1) { 50 | perror("send()"); 51 | snmp_event_done(); 52 | } 53 | 54 | free(entry->buf); 55 | 56 | snmp_event_remove(sock, flag); 57 | } 58 | 59 | static void 60 | agentx_read_handler(int sock, unsigned char flag, void *ud) 61 | { 62 | uint8_t *buf; 63 | int len; 64 | 65 | buf = xmalloc(TRANS_BUF_SIZ); 66 | 67 | /* Receive agentx PDU */ 68 | len = recv(sock, buf, TRANS_BUF_SIZ - 1, 0); 69 | if (len == -1) { 70 | perror("recv()"); 71 | snmp_event_done(); 72 | } 73 | 74 | /* Parse agentX PDU in decoder */ 75 | agentx_prot_ops.receive(buf, len); 76 | } 77 | 78 | /* Send angentX PDU to the remote */ 79 | static void 80 | transport_send(uint8_t *buf, int len) 81 | { 82 | agentx_entry.buf = buf; 83 | agentx_entry.len = len; 84 | snmp_event_add(agentx_entry.sock, SNMP_EV_WRITE, agentx_write_handler, &agentx_entry); 85 | } 86 | 87 | static void 88 | transport_running(void) 89 | { 90 | snmp_event_init(); 91 | snmp_event_add(agentx_entry.sock, SNMP_EV_READ, agentx_read_handler, NULL); 92 | snmp_event_run(); 93 | } 94 | 95 | static void 96 | transport_stop(void) 97 | { 98 | snmp_event_done(); 99 | close(agentx_entry.sock); 100 | } 101 | 102 | static int 103 | transport_init(int port) 104 | { 105 | struct sockaddr_in sin; 106 | 107 | agentx_datagram.sock = agentx_entry.sock = socket(AF_INET, SOCK_STREAM, 0); 108 | if (agentx_entry.sock < 0) { 109 | perror("usock"); 110 | return -1; 111 | } 112 | 113 | memset(&sin, 0, sizeof(sin)); 114 | sin.sin_family = AF_INET; 115 | sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 116 | sin.sin_port = htons(port); 117 | 118 | if (connect(agentx_entry.sock, (struct sockaddr *)&sin, sizeof(sin)) == -1) { 119 | perror("connect()"); 120 | close(agentx_datagram.sock); 121 | return -1; 122 | } 123 | 124 | return 0; 125 | } 126 | 127 | struct transport_operation agentx_trans_ops = { 128 | "agentx_tcp", 129 | transport_init, 130 | transport_running, 131 | transport_stop, 132 | transport_send, 133 | }; 134 | -------------------------------------------------------------------------------- /core/snmp_udp_uloop_transport.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "transport.h" 32 | #include "protocol.h" 33 | #include "libubox/uloop.h" 34 | #include "util.h" 35 | 36 | static struct uloop_fd server; 37 | static struct sockaddr_in *client_sin; 38 | 39 | struct send_data_entry { 40 | int len; 41 | uint8_t *buf; 42 | struct sockaddr_in *client_sin; 43 | }; 44 | 45 | static void 46 | server_cb(struct uloop_fd *fd, unsigned int events) 47 | { 48 | socklen_t server_sz = sizeof(struct sockaddr_in); 49 | int len; 50 | uint8_t * buf; 51 | 52 | buf = xmalloc(TRANS_BUF_SIZ); 53 | client_sin = xmalloc(server_sz); 54 | 55 | /* Receive UDP data, store the address of the sender in client_sin */ 56 | len = recvfrom(server.fd, buf, TRANS_BUF_SIZ, 0, (struct sockaddr *)client_sin, &server_sz); 57 | if (len == -1) { 58 | perror("recvfrom()"); 59 | uloop_done(); 60 | } 61 | 62 | /* Parse SNMP PDU in decoder */ 63 | snmp_prot_ops.receive(buf, len); 64 | } 65 | 66 | /* Send snmp datagram as a UDP packet to the remote */ 67 | static void 68 | transport_send(uint8_t *buf, int len) 69 | { 70 | struct send_data_entry *entry; 71 | 72 | entry = xmalloc(sizeof(struct send_data_entry)); 73 | entry->buf = buf; 74 | entry->len = len; 75 | entry->client_sin = client_sin; 76 | 77 | /* Send the data back to the client */ 78 | if (sendto(server.fd, entry->buf, entry->len, 0, (struct sockaddr *)entry->client_sin, sizeof(struct sockaddr_in)) == -1) { 79 | perror("sendto()"); 80 | uloop_done(); 81 | } 82 | 83 | free(entry->buf); 84 | free(entry->client_sin); 85 | free(entry); 86 | } 87 | 88 | static void 89 | transport_running(void) 90 | { 91 | uloop_init(); 92 | uloop_fd_add(&server, ULOOP_READ); 93 | uloop_run(); 94 | } 95 | 96 | static void 97 | transport_stop(void) 98 | { 99 | uloop_end(); 100 | } 101 | 102 | static void 103 | transport_init(int port) 104 | { 105 | struct sockaddr_in sin; 106 | 107 | server.cb = server_cb; 108 | server.fd = socket(AF_INET, SOCK_DGRAM, 0); 109 | if (server.fd < 0) { 110 | perror("usock"); 111 | return -1; 112 | } 113 | 114 | memset(&sin, 0, sizeof(sin)); 115 | sin.sin_family = AF_INET; 116 | sin.sin_addr.s_addr = INADDR_ANY; 117 | sin.sin_port = htons(port); 118 | 119 | if (bind(server.fd, (struct sockaddr *)&sin, sizeof(sin))) { 120 | perror("bind()"); 121 | return -1; 122 | } 123 | 124 | return 0; 125 | } 126 | 127 | struct transport_operation snmp_trans_ops = { 128 | "snmp_uloop", 129 | transport_init, 130 | transport_running, 131 | transport_stop, 132 | transport_send, 133 | }; 134 | -------------------------------------------------------------------------------- /core/snmp_udp_evloop_transport.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "transport.h" 31 | #include "protocol.h" 32 | #include "ev_loop.h" 33 | #include "util.h" 34 | 35 | struct snmp_data_entry { 36 | int sock; 37 | uint8_t *buf; 38 | int len; 39 | struct sockaddr_in *client_sin; 40 | }; 41 | 42 | static struct snmp_data_entry snmp_entry; 43 | 44 | static void 45 | snmp_write_handler(int sock, unsigned char flag, void *ud) 46 | { 47 | struct snmp_data_entry *entry = ud; 48 | 49 | if (sendto(sock, entry->buf, entry->len, 0, (struct sockaddr *)entry->client_sin, sizeof(struct sockaddr_in)) == -1) { 50 | perror("sendto()"); 51 | snmp_event_done(); 52 | } 53 | 54 | free(entry->buf); 55 | free(entry->client_sin); 56 | 57 | snmp_event_remove(sock, flag); 58 | } 59 | 60 | static void 61 | snmp_read_handler(int sock, unsigned char flag, void *ud) 62 | { 63 | socklen_t server_sz = sizeof(struct sockaddr_in); 64 | int len; 65 | uint8_t *buf; 66 | 67 | buf = xmalloc(TRANS_BUF_SIZ); 68 | snmp_entry.client_sin = xmalloc(server_sz); 69 | 70 | /* Receive UDP data, store the address of the sender in client_sin */ 71 | len = recvfrom(sock, buf, TRANS_BUF_SIZ, 0, (struct sockaddr *)snmp_entry.client_sin, &server_sz); 72 | if (len == -1) { 73 | perror("recvfrom()"); 74 | snmp_event_done(); 75 | } 76 | 77 | /* Parse SNMP PDU in decoder */ 78 | snmp_prot_ops.receive(buf, len); 79 | } 80 | 81 | /* Send snmp datagram as a UDP packet to the remote */ 82 | static void 83 | transport_send(uint8_t *buf, int len) 84 | { 85 | snmp_entry.buf = buf; 86 | snmp_entry.len = len; 87 | snmp_event_add(snmp_entry.sock, SNMP_EV_WRITE, snmp_write_handler, &snmp_entry); 88 | } 89 | 90 | static void 91 | transport_running(void) 92 | { 93 | snmp_event_init(); 94 | snmp_event_add(snmp_entry.sock, SNMP_EV_READ, snmp_read_handler, NULL); 95 | snmp_event_run(); 96 | } 97 | 98 | static void 99 | transport_stop(void) 100 | { 101 | snmp_event_done(); 102 | } 103 | 104 | static int 105 | transport_init(int port) 106 | { 107 | struct sockaddr_in sin; 108 | 109 | snmp_entry.sock = socket(AF_INET, SOCK_DGRAM, 0); 110 | if (snmp_entry.sock < 0) { 111 | perror("usock"); 112 | return -1; 113 | } 114 | 115 | memset(&sin, 0, sizeof(sin)); 116 | sin.sin_family = AF_INET; 117 | sin.sin_addr.s_addr = INADDR_ANY; 118 | sin.sin_port = htons(port); 119 | 120 | if (bind(snmp_entry.sock, (struct sockaddr *)&sin, sizeof(sin))) { 121 | perror("bind()"); 122 | close(snmp_entry.sock); 123 | return -1; 124 | } 125 | 126 | return 0; 127 | } 128 | 129 | struct transport_operation snmp_trans_ops = { 130 | "snmp_udp", 131 | transport_init, 132 | transport_running, 133 | transport_stop, 134 | transport_send, 135 | }; 136 | -------------------------------------------------------------------------------- /core/agentx_decoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "asn1.h" 26 | #include "util.h" 27 | 28 | /* Input: buffer, byte length, value type; 29 | * Output: none 30 | * Return: value bytes 31 | */ 32 | uint32_t 33 | agentx_value_dec_try(const uint8_t *buf, uint8_t flag, uint8_t type) 34 | { 35 | uint32_t ret; 36 | 37 | switch (type) { 38 | case ASN1_TAG_INT: 39 | case ASN1_TAG_CNT: 40 | case ASN1_TAG_GAU: 41 | case ASN1_TAG_TIMETICKS: 42 | ret = sizeof(uint32_t); 43 | case ASN1_TAG_CNT64: 44 | ret = sizeof(uint64_t); 45 | break; 46 | case ASN1_TAG_OCTSTR: 47 | case ASN1_TAG_IPADDR: 48 | ret = *(uint32_t *)buf; 49 | break; 50 | case ASN1_TAG_OBJID: 51 | ret = *buf; 52 | uint8_t prefix = *(buf + 1); 53 | if (ret == 0 && prefix == 0) { 54 | return 0; 55 | } 56 | ret = (ret + 5) * sizeof(uint32_t); 57 | break; 58 | default: 59 | ret = 0; 60 | break; 61 | } 62 | 63 | return ret; 64 | } 65 | 66 | /* Input: buffer, flag, value type; 67 | * Output: value pointer 68 | * Return: number of elements 69 | */ 70 | uint32_t 71 | agentx_value_dec(uint8_t **buffer, uint8_t flag, uint8_t type, void *value) 72 | { 73 | uint8_t *buf; 74 | uint32_t i, ret; 75 | uint32_t *oid_src, *oid_dest; 76 | uint32_t *inter_src, *inter_dest; 77 | uint64_t *long_src, *long_dest; 78 | 79 | buf = *buffer; 80 | 81 | switch (type) { 82 | case ASN1_TAG_INT: 83 | case ASN1_TAG_CNT: 84 | case ASN1_TAG_GAU: 85 | case ASN1_TAG_TIMETICKS: 86 | inter_src = (uint32_t *)buf; 87 | inter_dest = (uint32_t *)value; 88 | *inter_dest = *inter_src; 89 | buf += sizeof(uint32_t); 90 | ret = 1; 91 | break; 92 | case ASN1_TAG_CNT64: 93 | long_src = (uint64_t *)buf; 94 | long_dest = (uint64_t *)value; 95 | *long_dest = *long_src; 96 | buf += sizeof(uint64_t); 97 | ret = 1; 98 | break; 99 | case ASN1_TAG_OCTSTR: 100 | case ASN1_TAG_IPADDR: 101 | ret = *(uint32_t *)buf; 102 | buf += 4; 103 | memcpy(value, buf, ret); 104 | buf += uint_sizeof(ret); 105 | break; 106 | case ASN1_TAG_OBJID: 107 | ret = *buf++; 108 | uint8_t prefix = *buf++; 109 | buf += 2; 110 | if (ret == 0 && prefix == 0) { 111 | break; 112 | } 113 | ret += 5; 114 | oid_src = (uint32_t *)buf; 115 | oid_dest = (uint32_t *)value; 116 | oid_dest[0] = 1; 117 | oid_dest[1] = 3; 118 | oid_dest[2] = 6; 119 | oid_dest[3] = 1; 120 | oid_dest[4] = prefix; 121 | for (i = 5; i < ret; i++) { 122 | oid_dest[i] = oid_src[i - 5]; 123 | buf += sizeof(uint32_t); 124 | } 125 | break; 126 | default: 127 | ret = 0; 128 | break; 129 | } 130 | 131 | *buffer = buf; 132 | return ret; 133 | } 134 | -------------------------------------------------------------------------------- /mibs/icmp.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | 22 | local icmp_scalar_cache = {} 23 | 24 | local function __load_config() 25 | icmp_scalar_cache = {} 26 | for line in io.lines("/proc/net/snmp") do 27 | if string.match(line, "%w+") == 'Icmp' then 28 | for w in string.gmatch(line, "%d+") do 29 | table.insert(icmp_scalar_cache, tonumber(w)) 30 | end 31 | end 32 | end 33 | end 34 | 35 | local last_load_time = os.time() 36 | 37 | local function need_to_reload() 38 | if os.difftime(os.time(), last_load_time) < 3 then 39 | return false 40 | else 41 | last_load_time = os.time() 42 | return true 43 | end 44 | end 45 | 46 | local function load_config() 47 | if need_to_reload() == true then 48 | __load_config() 49 | end 50 | end 51 | 52 | __load_config() 53 | 54 | mib.module_methods.or_table_reg("1.3.6.1.2.1.5", "The MIB module for managing icmp and ICMP inplementations") 55 | 56 | local icmpGroup = { 57 | [1] = mib.ConstCount(function () load_config() return icmp_scalar_cache[1] end), 58 | [2] = mib.ConstCount(function () load_config() return icmp_scalar_cache[2] end), 59 | [3] = mib.ConstCount(function () load_config() return icmp_scalar_cache[4] end), 60 | [4] = mib.ConstCount(function () load_config() return icmp_scalar_cache[5] end), 61 | [5] = mib.ConstCount(function () load_config() return icmp_scalar_cache[6] end), 62 | [6] = mib.ConstCount(function () load_config() return icmp_scalar_cache[7] end), 63 | [7] = mib.ConstCount(function () load_config() return icmp_scalar_cache[8] end), 64 | [8] = mib.ConstCount(function () load_config() return icmp_scalar_cache[9] end), 65 | [9] = mib.ConstCount(function () load_config() return icmp_scalar_cache[10] end), 66 | [10] = mib.ConstCount(function () load_config() return icmp_scalar_cache[11] end), 67 | [11] = mib.ConstCount(function () load_config() return icmp_scalar_cache[12] end), 68 | [12] = mib.ConstCount(function () load_config() return icmp_scalar_cache[13] end), 69 | [13] = mib.ConstCount(function () load_config() return icmp_scalar_cache[14] end), 70 | [14] = mib.ConstCount(function () load_config() return icmp_scalar_cache[15] end), 71 | [15] = mib.ConstCount(function () load_config() return icmp_scalar_cache[16] end), 72 | [16] = mib.ConstCount(function () load_config() return icmp_scalar_cache[17] end), 73 | [17] = mib.ConstCount(function () load_config() return icmp_scalar_cache[18] end), 74 | [18] = mib.ConstCount(function () load_config() return icmp_scalar_cache[19] end), 75 | [19] = mib.ConstCount(function () load_config() return icmp_scalar_cache[20] end), 76 | [20] = mib.ConstCount(function () load_config() return icmp_scalar_cache[21] end), 77 | [21] = mib.ConstCount(function () load_config() return icmp_scalar_cache[22] end), 78 | [22] = mib.ConstCount(function () load_config() return icmp_scalar_cache[23] end), 79 | [23] = mib.ConstCount(function () load_config() return icmp_scalar_cache[24] end), 80 | [24] = mib.ConstCount(function () load_config() return icmp_scalar_cache[25] end), 81 | [25] = mib.ConstCount(function () load_config() return icmp_scalar_cache[26] end), 82 | [26] = mib.ConstCount(function () load_config() return icmp_scalar_cache[27] end), 83 | } 84 | 85 | return icmpGroup 86 | -------------------------------------------------------------------------------- /mibs/system.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | 22 | -- scalar index 23 | local sysDesc = 1 24 | local sysObjectID = 2 25 | local sysUpTime = 3 26 | local sysContact = 4 27 | local sysName = 5 28 | local sysLocation = 6 29 | local sysServices = 7 30 | local sysORLastChange = 8 31 | 32 | -- table index 33 | local sysORTable = 9 34 | -- entry index 35 | local sysOREntry = 1 36 | -- list index 37 | local sysORIndex = 1 38 | local sysORID = 2 39 | local sysORDesc = 3 40 | local sysORUpTime = 4 41 | 42 | local or_oid_cache = {} 43 | local or_entry_cache = {} 44 | 45 | local function or_entry_get(i, name) 46 | assert(type(name) == 'string') 47 | local value 48 | if or_entry_cache[i] then 49 | if name == 'uptime' then 50 | value = os.difftime(os.time(), or_entry_cache[i][name]) * 100 51 | else 52 | value = or_entry_cache[i][name] 53 | end 54 | end 55 | return value 56 | end 57 | 58 | local startup_time = 0 59 | local or_last_changed_time = 0 60 | 61 | local function mib_system_startup(time) 62 | startup_time = time 63 | or_last_changed_time = time 64 | end 65 | 66 | mib_system_startup(os.time()) 67 | 68 | local or_table_reg = function (oid, desc) 69 | local entry = {} 70 | entry['oid'] = {} 71 | for i in string.gmatch(oid, "%d") do 72 | table.insert(entry['oid'], tonumber(i)) 73 | end 74 | entry['desc'] = desc 75 | entry['uptime'] = os.time() 76 | table.insert(or_entry_cache, entry) 77 | 78 | or_last_changed_time = os.time() 79 | 80 | or_oid_cache[oid] = #or_entry_cache 81 | end 82 | 83 | local or_table_unreg = function (oid) 84 | local or_idx = or_oid_cache[oid] 85 | 86 | if or_entry_cache[or_idx] ~= nil then 87 | table.remove(or_entry_cache, or_idx) 88 | or_last_changed_time = os.time() 89 | end 90 | 91 | or_oid_cache[oid] = nil 92 | end 93 | 94 | local sysMethods = { 95 | ["or_table_reg"] = or_table_reg, 96 | ["or_table_unreg"] = or_table_unreg 97 | } 98 | 99 | mib.module_method_register(sysMethods) 100 | 101 | local sysGroup = { 102 | [sysDesc] = mib.ConstOctString(function () return mib.sh_call("uname -a", "*line") end), 103 | [sysObjectID] = mib.ConstOid(function () return { 1, 3, 6, 1, 2, 1, 1 } end), 104 | [sysUpTime] = mib.ConstTimeticks(function () return os.difftime(os.time(), startup_time) * 100 end), 105 | [sysContact] = mib.ConstOctString(function () return "Me " end), 106 | [sysName] = mib.ConstOctString(function () return mib.sh_call("uname -n", "*line") end), 107 | [sysLocation] = mib.ConstOctString(function () return "Shanghai" end), 108 | [sysServices] = mib.ConstInt(function () return 72 end), 109 | [sysORLastChange] = mib.ConstTimeticks(function () return os.difftime(os.time(), or_last_changed_time) * 100 end), 110 | [sysORTable] = { 111 | [sysOREntry] = { 112 | indexes = or_entry_cache, 113 | [sysORIndex] = mib.ConstInt(function () return nil, mib.SNMP_ERR_STAT_UNACCESS end), 114 | [sysORID] = mib.ConstOid(function (i) return or_entry_get(i, 'oid') end), 115 | [sysORDesc] = mib.ConstOctString(function (i) return or_entry_get(i, 'desc') end), 116 | [sysORUpTime] = mib.ConstTimeticks(function (i) return or_entry_get(i, 'uptime') end), 117 | } 118 | } 119 | } 120 | 121 | return sysGroup 122 | -------------------------------------------------------------------------------- /core/ev_loop.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include "ev_loop.h" 23 | 24 | #define SNMP_MAX_EVENTS 5 25 | 26 | struct snmp_event { 27 | int fd; 28 | transport_handler rcb; 29 | transport_handler wcb; 30 | void *rud; 31 | void *wud; 32 | unsigned char flag; 33 | unsigned char read; 34 | unsigned char write; 35 | }; 36 | 37 | struct snmp_event_loop { 38 | int start; 39 | int ev_no; 40 | int max_fd; 41 | struct snmp_event event[SNMP_MAX_EVENTS]; 42 | }; 43 | 44 | static struct snmp_event_loop ev_loop; 45 | 46 | #ifdef USE_EPOLL 47 | #include "ev_epoll.h" 48 | #else 49 | #ifdef USE_KQUEUE 50 | #include "ev_kqueue.h" 51 | #else 52 | #include "ev_select.h" 53 | #endif 54 | #endif 55 | 56 | void 57 | snmp_event_init(void) 58 | { 59 | int i; 60 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 61 | struct snmp_event *event = &ev_loop.event[i]; 62 | event->fd = -1; 63 | event->flag = SNMP_EV_NONE; 64 | event->read = 0; 65 | event->write = 0; 66 | } 67 | ev_loop.start = 1; 68 | ev_loop.ev_no = 0; 69 | ev_loop.max_fd = -1; 70 | __ev_init(); 71 | } 72 | 73 | void 74 | snmp_event_done(void) 75 | { 76 | int i; 77 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 78 | struct snmp_event *event = &ev_loop.event[i]; 79 | event->fd = -1; 80 | event->flag = SNMP_EV_NONE; 81 | event->read = 0; 82 | event->write = 0; 83 | } 84 | ev_loop.start = 0; 85 | ev_loop.ev_no = 0; 86 | ev_loop.max_fd = -1; 87 | __ev_done(); 88 | } 89 | 90 | int 91 | snmp_event_add(int fd, unsigned char flag, transport_handler cb, void *ud) 92 | { 93 | int i; 94 | 95 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 96 | struct snmp_event *event = &ev_loop.event[i]; 97 | if (event->fd == fd) { 98 | __ev_add(event, flag); 99 | event->flag |= flag; 100 | } else if (event->fd == -1) { 101 | if (fd > ev_loop.max_fd) { 102 | ev_loop.max_fd = fd; 103 | } 104 | event->fd = fd; 105 | __ev_add(event, flag); 106 | event->flag = flag; 107 | } else { 108 | continue; 109 | } 110 | 111 | if (flag & SNMP_EV_READ) { 112 | event->rcb = cb; 113 | event->rud = ud; 114 | } 115 | if (flag & SNMP_EV_WRITE) { 116 | event->wcb = cb; 117 | event->wud = ud; 118 | } 119 | return 0; 120 | } 121 | 122 | return -1; 123 | } 124 | 125 | void 126 | snmp_event_remove(int fd, unsigned char flag) 127 | { 128 | int i; 129 | 130 | if (fd == ev_loop.max_fd) { 131 | ev_loop.max_fd = -1; 132 | } 133 | 134 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 135 | struct snmp_event *event = &ev_loop.event[i]; 136 | if (event->fd == fd) { 137 | __ev_remove(event, flag); 138 | event->flag &= ~flag; 139 | if (flag & SNMP_EV_READ) { 140 | event->read = 0; 141 | } 142 | if (flag & SNMP_EV_WRITE) { 143 | event->write = 0; 144 | } 145 | if (event->flag == SNMP_EV_NONE) { 146 | event->fd = -1; 147 | } 148 | } 149 | if (fd > ev_loop.max_fd) { 150 | ev_loop.max_fd = fd; 151 | } 152 | } 153 | } 154 | 155 | static void 156 | snmp_event_poll(void) 157 | { 158 | int i; 159 | 160 | __ev_poll(&ev_loop); 161 | for (i = 0; i < SNMP_MAX_EVENTS; i++) { 162 | struct snmp_event *event = &ev_loop.event[i]; 163 | if (event->read && event->rcb != NULL) { 164 | event->rcb(event->fd, SNMP_EV_READ, event->rud); 165 | event->read = 0; 166 | } 167 | if (event->write && event->wcb != NULL) { 168 | event->wcb(event->fd, SNMP_EV_WRITE, event->wud); 169 | event->write = 0; 170 | } 171 | } 172 | } 173 | 174 | void 175 | snmp_event_run(void) 176 | { 177 | while (ev_loop.start) { 178 | snmp_event_poll(); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SmartSNMP - A Smart SNMP Agent 2 | ============================== 3 | 4 | [![Build Status](https://travis-ci.org/credosemi/smartsnmp.svg?branch=master)](https://travis-ci.org/credosemi/smartsnmp) [![Coverage Status](https://coveralls.io/repos/credosemi/smartsnmp/badge.svg?branch=master)](https://coveralls.io/r/credosemi/smartsnmp?branch=master) 5 | 6 | **SmartSNMP** is a minimal easy-config agent for network management supporting 7 | SNMPv1/v2c/v3(non-encryption) and AgentX. It is written in C99 and Lua5.1. It 8 | can run both on PC platforms like Linux and embedded systems such as OpenWRT. 9 | 10 | License 11 | ------- 12 | 13 | **SmartSNMP** is licensed under GPLv2 with "PRIVATE MIB" EXCEPTION, see `LICENSE` file for more details. 14 | 15 | Configuration and Interfaces 16 | ---------------------------- 17 | 18 | One of the biggest bonuses (aka smartest features) of this agent is that you can 19 | write your own private mibs and load it only if you learn to write lua files as 20 | shown in `config` and `mibs` directory. 21 | 22 | Operation 23 | --------- 24 | 25 | **SmartSNMP** can run in two modes: the **SNMP mode** and the **AgentX mode**. In SNMP 26 | mode the agent will run as an independent SNMP agent and process SNMP datagram 27 | from the client, while in AgentX mode the agent will run as an sub-agent against 28 | NET-SNMP as the master agent and process AgentX datagram from the master. 29 | 30 | Revelant test samples are shown respectively as `tests/snmpd_test.sh` and `tests/agentx_test.sh` 31 | 32 | Dependencies 33 | ------------ 34 | 35 | - Lua 5.1 36 | - One of transports 37 | - built-in 38 | - select 39 | - kqueue (not tested yet) 40 | - epoll 41 | - libevent 42 | - libubox/uloop (for OpenWrt) 43 | 44 | Build 45 | ----- 46 | 47 | Different event-driven mechanisms and transport options need respective libraries. 48 | For expample, in libevent transport you need to install libevent, in uloop 49 | transport you need to install libubox. Especially, on Ubuntu you should install 50 | liblua5.1 while liblua is needed on other platforms. 51 | 52 | Assume **SmartSNMP** is running on Ubuntu, you shall install libraries such as: 53 | 54 | # lua5.1 55 | sudo apt-get install -y lua5.1 liblua5.1-0-dev 56 | 57 | # scons & git 58 | sudo apt-get install -y scons git 59 | 60 | # clone with git 61 | git clone https://github.com/credosemi/smartsnmp.git 62 | 63 | # build 64 | cd smartsnmp 65 | scons 66 | 67 | For more build options, type: 68 | 69 | scons --help 70 | 71 | You will get: 72 | 73 | ... SCons Options ... 74 | Local Options: 75 | --transport=[built-in|libevent|uloop] 76 | transport you want to use 77 | --evloop=[select|kqueue|epoll] 78 | built-in event loop type 79 | --with-cflags=CFLAGS use CFLAGS as compile time arguments (will 80 | ignore CFLAGS env) 81 | --with-ldflags=LDFLAGS use LDFLAGS as link time arguments to ld (will 82 | ignore LDFLAGS env) 83 | --with-libs=LIBS use LIBS as link time arguments to ld 84 | --with-liblua=DIR use liblua in DIR 85 | --with-libubox=DIR use libubox in DIR (only for transport is uloop) 86 | --with-libevent=DIR use libevent in DIR (only for transport is 87 | libevent) 88 | --gcov=[yes|no] compile C source code with gcov support 89 | 90 | You can specify options above you need to build the project. 91 | 92 | _Installation scripts is coming soon._ 93 | 94 | Test script 95 | ----------- 96 | 97 | Any SNMP daemon installed in you system should be closed before test. 98 | 99 | sudo /etc/init.d/snmpd stop 100 | 101 | In **SNMP** mode, we would run the SmartSNMP daemon: 102 | 103 | cd smartsnmp 104 | sudo ./tests/snmp_daemon.sh 105 | 106 | Then run test cases at another terminal: 107 | 108 | cd smartsnmp 109 | ./tests/testcase.sh 110 | 111 | In **AgentX** mode, NET-SNMP will be tested as the master agent, so we will 112 | download **NET-SNMP-5.7.2.1** source and build out the image in `tests` directory: 113 | 114 | cd smartsnmp 115 | ./tests/netsnmp_build.sh 116 | 117 | Then run NET-SNMP as the master agent: 118 | 119 | sudo ./tests/net-snmp-release/sbin/snmpd -f -Lo -m "" -C -c tests/snmpd.conf 120 | 121 | Then run the SmartSNMP as a sub-agent at another terminal: 122 | 123 | cd smartsnmp 124 | sudo ./tests/agentx_daemon.sh 125 | 126 | Then run test cases at the third terminal: 127 | 128 | cd smartsnmp 129 | ./tests/testcase.sh 130 | 131 | TODO 132 | ---- 133 | 134 | See `TODO.md`. 135 | 136 | Authors 137 | ------- 138 | 139 | See `AUTHORS`. 140 | -------------------------------------------------------------------------------- /mibs/interfaces.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | local utils = require "smartsnmp.utils" 22 | 23 | local if_entry_cache = {} 24 | 25 | local entry = { 26 | desc = "lo", 27 | type = 24, 28 | mtu = 65535, 29 | speed = 10000000, 30 | phy_addr = utils.mac2oct('aa:0f:16:3e:79:42'), 31 | admin_stat = 1, 32 | open_stat = 1, 33 | in_octet = 2449205, 34 | out_octet = 2449198, 35 | spec = { 0, 0 } 36 | } 37 | table.insert(if_entry_cache, entry) 38 | 39 | entry = { 40 | desc = "eth0", 41 | type = 6, 42 | mtu = 1500, 43 | speed = 1000000, 44 | phy_addr = utils.mac2oct('00:1f:16:33:e7:21'), 45 | admin_stat = 1, 46 | open_stat = 1, 47 | in_octet = 672549159, 48 | out_octet = 672549138, 49 | spec = { 0, 0 } 50 | } 51 | table.insert(if_entry_cache, entry) 52 | 53 | entry = { 54 | desc = "eth1", 55 | type = 6, 56 | mtu = 1500, 57 | speed = 100000000, 58 | phy_addr = utils.mac2oct('8c:ae:4c:fe:17:9c'), 59 | admin_stat = 1, 60 | open_stat = 1, 61 | in_octet = 4914346, 62 | out_octet = 4914345, 63 | spec = { 0, 0 } 64 | } 65 | table.insert(if_entry_cache, entry) 66 | 67 | entry = { 68 | desc = "wlan0", 69 | type = 6, 70 | mtu = 1500, 71 | speed = 0, 72 | phy_addr = utils.mac2oct('00:26:c6:60:60:30'), 73 | admin_stat = 2, 74 | open_stat = 2, 75 | in_octet = 0, 76 | out_octet = 0, 77 | spec = { 0, 0 } 78 | } 79 | table.insert(if_entry_cache, entry) 80 | 81 | entry = { 82 | desc = "virbr0", 83 | type = 6, 84 | mtu = 1500, 85 | speed = 0, 86 | phy_addr = utils.mac2oct('16:0a:80:74:ee:77'), 87 | admin_stat = 1, 88 | open_stat = 2, 89 | in_octet = 0, 90 | out_octet = 0, 91 | spec = { 0, 0 } 92 | } 93 | table.insert(if_entry_cache, entry) 94 | 95 | local last_changed_time = os.time() 96 | 97 | local function if_entry_get(i, name) 98 | assert(type(name) == 'string') 99 | local value 100 | if if_entry_cache[i] then 101 | if name == "" then 102 | value = i 103 | else 104 | value = if_entry_cache[i][name] 105 | end 106 | end 107 | return value 108 | end 109 | 110 | local function if_entry_set(i, v, name) 111 | assert(type(name) == 'string') 112 | if if_entry_cache[i] then 113 | if_entry_cache[i][name] = v 114 | end 115 | end 116 | 117 | local ifGroup = { 118 | [1] = mib.ConstInt(function () return #if_entry_cache end), 119 | [2] = { 120 | [1] = { 121 | indexes = if_entry_cache, 122 | [1] = mib.ConstInt(function (i) return if_entry_get(i, '') end), 123 | [2] = mib.ConstOctString(function (i) return if_entry_get(i, 'desc') end), 124 | [3] = mib.ConstInt(function (i) return if_entry_get(i, 'type') end), 125 | [4] = mib.ConstInt(function (i) return if_entry_get(i, 'mtu') end), 126 | [5] = mib.ConstInt(function (i) return if_entry_get(i, 'speed') end), 127 | [6] = mib.ConstOctString(function (i) return if_entry_get(i, 'phy_addr') end), 128 | [7] = mib.Int(function (i) return if_entry_get(i, 'admin_stat') end, function (i, v) return if_entry_set(i, v, 'admin_stat') end), 129 | [8] = mib.ConstInt(function (i) return if_entry_get(i, 'open_stat') end), 130 | [9] = mib.ConstTimeticks(function (i) 131 | local time 132 | if if_entry_cache[i] then 133 | time = os.difftime(os.time(), last_changed_time) * 100 134 | end 135 | return time 136 | end), 137 | [10] = mib.ConstInt(function (i) return if_entry_get(i, 'in_octet') end), 138 | [16] = mib.ConstInt(function (i) return if_entry_get(i, 'out_octet') end), 139 | [22] = mib.ConstOid(function (i) return if_entry_get(i, 'spec') end), 140 | } 141 | } 142 | } 143 | 144 | return ifGroup 145 | -------------------------------------------------------------------------------- /core/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _UTIL_H_ 22 | #define _UTIL_H_ 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #define alloc_nr(x) (((x) + 2) * 3 / 2) 29 | #define uint_sizeof(n) ((n + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1)) 30 | #define elem_num(arr) (sizeof(arr) / sizeof(arr[0])) 31 | 32 | #define HTON16(x) NTOH16(x) 33 | #define HTON32(x) NTOH32(x) 34 | 35 | #define NTOH16(x) ((uint16_t)( \ 36 | (((uint16_t)(x) & 0x00ff) << 8) | \ 37 | (((uint16_t)(x) & 0xff00) >> 8))) 38 | 39 | #define NTOH32(x) ((uint32_t)( \ 40 | (((uint32_t)(x) & 0x000000ffU) << 24) | \ 41 | (((uint32_t)(x) & 0x0000ff00U) << 8) | \ 42 | (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ 43 | (((uint32_t)(x) & 0xff000000U) >> 24))) 44 | 45 | #define NTOH64(x) ((uint64_t)( \ 46 | (((uint64_t)(x) & 0x00000000000000ffU) << 56) | \ 47 | (((uint64_t)(x) & 0x000000000000ff00U) << 40) | \ 48 | (((uint64_t)(x) & 0x0000000000ff0000U) << 24) | \ 49 | (((uint64_t)(x) & 0x00000000ff000000U) << 8) | \ 50 | (((uint64_t)(x) & 0x000000ff00000000U) >> 8) | \ 51 | (((uint64_t)(x) & 0x0000ff0000000000U) >> 24) | \ 52 | (((uint64_t)(x) & 0x00ff000000000000U) >> 40) | \ 53 | (((uint64_t)(x) & 0xff00000000000000U) >> 56))) 54 | 55 | //#define LOGOFF 56 | 57 | #ifdef SYSLOG 58 | #ifdef LOGOFF 59 | #define SMARTSNMP_LOG(level, fmt...) 60 | #else 61 | #define SMARTSNMP_LOG(level, fmt...) \ 62 | do { \ 63 | switch (level) { \ 64 | case L_DEBUG: \ 65 | case L_INFO: \ 66 | case L_WARNING: error(fmt); break;\ 67 | case L_ERROR: die(fmt); break; \ 68 | default: break; \ 69 | } \ 70 | } while (0) 71 | #endif 72 | #else 73 | #ifdef LOGOFF 74 | #define SMARTSNMP_LOG(level, fmt...) 75 | #else 76 | #define SMARTSNMP_LOG(level, fmt...) \ 77 | do { \ 78 | switch (level) { \ 79 | case L_DEBUG: \ 80 | case L_INFO: \ 81 | case L_WARNING: fprintf(stdout, fmt); break; \ 82 | case L_ERROR: fprintf(stderr, fmt); break; \ 83 | default: break; \ 84 | } \ 85 | } while (0) 86 | #endif 87 | #endif 88 | 89 | /* vocal information */ 90 | typedef enum smartsnmp_log_level { 91 | L_ALL, 92 | L_DEBUG = 1, 93 | L_INFO, 94 | L_WARNING, 95 | L_ERROR, 96 | L_OFF, 97 | L_LEVEL_NUM 98 | } SMARTSNMP_LOG_LEVEL_E; 99 | 100 | struct err_msg_map { 101 | int error; 102 | const char *message; 103 | }; 104 | 105 | static inline void 106 | report(const char *prefix, const char *err, va_list params) 107 | { 108 | fputs(prefix, stderr); 109 | vfprintf(stderr, err, params); 110 | fputs("\n", stderr); 111 | } 112 | 113 | static inline void 114 | usage(const char *err) 115 | { 116 | fprintf(stderr, "usage: %s\n", err); 117 | exit(1); 118 | } 119 | 120 | static inline void 121 | die(const char *err, ...) 122 | { 123 | va_list params; 124 | 125 | va_start(params, err); 126 | report("fatal: ", err, params); 127 | va_end(params); 128 | exit(1); 129 | } 130 | 131 | static inline int 132 | error(const char *err, ...) 133 | { 134 | va_list params; 135 | 136 | va_start(params, err); 137 | report("error: ", err, params); 138 | va_end(params); 139 | return -1; 140 | } 141 | 142 | static inline void * 143 | xmalloc(size_t size) 144 | { 145 | void *ret = malloc(size); 146 | if (!ret) 147 | die("Out of memory, malloc failed"); 148 | return ret; 149 | } 150 | 151 | static inline void * 152 | xrealloc(void *ptr, size_t size) 153 | { 154 | void *ret = realloc(ptr, size); 155 | if (!ret) 156 | die("Out of memory, realloc failed"); 157 | return ret; 158 | } 159 | 160 | static inline void * 161 | xcalloc(int nr, size_t size) 162 | { 163 | void *ret = calloc(nr, size); 164 | if (!ret) 165 | die("Out of memory, calloc failed"); 166 | return ret; 167 | } 168 | 169 | static inline const char * 170 | error_message(struct err_msg_map *msg_blk, size_t size, int err) 171 | { 172 | int i; 173 | 174 | for (i = 0; i < size; i++) { 175 | if (msg_blk[i].error == err) { 176 | return msg_blk[i].message; 177 | } 178 | } 179 | 180 | return NULL; 181 | } 182 | 183 | #endif /* _UTIL_H_ */ 184 | -------------------------------------------------------------------------------- /core/snmp_udp_libevent_transport.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include "transport.h" 34 | #include "protocol.h" 35 | #include "util.h" 36 | 37 | static struct event_base *event_base; 38 | static struct event *snmp_recv_event; 39 | static struct event *snmp_send_event; 40 | static int sock; 41 | struct sockaddr_in *client_sin; 42 | 43 | struct send_data_entry { 44 | int len; 45 | uint8_t * buf; 46 | struct sockaddr_in * client_sin; 47 | TAILQ_ENTRY(send_data_entry) entries; 48 | }; 49 | 50 | TAILQ_HEAD(, send_data_entry) send_queue_head; 51 | 52 | static void 53 | snmp_write_cb(const int sock, short int which, void *arg) 54 | { 55 | struct send_data_entry * entry; 56 | 57 | if (!TAILQ_EMPTY(&send_queue_head)) { 58 | entry = TAILQ_FIRST(&send_queue_head); 59 | 60 | /* Send the data back to the client */ 61 | if (sendto(sock, entry->buf, entry->len, 0, (struct sockaddr *) entry->client_sin, sizeof(struct sockaddr_in)) == -1) { 62 | perror("sendto()"); 63 | event_loopbreak(); 64 | } 65 | TAILQ_REMOVE(&send_queue_head, entry, entries); 66 | 67 | free(entry->buf); 68 | free(entry->client_sin); 69 | free(entry); 70 | 71 | /* Free send event */ 72 | event_free(snmp_send_event); 73 | 74 | /* if there is other data to be sent, register another EV_WRITE event */ 75 | if (!TAILQ_EMPTY(&send_queue_head)) { 76 | snmp_send_event = event_new(event_base, sock, EV_WRITE, snmp_write_cb, NULL); 77 | event_add(snmp_send_event, NULL); 78 | } 79 | } 80 | } 81 | 82 | static void 83 | snmp_read_cb(const int sock, short int which, void *arg) 84 | { 85 | socklen_t server_sz = sizeof(struct sockaddr_in); 86 | int len; 87 | uint8_t * buf; 88 | 89 | buf = xmalloc(TRANS_BUF_SIZ); 90 | client_sin = xmalloc(server_sz); 91 | 92 | /* Receive UDP data, store the address of the sender in client_sin */ 93 | len = recvfrom(sock, buf, TRANS_BUF_SIZ, 0, (struct sockaddr *)client_sin, &server_sz); 94 | if (len == -1) { 95 | perror("recvfrom()"); 96 | event_loopbreak(); 97 | } 98 | 99 | /* Parse SNMP PDU in decoder */ 100 | snmp_prot_ops.receive(buf, len); 101 | } 102 | 103 | static void 104 | transport_send(uint8_t * buf, int len) 105 | { 106 | struct send_data_entry * entry; 107 | 108 | entry = xmalloc(sizeof(struct send_data_entry)); 109 | entry->buf = buf; 110 | entry->len = len; 111 | entry->client_sin = client_sin; 112 | 113 | /* Insert to tail */ 114 | TAILQ_INSERT_TAIL(&send_queue_head, entry, entries); 115 | 116 | /* Send event comes with UPD packet */ 117 | snmp_send_event = event_new(event_base, sock, EV_WRITE, snmp_write_cb, NULL); 118 | event_add(snmp_send_event, NULL); 119 | } 120 | 121 | static void 122 | transport_running(void) 123 | { 124 | /* Initialize libevent */ 125 | event_base = event_base_new(); 126 | 127 | /* Receive event can be added only once. */ 128 | snmp_recv_event = event_new(event_base, sock, EV_READ | EV_PERSIST, snmp_read_cb, NULL); 129 | event_add(snmp_recv_event, NULL); 130 | 131 | /* Enter the event loop; does not return. */ 132 | event_base_dispatch(event_base); 133 | 134 | event_base_free(event_base); 135 | 136 | close(sock); 137 | } 138 | 139 | static void 140 | transport_stop(void) 141 | { 142 | event_base_loopexit(event_base, 0); 143 | } 144 | 145 | static int 146 | transport_init(int port) 147 | { 148 | struct sockaddr_in sin; 149 | 150 | sock = socket(AF_INET, SOCK_DGRAM, 0); 151 | if (sock < 0) { 152 | perror("usock"); 153 | return -1; 154 | } 155 | 156 | memset(&sin, 0, sizeof(sin)); 157 | sin.sin_family = AF_INET; 158 | sin.sin_addr.s_addr = INADDR_ANY; 159 | sin.sin_port = htons(port); 160 | 161 | /* Initialize send queue */ 162 | TAILQ_INIT(&send_queue_head); 163 | 164 | if (bind(sock, (struct sockaddr *) &sin, sizeof(sin))) { 165 | perror("bind()"); 166 | close(sock); 167 | return -1; 168 | } 169 | 170 | return 0; 171 | } 172 | 173 | struct transport_operation snmp_trans_ops = { 174 | "snmp_libevent", 175 | transport_init, 176 | transport_running, 177 | transport_stop, 178 | transport_send, 179 | }; 180 | -------------------------------------------------------------------------------- /core/snmp_decoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "asn1.h" 26 | 27 | /* Input: buffer, byte length; 28 | * Output: none 29 | * Return: number of elements 30 | */ 31 | static uint32_t 32 | ber_oid_dec_try(const uint8_t *buf, uint32_t len) 33 | { 34 | uint32_t i, j; 35 | 36 | for (j = 2, i = 1; i < len; i++) { 37 | if (!(buf[i] & 0x80)) { 38 | j++; 39 | } 40 | } 41 | 42 | return j; 43 | } 44 | 45 | /* Input: buffer, byte length, value type; 46 | * Output: none 47 | * Return: number of elements 48 | */ 49 | uint32_t 50 | ber_value_dec_try(const uint8_t *buf, uint32_t len, uint8_t type) 51 | { 52 | uint32_t ret; 53 | 54 | if (len == 0) 55 | return 0; 56 | 57 | switch (type) { 58 | case ASN1_TAG_INT: 59 | case ASN1_TAG_CNT: 60 | case ASN1_TAG_GAU: 61 | case ASN1_TAG_TIMETICKS: 62 | ret = 1; 63 | break; 64 | case ASN1_TAG_OBJID: 65 | ret = ber_oid_dec_try(buf, len); 66 | break; 67 | case ASN1_TAG_OCTSTR: 68 | case ASN1_TAG_IPADDR: 69 | ret = len; 70 | break; 71 | case ASN1_TAG_SEQ: 72 | case ASN1_TAG_NUL: 73 | default: 74 | ret = 0; 75 | break; 76 | } 77 | 78 | return ret; 79 | } 80 | 81 | /* Input: buffer, byte length; 82 | * Output: interger pointer 83 | * Return: number of elements 84 | */ 85 | static uint32_t 86 | ber_int_dec(const uint8_t *buf, uint32_t len, int *value) 87 | { 88 | int i, j; 89 | 90 | *value = 0; 91 | 92 | if (buf[0] & 0x80) { 93 | for (i = 0, j = 0; j < sizeof(int) - len; j++) { 94 | *value = (*value << 8) | 0xff; 95 | } 96 | } else { 97 | i = 0; 98 | if (!buf[0]) { 99 | i++; 100 | } 101 | } 102 | 103 | while (i < len) { 104 | *value = (*value << 8) | buf[i++]; 105 | } 106 | 107 | return 1; 108 | } 109 | 110 | /* Input: buffer, byte length; 111 | * Output: unsigned interger pointer 112 | * Return: number of elements 113 | */ 114 | static uint32_t 115 | ber_uint_dec(const uint8_t *buf, uint32_t len, unsigned int *value) 116 | { 117 | int i; 118 | 119 | i = 0; 120 | if (!buf[0]) { 121 | i++; 122 | } 123 | 124 | *value = 0; 125 | while (i < len) { 126 | *value = (*value << 8) | buf[i++]; 127 | } 128 | 129 | return 1; 130 | } 131 | 132 | 133 | /* Input: buffer, byte length; 134 | * Output: oid pointer 135 | * Return: number of elements 136 | */ 137 | static uint32_t 138 | ber_oid_dec(const uint8_t *buf, uint32_t len, oid_t *oid) 139 | { 140 | uint32_t i, j; 141 | oid_t id; 142 | 143 | oid[0] = buf[0] / 40; 144 | oid[1] = buf[0] % 40; 145 | 146 | for (id = 0, j = 2, i = 1; i < len; i++) { 147 | id = (id << 7) | (buf[i] & 0x7f); 148 | if (!(buf[i] & 0x80)) { 149 | oid[j++] = id; 150 | id = 0; 151 | } 152 | } 153 | 154 | return j; 155 | } 156 | 157 | /* Input: buffer, byte length, value type; 158 | * Output: value pointer 159 | * Return: number of elements 160 | */ 161 | uint32_t 162 | ber_value_dec(const uint8_t *buf, uint32_t len, uint8_t type, void *value) 163 | { 164 | uint32_t ret; 165 | 166 | if (len == 0) 167 | return 0; 168 | 169 | switch (type) { 170 | case ASN1_TAG_INT: 171 | case ASN1_TAG_CNT: 172 | ret = ber_int_dec(buf, len, value); 173 | break; 174 | case ASN1_TAG_GAU: 175 | case ASN1_TAG_TIMETICKS: 176 | ret = ber_uint_dec(buf, len, value); 177 | break; 178 | case ASN1_TAG_OBJID: 179 | ret = ber_oid_dec(buf, len, value); 180 | break; 181 | case ASN1_TAG_OCTSTR: 182 | case ASN1_TAG_IPADDR: 183 | memcpy(value, buf, len); 184 | ret = len; 185 | break; 186 | case ASN1_TAG_SEQ: 187 | case ASN1_TAG_NUL: 188 | default: 189 | ret = 0; 190 | break; 191 | } 192 | 193 | return ret; 194 | } 195 | 196 | /* Input: buffer 197 | * Output: none 198 | * Return: length value 199 | */ 200 | uint32_t 201 | ber_length_dec_try(const uint8_t *buf) 202 | { 203 | uint32_t len = 1; 204 | 205 | if (buf[0] & 0x80) { 206 | len += buf[0] & 0x7f; 207 | } 208 | 209 | return len; 210 | } 211 | 212 | /* Input: buffer 213 | * Output: value pointer 214 | * Return: length value 215 | */ 216 | uint32_t 217 | ber_length_dec(const uint8_t *buf, uint32_t *value) 218 | { 219 | uint32_t i = 0, len = 1; 220 | 221 | if (buf[0] & 0x80) { 222 | len += buf[0] & 0x7f; 223 | i++; 224 | } 225 | 226 | *value = 0; 227 | while (i < len) 228 | *value = (*value << 8) | buf[i++]; 229 | 230 | return len; 231 | } 232 | -------------------------------------------------------------------------------- /core/mib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _MIB_H_ 22 | #define _MIB_H_ 23 | 24 | #include "asn1.h" 25 | #include "list.h" 26 | #include "lua.h" 27 | #include "lualib.h" 28 | #include "lauxlib.h" 29 | 30 | #define MIB_OBJ_UNKNOWN 0 31 | #define MIB_OBJ_GROUP 1 32 | #define MIB_OBJ_INSTANCE 2 33 | 34 | /* MIB request type */ 35 | typedef enum mib_request { 36 | MIB_REQ_GET = 0xA0, 37 | MIB_REQ_GETNEXT = 0xA1, 38 | MIB_RESP = 0xA2, 39 | MIB_REQ_SET = 0xA3, 40 | MIB_REQ_BULKGET = 0xA5, 41 | MIB_REQ_INF = 0xA6, 42 | MIB_TRAP = 0xA7, 43 | MIB_REPO = 0xA8, 44 | } MIB_REQ_E; 45 | 46 | /* MIB access attribute */ 47 | typedef enum mib_aces_attr { 48 | MIB_ACES_READ = 1, 49 | MIB_ACES_WRITE 50 | } MIB_ACES_ATTR_E; 51 | 52 | struct oid_search_res { 53 | /* Return oid */ 54 | oid_t *oid; 55 | uint32_t id_len; 56 | /* Instance oid of return */ 57 | oid_t *inst_id; 58 | uint32_t inst_id_len; 59 | /* Instance search callback in Lua */ 60 | int callback; 61 | /* Request id */ 62 | int request; 63 | /* Error status */ 64 | int err_stat; 65 | /* Search return value */ 66 | Variable var; 67 | }; 68 | 69 | struct mib_node { 70 | uint8_t type; 71 | }; 72 | 73 | struct mib_group_node { 74 | uint8_t type; 75 | uint16_t sub_id_cap; 76 | uint16_t sub_id_cnt; 77 | oid_t *sub_id; 78 | void **sub_ptr; 79 | }; 80 | 81 | struct mib_instance_node { 82 | uint8_t type; 83 | int callback; 84 | }; 85 | 86 | struct mib_view { 87 | struct mib_view *next; 88 | const oid_t *oid; 89 | uint32_t id_len; 90 | /* head of all relevant communities */ 91 | struct list_head communities; 92 | /* head of all relevant users */ 93 | struct list_head users; 94 | }; 95 | 96 | struct mib_community { 97 | struct mib_community *next; 98 | const char *name; 99 | /* head of relevant read only view */ 100 | struct list_head ro_views; 101 | /* head of relevant read write view */ 102 | struct list_head rw_views; 103 | }; 104 | 105 | struct mib_user { 106 | struct mib_user *next; 107 | const char *name; 108 | /* head of relevant read only view */ 109 | struct list_head ro_views; 110 | /* head of relevant read write view */ 111 | struct list_head rw_views; 112 | }; 113 | 114 | struct community_view { 115 | /* link to mib view */ 116 | struct list_head vlink; 117 | /* link to mib community */ 118 | struct list_head clink; 119 | struct mib_view *view; 120 | struct mib_community *community; 121 | }; 122 | 123 | struct user_view { 124 | /* link to mib view */ 125 | struct list_head vlink; 126 | /* link to mib user */ 127 | struct list_head ulink; 128 | struct mib_view *view; 129 | struct mib_user *user; 130 | }; 131 | 132 | extern lua_State *mib_lua_state; 133 | 134 | oid_t *oid_dup(const oid_t *oid, uint32_t len); 135 | oid_t *oid_cpy(oid_t *oid_dest, const oid_t *oid_src, uint32_t len); 136 | int oid_cmp(const oid_t *src, uint32_t src_len, const oid_t *target, uint32_t tar_len); 137 | int oid_cover(const oid_t *oid1, uint32_t len1, const oid_t *oid2, uint32_t len2); 138 | 139 | void mib_handler_unref(int handler); 140 | int mib_instance_search(struct oid_search_res *ret_oid); 141 | struct mib_node *mib_tree_search(struct mib_view *view, const oid_t *oid, uint32_t id_len, struct oid_search_res *ret_oid); 142 | void mib_tree_search_next(struct mib_view *view, const oid_t *oid, uint32_t id_len, struct oid_search_res *ret_oid); 143 | 144 | int mib_node_reg(const oid_t *oid, uint32_t id_len, int callback); 145 | void mib_node_unreg(const oid_t *oid, uint32_t id_len); 146 | void mib_community_reg(const oid_t *oid, uint32_t len, const char *community, MIB_ACES_ATTR_E attribute); 147 | void mib_community_unreg(const char *community, MIB_ACES_ATTR_E attribute); 148 | void mib_user_reg(const oid_t *oid, uint32_t len, const char *community, MIB_ACES_ATTR_E attribute); 149 | void mib_user_unreg(const char *user, MIB_ACES_ATTR_E attribute); 150 | struct mib_community *mib_community_search(const char *community); 151 | struct mib_view *mib_community_next_view(struct mib_community *c, MIB_ACES_ATTR_E attribute, struct mib_view *v); 152 | int mib_community_view_cover(struct mib_community *c, MIB_ACES_ATTR_E attribute, const oid_t *oid, uint32_t id_len); 153 | struct mib_user *mib_user_search(const char *user); 154 | struct mib_view *mib_user_next_view(struct mib_user *u, MIB_ACES_ATTR_E attribute, struct mib_view *v); 155 | int mib_user_view_cover(struct mib_user *u, MIB_ACES_ATTR_E attribute, const oid_t *oid, uint32_t id_len); 156 | 157 | void mib_init(void); 158 | 159 | #endif /* _MIB_H_ */ 160 | -------------------------------------------------------------------------------- /mibs/udp.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | 22 | local udpInDatagrams = 1 23 | local udpNoPorts = 2 24 | local udpInErrors = 3 25 | local udpOutDatagrams = 4 26 | local udpTable = 5 27 | 28 | local udp_entry_cache = {} 29 | local udp_scalar_cache = {} 30 | --[[ 31 | ["0.0.0.0.67"] = {}, 32 | ["0.0.0.0.68"] = {}, 33 | ["0.0.0.0.161"] = {}, 34 | ["0.0.0.0.5353"] = {}, 35 | ["0.0.0.0.44681"] = {}, 36 | ["0.0.0.0.51586"] = {}, 37 | ["127.0.0.1.53"] = {}, 38 | ["192.168.122.1.53"] = {}, 39 | ]] 40 | 41 | local hextab = { 42 | ['0'] = 0, 43 | ['1'] = 1, 44 | ['2'] = 2, 45 | ['3'] = 3, 46 | ['4'] = 4, 47 | ['5'] = 5, 48 | ['6'] = 6, 49 | ['7'] = 7, 50 | ['8'] = 8, 51 | ['9'] = 9, 52 | ['A'] = 10, 53 | ['a'] = 10, 54 | ['B'] = 11, 55 | ['b'] = 11, 56 | ['C'] = 12, 57 | ['c'] = 12, 58 | ['D'] = 13, 59 | ['d'] = 13, 60 | ['E'] = 14, 61 | ['e'] = 14, 62 | ['F'] = 15, 63 | ['f'] = 15, 64 | } 65 | 66 | local function ip_hex2num(hex) 67 | assert(type(hex) == 'string' and #hex == 8) 68 | local num = {} 69 | for i = 8, 2, -2 do 70 | local msb = string.char(string.byte(hex, i - 1)) 71 | local lsb = string.char(string.byte(hex, i)) 72 | table.insert(num, hextab[msb] * 16 + hextab[lsb]) 73 | end 74 | return table.concat(num, ".") 75 | end 76 | 77 | local function port_hex2num(hex) 78 | assert(type(hex) == 'string' and #hex == 4) 79 | local num = 0 80 | for i = 1, 4 do 81 | num = num * 16 + hextab[string.char(string.byte(hex, i))] 82 | end 83 | return tostring(num) 84 | end 85 | 86 | local function __load_config() 87 | udp_scalar_cache = {} 88 | udp_entry_cache = {} 89 | for line in io.lines("/proc/net/snmp") do 90 | if string.match(line, "%w+") == 'Udp' then 91 | for w in string.gmatch(line, "%d+") do 92 | table.insert(udp_scalar_cache, tonumber(w)) 93 | end 94 | end 95 | end 96 | for line in io.lines("/proc/net/udp") do 97 | local ipaddr = string.match(line, ".-:%s+(.-):") 98 | local port = string.match(line, ".-:%s+.-:(.-)%s+") 99 | if ipaddr ~= nil and port ~= nil then 100 | ipaddr = ip_hex2num(ipaddr) 101 | port = port_hex2num(port) 102 | udp_entry_cache[ipaddr .. '.' .. port] = true 103 | end 104 | end 105 | end 106 | 107 | local last_load_time = os.time() 108 | 109 | local function need_to_reload() 110 | if os.difftime(os.time(), last_load_time) < 3 then 111 | return false 112 | else 113 | last_load_time = os.time() 114 | return true 115 | end 116 | end 117 | 118 | local function load_config() 119 | if need_to_reload() == true then 120 | __load_config() 121 | end 122 | end 123 | 124 | __load_config() 125 | 126 | mib.module_methods.or_table_reg("1.3.6.1.2.1.7", "The MIB module for managing UDP inplementations") 127 | 128 | local udpGroup = { 129 | [udpInDatagrams] = mib.ConstCount(function () load_config() return udp_scalar_cache[1] end), 130 | [udpNoPorts] = mib.ConstCount(function () load_config() return udp_scalar_cache[2] end), 131 | [udpInErrors] = mib.ConstCount(function () load_config() return udp_scalar_cache[3] end), 132 | [udpOutDatagrams] = mib.ConstCount(function () load_config() return udp_scalar_cache[4] end), 133 | [udpTable] = { 134 | [1] = { 135 | indexes = udp_entry_cache, 136 | [1] = mib.ConstIpaddr(function (sub_oid) 137 | load_config() 138 | local ipaddr 139 | if type(sub_oid) == 'table' and udp_entry_cache[table.concat(sub_oid, ".")] ~= nil then 140 | ipaddr = {} 141 | for i = 1, 4 do 142 | table.insert(ipaddr, sub_oid[i]) 143 | end 144 | end 145 | return ipaddr 146 | end), 147 | [2] = mib.ConstInt(function (sub_oid) 148 | load_config() 149 | local port 150 | if type(sub_oid) == 'table' and udp_entry_cache[table.concat(sub_oid, ".")] ~= nil then 151 | port = sub_oid[5] 152 | end 153 | return port 154 | end), 155 | } 156 | } 157 | } 158 | 159 | return udpGroup 160 | -------------------------------------------------------------------------------- /core/snmp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _SNMP_H_ 22 | #define _SNMP_H_ 23 | 24 | #include "asn1.h" 25 | #include "list.h" 26 | 27 | /* Error status */ 28 | typedef enum snmp_err_stat { 29 | /* v1 */ 30 | SNMP_ERR_STAT_NO_ERR, 31 | SNMP_ERR_STAT_TOO_BIG = 1, 32 | SNMP_ERR_STAT_NO_SUCH_NAME, 33 | SNMP_ERR_STAT_BAD_VALUE, 34 | SNMP_ERR_STAT_READ_ONLY, 35 | SNMP_ERR_STAT_GEN_ERR, 36 | 37 | /* v2c */ 38 | SNMP_ERR_STAT_NO_ACCESS, 39 | SNMP_ERR_STAT_WRONG_TYPE, 40 | SNMP_ERR_STAT_WRONG_LEN, 41 | SNMP_ERR_STAT_ENCODING, 42 | SNMP_ERR_STAT_WRONG_VALUE, 43 | SNMP_ERR_STAT_NO_CREATION, 44 | SNMP_ERR_STAT_INCONSISTENT_VALUE, 45 | SNMP_ERR_STAT_RESOURCE_UNAVAIL, 46 | SNMP_ERR_STAT_COMMIT_FAILED, 47 | SNMP_ERR_STAT_UNDO_FAILED, 48 | SNMP_ERR_STAT_AUTHORIZATION, 49 | SNMP_ERR_STAT_NOT_WRITABLE, 50 | SNMP_ERR_STAT_INCONSISTENT_NAME, 51 | } SNMP_ERR_STAT_E; 52 | 53 | /* return code */ 54 | typedef enum snmp_err_code { 55 | SNMP_ERR_OK = 0, 56 | 57 | SNMP_ERR_VERSION = -100, 58 | 59 | SNMP_ERR_GLOBAL_DATA_LEN = -200, 60 | SNMP_ERR_GLOBAL_ID = -201, 61 | SNMP_ERR_GLOBAL_SIZE = -202, 62 | SNMP_ERR_GLOBAL_FLAGS = -203, 63 | SNMP_ERR_GLOBAL_MODEL = -204, 64 | 65 | SNMP_ERR_SECURITY_STR = -300, 66 | SNMP_ERR_SECURITY_SEQ = -301, 67 | SNMP_ERR_SECURITY_ENGINE_ID = -302, 68 | SNMP_ERR_SECURITY_ENGINE_ID_LEN = -303, 69 | SNMP_ERR_SECURITY_ENGINE_BOOTS = -304, 70 | SNMP_ERR_SECURITY_ENGINE_TIME = -305, 71 | SNMP_ERR_SECURITY_USER_NAME = -306, 72 | SNMP_ERR_SECURITY_USER_NAME_LEN = -307, 73 | SNMP_ERR_SECURITY_AUTH_PARA = -308, 74 | SNMP_ERR_SECURITY_AUTH_PARA_LEN = -309, 75 | SNMP_ERR_SECURITY_PRIV_PARA = -310, 76 | SNMP_ERR_SECURITY_PRIV_PARA_LEN = -311, 77 | 78 | SNMP_ERR_SCOPE_PDU_SEQ = -400, 79 | 80 | SNMP_ERR_CONTEXT_ID = -500, 81 | SNMP_ERR_CONTEXT_ID_LEN = -501, 82 | SNMP_ERR_CONTEXT_NAME = -502, 83 | SNMP_ERR_CONTEXT_NAME_LEN = -503, 84 | 85 | SNMP_ERR_PDU_TYPE = -600, 86 | SNMP_ERR_PDU_LEN = -601, 87 | SNMP_ERR_PDU_REQID = -602, 88 | SNMP_ERR_PDU_ERRSTAT = -603, 89 | SNMP_ERR_PDU_ERRIDX = -604, 90 | 91 | SNMP_ERR_VB_LIST_SEQ = -700, 92 | SNMP_ERR_VB_SEQ = -701, 93 | SNMP_ERR_VB_OID_TYPE = -702, 94 | SNMP_ERR_VB_VAR = -703, 95 | SNMP_ERR_VB_VALUE_LEN = -704, 96 | SNMP_ERR_VB_OID_LEN = -705, 97 | } SNMP_ERR_CODE_E; 98 | 99 | struct var_bind { 100 | struct list_head link; 101 | 102 | oid_t *oid; 103 | uint32_t vb_len; 104 | uint32_t oid_len; 105 | 106 | /* Number of elements as vb_in, 107 | * number of bytes as vb_out. */ 108 | uint32_t value_len; 109 | uint8_t value_type; 110 | uint8_t value[0]; 111 | }; 112 | 113 | struct pdu_hdr { 114 | uint8_t pdu_type; 115 | uint32_t pdu_len; 116 | /* request id */ 117 | uint32_t req_id_len; 118 | integer_t req_id; 119 | /* Error status */ 120 | uint32_t err_stat_len; 121 | integer_t err_stat; 122 | /* Error index */ 123 | uint32_t err_idx_len; 124 | integer_t err_idx; 125 | }; 126 | 127 | struct snmp_datagram { 128 | void *recv_buf; 129 | void *send_buf; 130 | 131 | uint32_t data_len; 132 | /* version */ 133 | integer_t version; 134 | uint32_t ver_len; 135 | /* global data */ 136 | uint32_t msg_len; 137 | integer_t msg_id; 138 | uint32_t msg_id_len; 139 | integer_t msg_max_size; 140 | uint32_t msg_size_len; 141 | octstr_t msg_flags; 142 | uint32_t msg_flags_len; 143 | integer_t msg_security_model; 144 | uint32_t msg_model_len; 145 | /* security parameter */ 146 | uint32_t secur_str_len; 147 | uint32_t secur_para_len; 148 | octstr_t engine_id[33]; 149 | uint32_t engine_id_len; 150 | integer_t engine_boots; 151 | uint32_t engine_boots_len; 152 | integer_t engine_time; 153 | uint32_t engine_time_len; 154 | octstr_t user_name[41]; 155 | uint32_t user_name_len; 156 | octstr_t auth_para[41]; 157 | uint32_t auth_para_len; 158 | octstr_t priv_para[41]; 159 | uint32_t priv_para_len; 160 | /* context */ 161 | uint32_t scope_len; 162 | octstr_t context_id[33]; 163 | uint32_t context_id_len; 164 | octstr_t context_name[41]; 165 | uint32_t context_name_len; 166 | /* PDU */ 167 | struct pdu_hdr pdu_hdr; 168 | /* varbind */ 169 | uint32_t vb_list_len; 170 | uint32_t vb_in_cnt; 171 | uint32_t vb_out_cnt; 172 | struct list_head vb_in_list; 173 | struct list_head vb_out_list; 174 | }; 175 | 176 | extern struct snmp_datagram snmp_datagram; 177 | 178 | uint32_t ber_value_enc_try(const void *value, uint32_t len, uint8_t type); 179 | uint32_t ber_value_enc(const void *value, uint32_t len, uint8_t type, uint8_t *buf); 180 | uint32_t ber_length_enc_try(uint32_t value); 181 | uint32_t ber_length_enc(uint32_t value, uint8_t *buf); 182 | 183 | uint32_t ber_value_dec_try(const uint8_t *buf, uint32_t len, uint8_t type); 184 | uint32_t ber_value_dec(const uint8_t *buf, uint32_t len, uint8_t type, void *value); 185 | uint32_t ber_length_dec_try(const uint8_t *buf); 186 | uint32_t ber_length_dec(const uint8_t *buf, uint32_t *value); 187 | 188 | void snmpd_recv(uint8_t *buf, int len); 189 | 190 | void snmp_get(struct snmp_datagram *sdg); 191 | void snmp_getnext(struct snmp_datagram *sdg); 192 | void snmp_set(struct snmp_datagram *sdg); 193 | void snmp_bulkget(struct snmp_datagram *sdg); 194 | void snmp_response(struct snmp_datagram *sdg); 195 | #endif /* _SNMP_H_ */ 196 | -------------------------------------------------------------------------------- /bin/smartsnmpd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lua 2 | -- 3 | -- This file is part of SmartSNMP 4 | -- Copyright (C) 2014, Credo Semiconductor Inc. 5 | -- 6 | -- This program is free software; you can redistribute it and/or modify 7 | -- it under the terms of the GNU General Public License as published by 8 | -- the Free Software Foundation; either version 2 of the License, or 9 | -- (at your option) any later version. 10 | -- 11 | -- This program is distributed in the hope that it will be useful, 12 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | -- GNU General Public License for more details. 15 | -- 16 | -- You should have received a copy of the GNU General Public License along 17 | -- with this program; if not, write to the Free Software Foundation, Inc., 18 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | -- local mib = require "lualib.mib" 20 | -- 21 | 22 | local snmpd = require "smartsnmp" 23 | local utils = require "smartsnmp.utils" 24 | 25 | -- default configuration file 26 | local config_file = 'config/snmp.conf' 27 | 28 | ------------------------------------------------------------------------------- 29 | -- parse arguments 30 | ------------------------------------------------------------------------------- 31 | 32 | local opts = utils.getopt( arg, "pc" ) 33 | for k, v in pairs(opts) do 34 | if k == 'c' and type(v) == 'string' then 35 | config_file = v 36 | end 37 | end 38 | 39 | ------------------------------------------------------------------------------- 40 | -- load configuration file 41 | ------------------------------------------------------------------------------- 42 | 43 | -- load mib module file 44 | local config_chunk, err = loadfile(config_file) 45 | if config_chunk == nil then 46 | print("Failed to open configuration file.") 47 | print(err) 48 | os.exit(-1) 49 | end 50 | 51 | local status, config = pcall(config_chunk) 52 | if status == false then 53 | print("Failed to load configuration file.") 54 | print(config) 55 | os.exit(-1) 56 | end 57 | 58 | -- check configurations 59 | if type(protocol) ~= 'string' then 60 | print("Can't get transport protocol for SNMP agent, please check your configuration file!") 61 | os.exit(-1) 62 | end 63 | 64 | if type(port) ~= 'number' then 65 | print("Can't get listen port number for SNMP agent, please check your configuration file!") 66 | os.exit(-1) 67 | end 68 | 69 | if communities ~= nil and type(communities) ~= 'table' then 70 | print("Can't set communities for SNMPv2c agent, please check your configuration file!") 71 | os.exit(-1) 72 | end 73 | 74 | if users ~= nil and type(users) ~= 'table' then 75 | print("Can't set users for SNMPv3 agent, please check your configuration file!") 76 | os.exit(-1) 77 | end 78 | 79 | if type(mib_module_path) ~= 'string' then 80 | print("Can't get mib_module_path for SNMP agent, please check your configuration file!") 81 | os.exit(-1) 82 | end 83 | 84 | if type(mib_modules) ~= 'table' then 85 | print("Can't get mib_modules for SNMP agent, please check your configuration file!") 86 | os.exit(-1) 87 | end 88 | 89 | ------------------------------------------------------------------------------- 90 | -- setup snmp agent, load mib modules and run it. 91 | ------------------------------------------------------------------------------- 92 | 93 | -- load mib module with error handling 94 | local register_mib_module = function(oid_str, mib_module_name) 95 | -- TODO: check if oid is illegal 96 | local oid = utils.str2oid(oid_str) 97 | local mib_module_file = mib_module_path..'/'..mib_module_name..'.lua' 98 | 99 | -- load mib module file 100 | local mib_module, err = loadfile(mib_module_file) 101 | if mib_module == nil then 102 | return false, err 103 | end 104 | 105 | local status, mib_group_or_err = pcall(mib_module) 106 | if status == false then 107 | return false, mib_group_or_err 108 | end 109 | 110 | return pcall(snmpd.register_mib_group, oid, mib_group_or_err, mib_module_name) 111 | end 112 | 113 | -- Sort for module reference sequence 114 | local mib_mod_refs = {} 115 | 116 | for oid_str, mib_module_name in pairs(mib_modules) do 117 | local row = {} 118 | row['oid'] = oid_str 119 | row['name'] = mib_module_name 120 | if (row['name'] == "system") then 121 | table.insert(mib_mod_refs, 1, row) 122 | else 123 | table.insert(mib_mod_refs, row) 124 | end 125 | end 126 | 127 | if communities ~= nil then 128 | for _, t in ipairs(communities) do 129 | if t.views ~= nil then 130 | if next(t.views) == nil then 131 | snmpd.set_rw_community(t.community) 132 | snmpd.set_ro_community(t.community) 133 | else 134 | for view, attribute in pairs(t.views) do 135 | if attribute == 'rw' then 136 | snmpd.set_rw_community(t.community, utils.str2oid(view)) 137 | else 138 | snmpd.set_ro_community(t.community, utils.str2oid(view)) 139 | end 140 | end 141 | end 142 | end 143 | end 144 | end 145 | 146 | if users ~= nil then 147 | for _, t in ipairs(users) do 148 | if t.views ~= nil then 149 | if next(t.views) == nil then 150 | snmpd.set_rw_user(t.user) 151 | snmpd.set_ro_user(t.user) 152 | else 153 | for view, attribute in pairs(t.views) do 154 | if attribute == 'rw' then 155 | snmpd.set_rw_user(t.user, utils.str2oid(view)) 156 | else 157 | snmpd.set_ro_user(t.user, utils.str2oid(view)) 158 | end 159 | end 160 | end 161 | end 162 | end 163 | end 164 | 165 | snmpd.init(protocol, port) 166 | 167 | snmpd.open() 168 | 169 | for i, v in ipairs(mib_mod_refs) do 170 | status, err = register_mib_module(v['oid'], v['name']) 171 | if status ~= true then 172 | print("Failed to load MIB module: "..v['name']) 173 | print(err) 174 | end 175 | end 176 | 177 | mib_modules = nil 178 | mib_mod_refs = nil 179 | 180 | if protocol == 'snmp' then 181 | print("SmartSNMP (Mode: SNMP Agent)") 182 | else 183 | print("SmartSNMP (Mode: AgentX Sub-Agent)") 184 | end 185 | 186 | snmpd.start() 187 | -------------------------------------------------------------------------------- /core/list.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2011 Felix Fietkau 3 | * Copyright (c) 2010 Isilon Systems, Inc. 4 | * Copyright (c) 2010 iX Systems, Inc. 5 | * Copyright (c) 2010 Panasas, Inc. 6 | * All rights reserved. 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 unmodified, this list of conditions, and the following 13 | * disclaimer. 14 | * 2. Redistributions in binary form must reproduce the above copyright 15 | * notice, this list of conditions and the following disclaimer in the 16 | * documentation and/or other materials provided with the distribution. 17 | * 18 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | #ifndef _LINUX_LIST_H_ 30 | #define _LINUX_LIST_H_ 31 | 32 | #include 33 | #include 34 | 35 | #define prefetch(x) 36 | 37 | #define container_of(ptr, type, member) \ 38 | ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member))) 39 | 40 | #ifndef container_of 41 | #define container_of(ptr, type, member) \ 42 | ({ \ 43 | const typeof(((type *) NULL)->member) *__mptr = (ptr); \ 44 | (type *) ((char *) __mptr - offsetof(type, member)); \ 45 | }) 46 | #endif 47 | 48 | struct list_head { 49 | struct list_head *next; 50 | struct list_head *prev; 51 | }; 52 | 53 | #define LIST_HEAD_INIT(name) { &(name), &(name) } 54 | #undef LIST_HEAD 55 | #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) 56 | 57 | static inline void 58 | INIT_LIST_HEAD(struct list_head *list) 59 | { 60 | list->next = list->prev = list; 61 | } 62 | 63 | static inline bool 64 | list_empty(const struct list_head *head) 65 | { 66 | return (head->next == head); 67 | } 68 | 69 | static inline bool 70 | list_is_first(const struct list_head *list, 71 | const struct list_head *head) 72 | { 73 | return list->prev == head; 74 | } 75 | 76 | static inline bool 77 | list_is_last(const struct list_head *list, 78 | const struct list_head *head) 79 | { 80 | return list->next == head; 81 | } 82 | 83 | static inline void 84 | _list_del(struct list_head *entry) 85 | { 86 | entry->next->prev = entry->prev; 87 | entry->prev->next = entry->next; 88 | } 89 | 90 | static inline void 91 | list_del(struct list_head *entry) 92 | { 93 | _list_del(entry); 94 | entry->next = entry->prev = NULL; 95 | } 96 | 97 | static inline void 98 | _list_add(struct list_head *_new, struct list_head *prev, 99 | struct list_head *next) 100 | { 101 | 102 | next->prev = _new; 103 | _new->next = next; 104 | _new->prev = prev; 105 | prev->next = _new; 106 | } 107 | 108 | static inline void 109 | list_del_init(struct list_head *entry) 110 | { 111 | _list_del(entry); 112 | INIT_LIST_HEAD(entry); 113 | } 114 | 115 | #define list_entry(ptr, type, field) container_of(ptr, type, field) 116 | #define list_first_entry(ptr, type, field) list_entry((ptr)->next, type, field) 117 | #define list_last_entry(ptr, type, field) list_entry((ptr)->prev, type, field) 118 | 119 | #define list_for_each(p, head) \ 120 | for (p = (head)->next; p != (head); p = p->next) 121 | 122 | #define list_for_each_safe(p, n, head) \ 123 | for (p = (head)->next, n = p->next; p != (head); p = n, n = p->next) 124 | 125 | #define list_for_each_entry(p, h, field) \ 126 | for (p = list_first_entry(h, typeof(*p), field); &p->field != (h); \ 127 | p = list_entry(p->field.next, typeof(*p), field)) 128 | 129 | #define list_for_each_entry_safe(p, n, h, field) \ 130 | for (p = list_first_entry(h, typeof(*p), field), \ 131 | n = list_entry(p->field.next, typeof(*p), field); &p->field != (h);\ 132 | p = n, n = list_entry(n->field.next, typeof(*n), field)) 133 | 134 | #define list_for_each_entry_reverse(p, h, field) \ 135 | for (p = list_last_entry(h, typeof(*p), field); &p->field != (h); \ 136 | p = list_entry(p->field.prev, typeof(*p), field)) 137 | 138 | #define list_for_each_prev(p, h) for (p = (h)->prev; p != (h); p = p->prev) 139 | #define list_for_each_prev_safe(p, n, h) for (p = (h)->prev, n = p->prev; p != (h); p = n, n = p->prev) 140 | 141 | static inline void 142 | list_add(struct list_head *_new, struct list_head *head) 143 | { 144 | _list_add(_new, head, head->next); 145 | } 146 | 147 | static inline void 148 | list_add_tail(struct list_head *_new, struct list_head *head) 149 | { 150 | _list_add(_new, head->prev, head); 151 | } 152 | 153 | static inline void 154 | list_move(struct list_head *list, struct list_head *head) 155 | { 156 | _list_del(list); 157 | list_add(list, head); 158 | } 159 | 160 | static inline void 161 | list_move_tail(struct list_head *entry, struct list_head *head) 162 | { 163 | _list_del(entry); 164 | list_add_tail(entry, head); 165 | } 166 | 167 | static inline void 168 | _list_splice(const struct list_head *list, struct list_head *prev, 169 | struct list_head *next) 170 | { 171 | struct list_head *first; 172 | struct list_head *last; 173 | 174 | if (list_empty(list)) 175 | return; 176 | 177 | first = list->next; 178 | last = list->prev; 179 | first->prev = prev; 180 | prev->next = first; 181 | last->next = next; 182 | next->prev = last; 183 | } 184 | 185 | static inline void 186 | list_splice(const struct list_head *list, struct list_head *head) 187 | { 188 | _list_splice(list, head, head->next); 189 | } 190 | 191 | static inline void 192 | list_splice_tail(struct list_head *list, struct list_head *head) 193 | { 194 | _list_splice(list, head->prev, head); 195 | } 196 | 197 | static inline void 198 | list_splice_init(struct list_head *list, struct list_head *head) 199 | { 200 | _list_splice(list, head, head->next); 201 | INIT_LIST_HEAD(list); 202 | } 203 | 204 | static inline void 205 | list_splice_tail_init(struct list_head *list, struct list_head *head) 206 | { 207 | _list_splice(list, head->prev, head); 208 | INIT_LIST_HEAD(list); 209 | } 210 | 211 | #endif /* _LINUX_LIST_H_ */ 212 | -------------------------------------------------------------------------------- /lualib/smartsnmp/utils.lua: -------------------------------------------------------------------------------- 1 | ------------------------------------------------------------------------------ 2 | -- getopt_alt.lua from http://lua-users.org/wiki/AlternativeGetOpt 3 | 4 | -- getopt, POSIX style command line argument parser 5 | -- param arg contains the command line arguments in a standard table. 6 | -- param options is a string with the letters that expect string values. 7 | -- returns a table where associated keys are true, nil, or a string value. 8 | -- The following example styles are supported 9 | -- -a one ==> opts["a"]=="one" 10 | -- -bone ==> opts["b"]=="one" 11 | -- -c ==> opts["c"]==true 12 | -- --c=one ==> opts["c"]=="one" 13 | -- -cdaone ==> opts["c"]==true opts["d"]==true opts["a"]=="one" 14 | -- note POSIX demands the parser ends at the first non option 15 | -- this behavior isn't implemented. 16 | ------------------------------------------------------------------------------ 17 | 18 | local utils = {} 19 | 20 | utils.getopt = function (arg, options) 21 | local tab = {} 22 | for k, v in ipairs(arg) do 23 | if string.sub( v, 1, 2) == "--" then 24 | local x = string.find( v, "=", 1, true ) 25 | if x then tab[ string.sub( v, 3, x-1 ) ] = string.sub( v, x+1 ) 26 | else tab[ string.sub( v, 3 ) ] = true 27 | end 28 | elseif string.sub( v, 1, 1 ) == "-" then 29 | local y = 2 30 | local l = string.len(v) 31 | local jopt 32 | while ( y <= l ) do 33 | jopt = string.sub( v, y, y ) 34 | if string.find( options, jopt, 1, true ) then 35 | if y < l then 36 | tab[ jopt ] = string.sub( v, y+1 ) 37 | y = l 38 | else 39 | tab[ jopt ] = arg[ k + 1 ] 40 | end 41 | else 42 | tab[ jopt ] = true 43 | end 44 | y = y + 1 45 | end 46 | end 47 | end 48 | return tab 49 | end 50 | 51 | ----------------------------------------- 52 | -- convert oid string to oid lua table 53 | ----------------------------------------- 54 | utils.str2oid = function (s) 55 | local oid = {} 56 | for n in string.gmatch(s, '%d+') do 57 | table.insert(oid, tonumber(n)) 58 | end 59 | return oid 60 | end 61 | 62 | -------------------------------------------------------------------------------- 63 | -- iter 64 | -- 65 | -- Return an iterable function. If _o_ is an array table, it will return a 66 | -- function that return the table element one by one. If _o_ is a function, 67 | -- the _o_ will be return directly. 68 | -------------------------------------------------------------------------------- 69 | utils.iter = function(o) 70 | if type(o) == 'function' then 71 | return o 72 | else 73 | local i = 0 74 | local n = table.getn(o) 75 | return function () 76 | i = i + 1 77 | if i <= n then return o[i] end 78 | end 79 | end 80 | end 81 | 82 | ------------------------------------------------------------------------------- 83 | -- map 84 | -- 85 | -- Apply function to every item of iterable and return an array of the results. 86 | ------------------------------------------------------------------------------- 87 | utils.map = function(fun, iter) 88 | local mapped_list = {} 89 | 90 | iter = utils.iter(iter) 91 | 92 | for v in iter do 93 | table.insert(mapped_list, fun(v)) 94 | end 95 | 96 | return mapped_list 97 | end 98 | 99 | ------------------------------------------------------------------------------- 100 | -- reduce 101 | -- 102 | -- Apply function of two arguments cumulatively to the items of iterable, 103 | -- from left to right, so as to reduce the iterable to a single value. 104 | -- 105 | -- iter could be a table or iterable function. 106 | ------------------------------------------------------------------------------- 107 | utils.reduce = function(fun, iter, init) 108 | local accum_val 109 | 110 | iter = utils.iter(iter) 111 | 112 | if init == nil then 113 | init = iter() 114 | if init == nil then 115 | error('reduce() of empty sequence with no initial value') 116 | end 117 | end 118 | 119 | accum_val = init 120 | for v in iter do 121 | accum_val = fun(accum_val, v) 122 | end 123 | 124 | return accum_val 125 | end 126 | 127 | ------------------------------------------------------------------------------- 128 | -- filter 129 | -- 130 | -- Construct an array table from those elements of array table or iterable 131 | -- function for which function returns true. 132 | ------------------------------------------------------------------------------- 133 | utils.filter = function(fun, iter) 134 | local filtered_list = {} 135 | 136 | iter = utils.iter(iter) 137 | 138 | for v in iter do 139 | if fun(v) then 140 | table.insert(filtered_list, v) 141 | end 142 | end 143 | 144 | return filtered_list 145 | end 146 | 147 | -------------------------------------------------------------------------------- 148 | -- str2mac 149 | -- 150 | -- convert MAC address string to table of number 151 | -------------------------------------------------------------------------------- 152 | utils.str2mac = function(mac) 153 | assert(type(mac) == 'string') 154 | local mtab = {mac:match("(%x%x):(%x%x):(%x%x):(%x%x):(%x%x):(%x%x)")} 155 | if #mtab ~= 6 then 156 | error("invaild MAC address.") 157 | end 158 | return utils.map(function(x) return tonumber(x, 16) end, mtab) 159 | end 160 | 161 | -------------------------------------------------------------------------------- 162 | -- mac2oct 163 | -- 164 | -- convert MAC address array table or mac string to encoded octstring 165 | -------------------------------------------------------------------------------- 166 | utils.mac2oct = function(mac) 167 | if type(mac) == 'string' then 168 | mac = utils.str2mac(mac) 169 | end 170 | 171 | return utils.reduce( 172 | function(s, n) 173 | return s..string.char(n) 174 | end, 175 | mac, 176 | '' 177 | ) 178 | end 179 | 180 | -------------------------------------------------------------------------------- 181 | -- print_table 182 | -- 183 | -- pretty print lua table, php print_r like. 184 | -- based on https://gist.github.com/nrk/31175 185 | -------------------------------------------------------------------------------- 186 | utils.print_table = function(t) 187 | local print_r_cache = {} 188 | local sub_print_r = function(t, indent) 189 | if print_r_cache[tostring(t)] then 190 | print(indent.."*"..tostring(t)) 191 | else 192 | print_r_cache[tostring(t)]=true 193 | if type(t) == "table" then 194 | pos = tostring(pos) 195 | for pos,val in pairs(t) do 196 | if type(val) == "table" then 197 | print(indent.."["..pos.."] => "..tostring(t).." {") 198 | sub_print_r(val,indent..string.rep(" ",string.len(pos)+8)) 199 | print(indent..string.rep(" ",string.len(pos)+6).."}") 200 | else 201 | print(indent.."["..pos.."] => "..tostring(val)) 202 | end 203 | end 204 | else 205 | print(indent..tostring(t)) 206 | end 207 | end 208 | end 209 | sub_print_r(t,"\t") 210 | end 211 | 212 | return utils 213 | -------------------------------------------------------------------------------- /core/agentx.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "agentx.h" 32 | #include "transport.h" 33 | #include "protocol.h" 34 | #include "mib.h" 35 | #include "util.h" 36 | 37 | struct agentx_datagram agentx_datagram; 38 | 39 | /* Receive agentX request datagram from transport layer */ 40 | static void 41 | agentx_receive(uint8_t *buf, int len) 42 | { 43 | agentx_recv(buf, len); 44 | } 45 | 46 | /* Send agentX response datagram to transport layer */ 47 | static void 48 | agentx_send(uint8_t *buf, int len) 49 | { 50 | agentx_trans_ops.send(buf, len); 51 | } 52 | 53 | /* Register mib group node */ 54 | static int 55 | agentx_mib_node_reg(const oid_t *grp_id, int id_len, int grp_cb) 56 | { 57 | struct x_pdu_buf x_pdu; 58 | 59 | /* Check oid prefix */ 60 | if (id_len < 4 || grp_id[0] != 1 || grp_id[1] != 3 || grp_id[2] != 6 || grp_id[3] != 1) { 61 | SMARTSNMP_LOG(L_ERROR, "Oid prefix must be .1.3.6.1!\n"); 62 | return -1; 63 | } 64 | 65 | /* Send agentX register PDU */ 66 | x_pdu = agentx_register_pdu(&agentx_datagram, grp_id, id_len, NULL, 0, 0, 127, 0, 0); 67 | if (send(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0) == -1) { 68 | SMARTSNMP_LOG(L_ERROR, "Send agentX register PDU failure!\n"); 69 | return -1; 70 | } 71 | 72 | /* Receive agentX resoponse PDU */ 73 | x_pdu.len = TRANS_BUF_SIZ; 74 | x_pdu.buf = xrealloc(x_pdu.buf, x_pdu.len); 75 | x_pdu.len = recv(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0); 76 | if (x_pdu.len == -1) { 77 | SMARTSNMP_LOG(L_ERROR, "Receive agentX register response PDU failure!\n"); 78 | return -1; 79 | } 80 | 81 | /* Verify register response PDU */ 82 | if (agentx_recv(x_pdu.buf, x_pdu.len) != AGENTX_ERR_OK) { 83 | SMARTSNMP_LOG(L_ERROR, "Parse agentX rigister response PDU error!\n"); 84 | return -1; 85 | } 86 | 87 | /* Register node */ 88 | return mib_node_reg(grp_id, id_len, grp_cb); 89 | } 90 | 91 | /* Unregister mib group node */ 92 | static int 93 | agentx_mib_node_unreg(const oid_t *grp_id, int id_len) 94 | { 95 | struct x_pdu_buf x_pdu; 96 | 97 | /* Check oid prefix */ 98 | if (id_len < 4 || grp_id[0] != 1 || grp_id[1] != 3 || grp_id[2] != 6 || grp_id[3] != 1) { 99 | SMARTSNMP_LOG(L_ERROR, "Oid prefix must be .1.3.6.1!"); 100 | return -1; 101 | } 102 | 103 | /* Send angentX register PDU */ 104 | x_pdu = agentx_unregister_pdu(&agentx_datagram, grp_id, id_len, NULL, 0, 0, 127, 0, 0); 105 | if (send(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0) == -1) { 106 | SMARTSNMP_LOG(L_ERROR, "Send agentX unregister PDU failure!"); 107 | return -1; 108 | } 109 | 110 | /* Receive agentX response PDU */ 111 | x_pdu.len = TRANS_BUF_SIZ; 112 | x_pdu.buf = xrealloc(x_pdu.buf, x_pdu.len); 113 | x_pdu.len = recv(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0); 114 | if (x_pdu.len == -1) { 115 | SMARTSNMP_LOG(L_ERROR, "Receive agentX unregister response PDU failure!\n"); 116 | return -1; 117 | } 118 | 119 | /* Verify register response PDU */ 120 | if (agentx_recv(x_pdu.buf, x_pdu.len) != AGENTX_ERR_OK) { 121 | SMARTSNMP_LOG(L_ERROR, "Unregister response error!"); 122 | return -1; 123 | } 124 | 125 | /* Unregister node */ 126 | mib_node_unreg(grp_id, id_len); 127 | return 0; 128 | } 129 | 130 | static int 131 | agentx_init(int port) 132 | { 133 | INIT_LIST_HEAD(&agentx_datagram.vb_in_list); 134 | INIT_LIST_HEAD(&agentx_datagram.vb_out_list); 135 | INIT_LIST_HEAD(&agentx_datagram.sr_in_list); 136 | INIT_LIST_HEAD(&agentx_datagram.sr_out_list); 137 | return agentx_trans_ops.init(port); 138 | } 139 | 140 | static int 141 | agentx_open(void) 142 | { 143 | struct x_pdu_buf x_pdu; 144 | const char *descr = "SmartSNMP AgentX sub-agent"; 145 | 146 | /* Send agentX open PDU */ 147 | x_pdu = agentx_open_pdu(&agentx_datagram, NULL, 0, descr, strlen(descr)); 148 | if (send(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0) == -1) { 149 | SMARTSNMP_LOG(L_ERROR, "Send agentX open PDU failure!\n"); 150 | return -1; 151 | } 152 | 153 | /* Receive agentX open response PDU */ 154 | x_pdu.len = TRANS_BUF_SIZ; 155 | x_pdu.buf = xrealloc(x_pdu.buf, x_pdu.len); 156 | x_pdu.len = recv(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0); 157 | if (x_pdu.len == -1) { 158 | SMARTSNMP_LOG(L_ERROR, "Receive agentX open response PDU failure!\n"); 159 | return -1; 160 | } 161 | 162 | /* Verify open response PDU */ 163 | if (agentx_recv(x_pdu.buf, x_pdu.len) != AGENTX_ERR_OK) { 164 | SMARTSNMP_LOG(L_ERROR, "Parse agentX open response PDU error!\n"); 165 | return -1; 166 | } 167 | 168 | return 0; 169 | } 170 | 171 | static int 172 | agentx_close(void) 173 | { 174 | struct x_pdu_buf x_pdu; 175 | 176 | /* Send agentX close PDU */ 177 | x_pdu = agentx_close_pdu(&agentx_datagram, R_SHUTDOWN); 178 | if (send(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0) == -1) { 179 | SMARTSNMP_LOG(L_ERROR, "Send agentX close PDU failure!\n"); 180 | return -1; 181 | } 182 | 183 | /* Receive agentX close response PDU */ 184 | x_pdu.len = TRANS_BUF_SIZ; 185 | x_pdu.buf = xrealloc(x_pdu.buf, x_pdu.len); 186 | x_pdu.len = recv(agentx_datagram.sock, x_pdu.buf, x_pdu.len, 0); 187 | if (x_pdu.len == -1) { 188 | SMARTSNMP_LOG(L_ERROR, "Receive agentX close response PDU failure!\n"); 189 | return -1; 190 | } 191 | 192 | /* Verify close response PDU */ 193 | if (agentx_recv(x_pdu.buf, x_pdu.len) != AGENTX_ERR_OK) { 194 | SMARTSNMP_LOG(L_ERROR, "Parse agentX close response PDU error!\n"); 195 | return -1; 196 | } 197 | 198 | agentx_trans_ops.stop(); 199 | return 0; 200 | } 201 | 202 | static void 203 | agentx_run(void) 204 | { 205 | return agentx_trans_ops.running(); 206 | } 207 | 208 | struct protocol_operation agentx_prot_ops = { 209 | "agentx", 210 | agentx_init, 211 | agentx_open, 212 | agentx_close, 213 | agentx_run, 214 | agentx_mib_node_reg, 215 | agentx_mib_node_unreg, 216 | agentx_receive, 217 | agentx_send, 218 | }; 219 | -------------------------------------------------------------------------------- /core/agentx.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartAGENTX 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #ifndef _AGENTX_H_ 22 | #define _AGENTX_H_ 23 | 24 | #include "asn1.h" 25 | #include "list.h" 26 | 27 | /* AgentX PDU flags */ 28 | #define INSTANCE_REGISTRATION 0x1 29 | #define NEW_INDEX 0x2 30 | #define ANY_INDEX 0x4 31 | #define NON_DEFAULT_CONTEXT 0x8 32 | #define NETWORD_BYTE_ORDER 0x10 33 | 34 | /* AgentX PDU tags */ 35 | typedef enum agentx_pdu_type { 36 | AGENTX_PDU_OPEN = 1, 37 | AGENTX_PDU_CLOSE, 38 | AGENTX_PDU_REG, 39 | AGENTX_PDU_UNREG, 40 | AGENTX_PDU_GET, 41 | AGENTX_PDU_GETNEXT, 42 | AGENTX_PDU_GETBULK, 43 | AGENTX_PDU_TESTSET, 44 | AGENTX_PDU_COMMITSET, 45 | AGENTX_PDU_UNDOSET, 46 | AGENTX_PDU_CLEANUPSET, 47 | AGENTX_PDU_NOTIFY, 48 | AGENTX_PDU_PING, 49 | AGENTX_PDU_INDEXALLOC, 50 | AGENTX_PDU_INDEXDEALLOC, 51 | AGENTX_PDU_ADDAGENTCAP, 52 | AGENTX_PDU_REMOVEAGENTCAP, 53 | AGENTX_PDU_RESPONSE, 54 | } AGENTX_PDU_TYPE_E; 55 | 56 | /* AgentX error code */ 57 | typedef enum agentx_err_code { 58 | AGENTX_ERR_OK = 0, 59 | 60 | AGENTX_ERR_PDU_CTX_LEN = -100, 61 | 62 | AGENTX_ERR_VB_VAR = -200, 63 | AGENTX_ERR_VB_VALUE_LEN = -201, 64 | AGENTX_ERR_VB_OID_LEN = -202, 65 | 66 | AGENTX_ERR_SR_VAR = -300, 67 | AGENTX_ERR_SR_OID_LEN = -301, 68 | } AGENTX_ERR_CODE_E; 69 | 70 | /* AgentX error status */ 71 | typedef enum agentx_err_stat { 72 | /* v1 */ 73 | AGENTX_ERR_STAT_NO_ERR, 74 | AGENTX_ERR_STAT_TOO_BIG = 1, 75 | AGENTX_ERR_STAT_NO_SUCH_NAME, 76 | AGENTX_ERR_STAT_BAD_VALUE, 77 | AGENTX_ERR_STAT_READ_ONLY, 78 | AGENTX_ERR_STAT_GEN_ERR, 79 | 80 | /* v2c */ 81 | AGENTX_ERR_STAT_NO_ACCESS, 82 | AGENTX_ERR_STAT_WRONG_TYPE, 83 | AGENTX_ERR_STAT_WRONG_LEN, 84 | AGENTX_ERR_STAT_ENCODING, 85 | AGENTX_ERR_STAT_WRONG_VALUE, 86 | AGENTX_ERR_STAT_NO_CREATION, 87 | AGENTX_ERR_STAT_INCONSISTENT_VALUE, 88 | AGENTX_ERR_STAT_RESOURCE_UNAVAIL, 89 | AGENTX_ERR_STAT_COMMIT_FAILED, 90 | AGENTX_ERR_STAT_UNDO_FAILED, 91 | AGENTX_ERR_STAT_AUTHORIZATION, 92 | AGENTX_ERR_STAT_NOT_WRITABLE, 93 | AGENTX_ERR_STAT_INCONSISTENT_NAME, 94 | } AGENTX_ERR_STAT_E; 95 | 96 | /* AgentX close PDU reason */ 97 | typedef enum agentx_close_reason { 98 | R_OTHER = 1, 99 | R_PARSE_ERROR, 100 | R_PROTOCOL_ERROR, 101 | R_TIMEOUTS, 102 | R_SHUTDOWN, 103 | R_MANAGE, 104 | } AGENTX_CLOSE_REASON_E; 105 | 106 | /* AgentX administrative error in response */ 107 | typedef enum agentx_err_response { 108 | E_OPEN_FAILED = 0x100, 109 | E_NOT_OPEN, 110 | E_INDEX_WRONG_TYPE, 111 | E_INDEX_ALREADY_ALLOCATED, 112 | E_INDEX_NONE_AVAILABLE, 113 | E_INDEX_NOT_ALLOCATED, 114 | E_UNSUPPORTED_CONTEXT, 115 | E_DUPLICATE_REGISTRATION, 116 | E_UNKNOWN_REGISTRATION, 117 | E_UNKNOWN_AGENT_CAPS, 118 | E_PARSE_ERROR, 119 | E_REQUEST_DENIED, 120 | E_PROCESSING_ERROR, 121 | } AGENTX_ERR_RESPONSE_E; 122 | 123 | struct x_pdu_hdr { 124 | uint8_t version; 125 | uint8_t type; 126 | uint8_t flags; 127 | uint8_t reserved; 128 | uint32_t session_id; 129 | uint32_t transaction_id; 130 | uint32_t packet_id; 131 | uint32_t payload_length; 132 | }; 133 | 134 | struct x_objid_t { 135 | uint8_t n_subid; 136 | uint8_t prefix; 137 | uint8_t include; 138 | uint8_t reserved; 139 | uint32_t sub_id[0]; 140 | }; 141 | 142 | struct x_octstr_t { 143 | uint32_t len; 144 | uint8_t str[0]; 145 | }; 146 | 147 | struct x_pdu_buf { 148 | uint8_t *buf; 149 | uint32_t len; 150 | }; 151 | 152 | struct x_search_range { 153 | struct list_head link; 154 | oid_t *start; 155 | oid_t *end; 156 | uint32_t start_len; 157 | uint32_t end_len; 158 | uint8_t start_include; 159 | uint8_t end_include; 160 | }; 161 | 162 | struct x_var_bind { 163 | struct list_head link; 164 | oid_t *oid; 165 | uint32_t oid_len; 166 | uint16_t val_type; 167 | /* Number of elements as vb_in, 168 | * number of bytes as vb_out. */ 169 | uint32_t val_len; 170 | uint8_t value[0]; 171 | }; 172 | 173 | struct agentx_datagram { 174 | int sock; 175 | 176 | void *recv_buf; 177 | void *send_buf; 178 | 179 | /* context (community) */ 180 | octstr_t context[41]; 181 | uint32_t ctx_len; 182 | 183 | struct x_pdu_hdr pdu_hdr; 184 | 185 | union { 186 | struct { 187 | uint8_t reason; 188 | } close; 189 | struct { 190 | uint16_t non_rep; 191 | uint16_t max_rep; 192 | } getbulk; 193 | struct { 194 | uint32_t sys_up_time; 195 | uint16_t error; 196 | uint16_t index; 197 | } response; 198 | } u; 199 | 200 | uint32_t vb_in_cnt; 201 | uint32_t vb_out_cnt; 202 | struct list_head vb_in_list; 203 | struct list_head vb_out_list; 204 | 205 | uint32_t sr_in_cnt; 206 | uint32_t sr_out_cnt; 207 | struct list_head sr_in_list; 208 | struct list_head sr_out_list; 209 | }; 210 | 211 | extern struct agentx_datagram agentx_datagram; 212 | 213 | uint32_t agentx_value_dec(uint8_t **buffer, uint8_t flag, uint8_t type, void *value); 214 | uint32_t agentx_value_dec_try(const uint8_t *buf, uint8_t flag, uint8_t type); 215 | uint32_t agentx_value_enc(const void *value, uint32_t len, uint8_t type, uint8_t *buf); 216 | uint32_t agentx_value_enc_try(uint32_t len, uint8_t type); 217 | 218 | int agentx_recv(uint8_t *buf, int len); 219 | void agentx_response(struct agentx_datagram *xdg); 220 | void agentx_get(struct agentx_datagram *xdg); 221 | void agentx_getnext(struct agentx_datagram *xdg); 222 | void agentx_set(struct agentx_datagram *xdg); 223 | 224 | struct x_pdu_buf agentx_open_pdu(struct agentx_datagram *xdg, const oid_t *oid, uint32_t oid_len, const char *descr, uint32_t descr_len); 225 | struct x_pdu_buf agentx_close_pdu(struct agentx_datagram *xdg, uint32_t reason); 226 | struct x_pdu_buf agentx_register_pdu(struct agentx_datagram *xdg, const oid_t *oid, uint32_t oid_len, const char *community, uint32_t comm_len, 227 | uint8_t timeout, uint8_t priority, uint8_t range_subid, uint32_t upper_bound); 228 | struct x_pdu_buf agentx_unregister_pdu(struct agentx_datagram *xdg, const oid_t *oid, uint32_t oid_len, const char *community, uint32_t comm_len, 229 | uint8_t timeout, uint8_t priority, uint8_t range_subid, uint32_t upper_bound); 230 | struct x_pdu_buf agentx_ping_pdu(struct agentx_datagram *xdg, const char *context, uint32_t context_len); 231 | struct x_pdu_buf agentx_response_pdu(struct agentx_datagram *xdg); 232 | 233 | #endif /* _AGENTX_H_ */ 234 | -------------------------------------------------------------------------------- /core/smartsnmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include "mib.h" 26 | #include "snmp.h" 27 | #include "agentx.h" 28 | #include "protocol.h" 29 | #include "util.h" 30 | 31 | static struct protocol_operation *prot_ops; 32 | 33 | void sig_int_handler(int dummy) 34 | { 35 | prot_ops->close(); 36 | } 37 | 38 | int 39 | smartsnmp_init(lua_State *L) 40 | { 41 | int ret; 42 | const char *protocol = luaL_checkstring(L, 1); 43 | int port = luaL_checkint(L, 2); 44 | 45 | signal(SIGINT, sig_int_handler); 46 | 47 | mib_init(); 48 | 49 | if (!strcmp(protocol, "snmp")) { 50 | prot_ops = &snmp_prot_ops; 51 | } else if (!strcmp(protocol, "agentx")) { 52 | prot_ops = &agentx_prot_ops; 53 | } else { 54 | lua_pushboolean(L, 0); 55 | return 1; 56 | } 57 | 58 | ret = prot_ops->init(port); 59 | 60 | if (ret < 0) { 61 | lua_pushboolean(L, 0); 62 | } else { 63 | lua_pushboolean(L, 1); 64 | } 65 | return 1; 66 | } 67 | 68 | int 69 | smartsnmp_open(lua_State *L) 70 | { 71 | int ret = prot_ops->open(); 72 | if (ret < 0) { 73 | lua_pushboolean(L, 0); 74 | } else { 75 | lua_pushboolean(L, 1); 76 | } 77 | 78 | return 1; 79 | } 80 | 81 | int 82 | smartsnmp_run(lua_State *L) 83 | { 84 | prot_ops->run(); 85 | return 0; 86 | } 87 | 88 | int 89 | smartsnmp_exit(lua_State *L) 90 | { 91 | prot_ops->close(); 92 | return 0; 93 | } 94 | 95 | /* Register mib nodes from Lua */ 96 | int 97 | smartsnmp_mib_node_reg(lua_State *L) 98 | { 99 | oid_t *grp_id; 100 | int i, grp_id_len, grp_cb; 101 | 102 | /* Check if the first argument is a table. */ 103 | luaL_checktype(L, 1, LUA_TTABLE); 104 | /* Get oid length */ 105 | grp_id_len = lua_objlen(L, 1); 106 | /* Get oid */ 107 | grp_id = xmalloc(grp_id_len * sizeof(oid_t)); 108 | for (i = 0; i < grp_id_len; i++) { 109 | lua_rawgeti(L, 1, i + 1); 110 | grp_id[i] = lua_tointeger(L, -1); 111 | lua_pop(L, 1); 112 | } 113 | /* Get lua callback of grpance node */ 114 | if (!lua_isfunction(L, -1)) { 115 | lua_pushstring(L, "Handler is not a function!"); 116 | lua_error(L); 117 | } 118 | grp_cb = luaL_ref(L, LUA_ENVIRONINDEX); 119 | 120 | /* Register node */ 121 | i = prot_ops->reg(grp_id, grp_id_len, grp_cb); 122 | free(grp_id); 123 | 124 | /* Return value */ 125 | lua_pushnumber(L, i); 126 | return 1; 127 | } 128 | 129 | /* Unregister mib nodes from Lua */ 130 | int 131 | smartsnmp_mib_node_unreg(lua_State *L) 132 | { 133 | oid_t *grp_id; 134 | int i, grp_id_len; 135 | 136 | /* Check if the first argument is a table. */ 137 | luaL_checktype(L, 1, LUA_TTABLE); 138 | /* Get oid length */ 139 | grp_id_len = lua_objlen(L, 1); 140 | /* Get oid */ 141 | grp_id = xmalloc(grp_id_len * sizeof(oid_t)); 142 | for (i = 0; i < grp_id_len; i++) { 143 | lua_rawgeti(L, 1, i + 1); 144 | grp_id[i] = lua_tointeger(L, -1); 145 | lua_pop(L, 1); 146 | } 147 | 148 | /* Unregister group node */ 149 | i = prot_ops->unreg(grp_id, grp_id_len); 150 | free(grp_id); 151 | 152 | /* Return value */ 153 | lua_pushnumber(L, i); 154 | return 1; 155 | } 156 | 157 | /* Register community string from Lua */ 158 | int 159 | smartsnmp_mib_community_reg(lua_State *L) 160 | { 161 | oid_t *oid; 162 | int i, id_len, attribute; 163 | const char *community; 164 | 165 | /* Check if the first argument is a table. */ 166 | luaL_checktype(L, 1, LUA_TTABLE); 167 | /* Get oid length */ 168 | id_len = lua_objlen(L, 1); 169 | /* Get oid */ 170 | oid = xmalloc(id_len * sizeof(oid_t)); 171 | for (i = 0; i < id_len; i++) { 172 | lua_rawgeti(L, 1, i + 1); 173 | oid[i] = lua_tointeger(L, -1); 174 | lua_pop(L, 1); 175 | } 176 | 177 | /* Community string and RW attribute */ 178 | community = luaL_checkstring(L, 2); 179 | attribute = luaL_checkint(L, 3); 180 | 181 | /* Register community string */ 182 | mib_community_reg(oid, id_len, community, attribute); 183 | free(oid); 184 | 185 | return 0; 186 | } 187 | 188 | /* Unregister mib community string from Lua */ 189 | int 190 | smartsnmp_mib_community_unreg(lua_State *L) 191 | { 192 | /* Unregister community string */ 193 | const char *community = luaL_checkstring(L, 1); 194 | int attribute = luaL_checkint(L, 2); 195 | mib_community_unreg(community, attribute); 196 | 197 | return 0; 198 | } 199 | 200 | /* Register mib user from Lua */ 201 | int 202 | smartsnmp_mib_user_reg(lua_State *L) 203 | { 204 | oid_t *oid; 205 | int i, id_len, attribute; 206 | const char *user; 207 | 208 | /* Check if the first argument is a table. */ 209 | luaL_checktype(L, 1, LUA_TTABLE); 210 | /* Get oid length */ 211 | id_len = lua_objlen(L, 1); 212 | /* Get oid */ 213 | oid = xmalloc(id_len * sizeof(oid_t)); 214 | for (i = 0; i < id_len; i++) { 215 | lua_rawgeti(L, 1, i + 1); 216 | oid[i] = lua_tointeger(L, -1); 217 | lua_pop(L, 1); 218 | } 219 | 220 | /* User string and RW attribute */ 221 | user = luaL_checkstring(L, 2); 222 | attribute = luaL_checkint(L, 3); 223 | 224 | /* Register user string */ 225 | mib_user_reg(oid, id_len, user, attribute); 226 | free(oid); 227 | 228 | return 0; 229 | } 230 | 231 | /* Unregister mib user string from Lua */ 232 | int 233 | smartsnmp_mib_user_unreg(lua_State *L) 234 | { 235 | /* Unregister user string */ 236 | const char *user = luaL_checkstring(L, 1); 237 | int attribute = luaL_checkint(L, 2); 238 | mib_user_unreg(user, attribute); 239 | 240 | return 0; 241 | } 242 | 243 | static const luaL_Reg smartsnmp_func[] = { 244 | { "init", smartsnmp_init }, 245 | { "open", smartsnmp_open }, 246 | { "run", smartsnmp_run }, 247 | { "exit", smartsnmp_exit }, 248 | { "mib_node_reg", smartsnmp_mib_node_reg }, 249 | { "mib_node_unreg", smartsnmp_mib_node_unreg }, 250 | { "mib_community_reg", smartsnmp_mib_community_reg }, 251 | { "mib_community_unreg", smartsnmp_mib_community_unreg }, 252 | { "mib_user_reg", smartsnmp_mib_user_reg }, 253 | { "mib_user_unreg", smartsnmp_mib_user_unreg }, 254 | { NULL, NULL } 255 | }; 256 | 257 | /* Init smartsnmp agent from lua */ 258 | int 259 | luaopen_smartsnmp_core(lua_State *L) 260 | { 261 | /* Store lua environment */ 262 | mib_lua_state = L; 263 | lua_newtable(L); 264 | lua_replace(L, LUA_ENVIRONINDEX); 265 | 266 | /* Register smartsnmp_func into lua */ 267 | luaL_register(L, "smartsnmp_lib", smartsnmp_func); 268 | 269 | return 1; 270 | } 271 | -------------------------------------------------------------------------------- /SConstruct: -------------------------------------------------------------------------------- 1 | # This file is part of SmartSNMP 2 | # Copyright (C) 2014, Credo Semiconductor Inc. 3 | # 4 | # This program is free software; you can redistribute it and/or modify 5 | # it under the terms of the GNU General Public License as published by 6 | # the Free Software Foundation; either version 2 of the License, or 7 | # (at your option) any later version. 8 | # 9 | # This program is distributed in the hope that it will be useful, 10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | # GNU General Public License for more details. 13 | # 14 | # You should have received a copy of the GNU General Public License along 15 | # with this program; if not, write to the Free Software Foundation, Inc., 16 | # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | import os 19 | import sys 20 | 21 | sys.path.append(os.path.join(os.getcwd(), "scons_tools")) 22 | 23 | from SCons.SConf import SConfError 24 | from select_probe import * 25 | from endian_probe import * 26 | from kqueue_probe import * 27 | from epoll_probe import * 28 | 29 | # options 30 | AddOption( 31 | '--transport', 32 | dest='transport', 33 | default = 'built-in', 34 | type='string', 35 | nargs=1, 36 | action='store', 37 | metavar='[built-in|libevent|uloop]', 38 | help='transport you want to use' 39 | ) 40 | 41 | AddOption( 42 | '--evloop', 43 | dest='evloop', 44 | default = 'select', 45 | type='string', 46 | nargs=1, 47 | action='store', 48 | metavar='[select|epoll|kqueue]', 49 | help='transport you want to use' 50 | ) 51 | 52 | AddOption( 53 | '--with-cflags', 54 | dest='cflags', 55 | default = '', 56 | type='string', 57 | nargs=1, 58 | action='store', 59 | metavar='CFLAGS', 60 | help='use CFLAGS as compile time arguments (will ignore CFLAGS env)' 61 | ) 62 | 63 | AddOption( 64 | '--with-ldflags', 65 | dest='ldflags', 66 | default = '', 67 | type='string', 68 | nargs=1, 69 | action='store', 70 | metavar='LDFLAGS', 71 | help='use LDFLAGS as link time arguments to ld (will ignore LDFLAGS env)' 72 | ) 73 | 74 | AddOption( 75 | '--with-libs', 76 | dest='libs', 77 | default = '', 78 | type='string', 79 | nargs=1, 80 | action='store', 81 | metavar='LIBS', 82 | help='use LIBS as link time arguments to ld' 83 | ) 84 | 85 | AddOption( 86 | '--with-liblua', 87 | dest='liblua_dir', 88 | default = '', 89 | type='string', 90 | nargs=1, 91 | action='store', 92 | metavar='DIR', 93 | help='use liblua in DIR' 94 | ) 95 | 96 | AddOption( 97 | '--with-libubox', 98 | dest='libubox_dir', 99 | default = '', 100 | type='string', 101 | nargs=1, 102 | action='store', 103 | metavar='DIR', 104 | help='use libubox in DIR (only for transport is uloop)' 105 | ) 106 | 107 | AddOption( 108 | '--with-libevent', 109 | dest='libevent_dir', 110 | default = '', 111 | type='string', 112 | nargs=1, 113 | action='store', 114 | metavar='DIR', 115 | help='use libevent in DIR (only for transport is libevent)' 116 | ) 117 | 118 | AddOption( 119 | '--gcov', 120 | dest='gcov', 121 | default = '', 122 | type='string', 123 | nargs=1, 124 | action='store', 125 | metavar='[yes|no]', 126 | help='compile C source code with gcov support' 127 | ) 128 | 129 | env = Environment( 130 | ENV = os.environ, 131 | LIBS = ['m', 'dl'], 132 | CFLAGS = ['-std=c99', '-Wall'], 133 | ) 134 | 135 | # handle options/environment varibles. 136 | if os.environ.has_key('CC'): 137 | env.Replace(CC = os.environ['CC']) 138 | 139 | # CFLAGS 140 | if GetOption("cflags") != "": 141 | env.Append(CFLAGS = GetOption("cflags")) 142 | elif os.environ.has_key('CFLAGS'): 143 | env.Append(CFLAGS = os.environ['CFLAGS']) 144 | 145 | # LDFLAGS 146 | if GetOption("ldflags") != "": 147 | env.Replace(LINKFLAGS = GetOption("ldflags")) 148 | elif os.environ.has_key('LDFLAGS'): 149 | env.Replace(LINKFLAGS = os.environ['LDFLAGS']) 150 | elif os.environ.has_key('LINKFLAGS'): 151 | env.Replace(LINKFLAGS = os.environ['LINKFLAGS']) 152 | 153 | # LIBS 154 | if GetOption("libs") != "": 155 | env.Append(LIBS = GetOption("libs")) 156 | 157 | # liblua 158 | if GetOption("liblua_dir") != "": 159 | env.Append(LIBPATH = [GetOption("liblua_dir")]) 160 | 161 | # libevent 162 | if GetOption("libevent_dir") != "": 163 | env.Append(LIBPATH = [GetOption("libevent_dir")]) 164 | 165 | # libubox 166 | if GetOption("libubox_dir") != "": 167 | env.Append(LIBPATH = [GetOption("libubox_dir")]) 168 | 169 | # transport select 170 | if GetOption("transport") == 'libevent': 171 | env.Append(LIBS = ['event']) 172 | transport_src = [env.File("core/agentx_tcp_evloop_transport.c"), env.File("core/snmp_udp_libevent_transport.c"), env.File("core/ev_loop.c")] 173 | elif GetOption("transport") == 'uloop': 174 | env.Append(LIBS = ['ubox']) 175 | transport_src = [env.File("core/agentx_tcp_evloop_transport.c"), env.File("core/snmp_udp_uloop_transport.c"), env.File("core/ev_loop.c")] 176 | elif GetOption("transport") == 'built-in' or GetOption("transport") == '': 177 | transport_src = [env.File("core/agentx_tcp_evloop_transport.c"), env.File("core/snmp_udp_evloop_transport.c"), env.File("core/ev_loop.c")] 178 | # built-in event loop check 179 | if GetOption("evloop") == 'epoll': 180 | env.Append(CFLAGS = ["-DUSE_EPOLL"]) 181 | elif GetOption("evloop") == 'kqueue': 182 | env.Append(CFLAGS = ["-DUSE_KQUEUE"]) 183 | elif GetOption("evloop") == 'select' or GetOption("evloop") == '': 184 | pass 185 | else: 186 | print "Error: Not the right event driving type" 187 | Exit(1) 188 | else: 189 | print "Error: Transport not found!" 190 | Exit(1) 191 | 192 | # autoconf 193 | conf = Configure(env, custom_tests = {'CheckEpoll' : CheckEpoll, 'CheckSelect' : CheckSelect, 'CheckKqueue' : CheckKqueue, 'CheckEndian' : CheckEndian}) 194 | 195 | # Endian check 196 | endian = conf.CheckEndian() 197 | if endian == 'Big': 198 | env.Append(CFLAGS = ["-DBIG_ENDIAN"]) 199 | elif endian == 'Little': 200 | env.Append(CFLAGS = ["-DLITTLE_ENDIAN"]) 201 | else: 202 | raise SConfError("Error when testing the endian.") 203 | 204 | # built-in event loop check 205 | if GetOption("transport") == 'built-in' or GetOption("transport") == '': 206 | if GetOption("evloop") == 'epoll': 207 | if not conf.CheckEpoll(): 208 | print "Error: epoll failed" 209 | Exit(1) 210 | elif GetOption("evloop") == 'kqueue': 211 | if not conf.CheckKqueue(): 212 | print "Error: Kqueue failed" 213 | Exit(1) 214 | elif GetOption("evloop") == 'select' or GetOption("evloop") == '': 215 | if not conf.CheckSelect(): 216 | print "Error: select failed" 217 | Exit(1) 218 | else: 219 | print "Error: Not the right event driving type" 220 | Exit(1) 221 | 222 | # CFLAGS 223 | if GetOption("gcov") == "yes": 224 | env.Append( 225 | CFLAGS = ['-fprofile-arcs', '-ftest-coverage'], 226 | LINKFLAGS = ['-fprofile-arcs', '-ftest-coverage'], 227 | ) 228 | 229 | # find liblua. On Ubuntu, liblua is named liblua5.1, so we need to check this. 230 | if conf.CheckLib('lua'): 231 | env.Append(LIBS = ['lua']) 232 | elif conf.CheckLib('lua5.1'): 233 | env.Append(LIBS = ['lua5.1']) 234 | else: 235 | print "Error: liblua or liblua5.1 not found!" 236 | Exit(1) 237 | 238 | # find lua header files 239 | if conf.CheckCHeader('lua.h'): 240 | pass 241 | elif conf.CheckCHeader('lua5.1/lua.h'): 242 | env.Append(CFLAGS = ['-I/usr/include/lua5.1']) 243 | else: 244 | print "Error: lua.h not found" 245 | Exit(1) 246 | 247 | env = conf.Finish() 248 | 249 | src = env.Glob("core/snmp_msg*.c") + env.Glob("core/snmp_*coder.c") + env.Glob("core/snmp.c") + env.Glob("core/agentx_msg*.c") + env.Glob("core/agentx_*coder.c") + env.Glob("core/agentx.c") + env.Glob("core/mib_*.c") + env.Glob("core/smartsnmp.c") + transport_src 250 | 251 | # generate lua c module 252 | libsmartsnmp_core = env.SharedLibrary('build/smartsnmp/core', src, SHLIBPREFIX = '') 253 | -------------------------------------------------------------------------------- /core/agentx_msg_proc.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of Smartagentx 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "mib.h" 26 | #include "agentx.h" 27 | #include "util.h" 28 | 29 | static oid_t agentx_dummy_view[] = { 1, 3, 6, 1 }; 30 | 31 | static void 32 | mib_get(struct agentx_datagram *xdg, struct x_search_range *sr_in, struct oid_search_res *ret_oid) 33 | { 34 | struct mib_view view; 35 | 36 | view.oid = agentx_dummy_view; 37 | view.id_len = elem_num(agentx_dummy_view); 38 | 39 | mib_tree_search(&view, sr_in->start, sr_in->start_len, ret_oid); 40 | } 41 | 42 | void 43 | agentx_get(struct agentx_datagram *xdg) 44 | { 45 | uint32_t val_len, sr_in_cnt = 0; 46 | struct list_head *curr, *next; 47 | struct x_var_bind *vb_out; 48 | struct x_search_range *sr_in; 49 | struct oid_search_res ret_oid; 50 | 51 | memset(&ret_oid, 0, sizeof(ret_oid)); 52 | ret_oid.request = MIB_REQ_GET; 53 | 54 | list_for_each_safe(curr, next, &xdg->sr_in_list) { 55 | sr_in = list_entry(curr, struct x_search_range, link); 56 | sr_in_cnt++; 57 | 58 | /* Search at the input oid */ 59 | mib_get(xdg, sr_in, &ret_oid); 60 | 61 | val_len = agentx_value_enc_try(length(&ret_oid.var), tag(&ret_oid.var)); 62 | vb_out = xmalloc(sizeof(*vb_out) + val_len); 63 | vb_out->oid = ret_oid.oid; 64 | vb_out->oid_len = ret_oid.id_len; 65 | vb_out->val_type = tag(&ret_oid.var); 66 | vb_out->val_len = agentx_value_enc(value(&ret_oid.var), length(&ret_oid.var), tag(&ret_oid.var), vb_out->value); 67 | 68 | /* Error status */ 69 | if (ret_oid.err_stat) { 70 | if (!xdg->u.response.error) { 71 | /* Report the first object error status in search range */ 72 | xdg->u.response.error = ret_oid.err_stat; 73 | xdg->u.response.index = sr_in_cnt; 74 | } 75 | } 76 | 77 | /* Add into list. */ 78 | list_add_tail(&vb_out->link, &xdg->vb_out_list); 79 | xdg->vb_out_cnt++; 80 | } 81 | 82 | agentx_response(xdg); 83 | } 84 | 85 | static void 86 | mib_getnext(struct agentx_datagram *xdg, struct x_search_range *sr_in, struct oid_search_res *ret_oid) 87 | { 88 | struct mib_view view; 89 | 90 | view.oid = agentx_dummy_view; 91 | view.id_len = elem_num(agentx_dummy_view); 92 | 93 | /* Search at the included start oid */ 94 | if (sr_in->start_include) { 95 | mib_tree_search(&view, sr_in->start, sr_in->start_len, ret_oid); 96 | if (ret_oid->err_stat || !MIB_TAG_VALID(tag(&ret_oid->var))) { 97 | /* Invalid query */ 98 | free(ret_oid->oid); 99 | } 100 | } 101 | 102 | /* If start oid not included or not exist, search the next one */ 103 | if (!sr_in->start_include || ret_oid->err_stat || !MIB_TAG_VALID(tag(&ret_oid->var))) { 104 | mib_tree_search_next(&view, sr_in->start, sr_in->start_len, ret_oid); 105 | if (!ret_oid->err_stat && MIB_TAG_VALID(tag(&ret_oid->var))) { 106 | /* Check whether return oid exceeds end oid */ 107 | if ((sr_in->end_include && oid_cmp(ret_oid->oid, ret_oid->id_len, sr_in->end, sr_in->end_len) > 0) || 108 | (!sr_in->end_include && oid_cmp(ret_oid->oid, ret_oid->id_len, sr_in->end, sr_in->end_len) >= 0)) { 109 | /* Oid exceeds, end_of_mib_view */ 110 | oid_cpy(ret_oid->oid, sr_in->start, sr_in->start_len); 111 | ret_oid->id_len = sr_in->start_len; 112 | ret_oid->inst_id = NULL; 113 | ret_oid->inst_id_len = 0; 114 | tag(&ret_oid->var) = ASN1_TAG_END_OF_MIB_VIEW; 115 | } 116 | } 117 | } 118 | } 119 | 120 | void 121 | agentx_getnext(struct agentx_datagram *xdg) 122 | { 123 | uint32_t val_len, sr_in_cnt = 0; 124 | struct list_head *curr, *next; 125 | struct x_var_bind *vb_out; 126 | struct x_search_range *sr_in; 127 | struct oid_search_res ret_oid; 128 | 129 | memset(&ret_oid, 0, sizeof(ret_oid)); 130 | ret_oid.request = MIB_REQ_GETNEXT; 131 | 132 | list_for_each_safe(curr, next, &xdg->sr_in_list) { 133 | sr_in = list_entry(curr, struct x_search_range, link); 134 | sr_in_cnt++; 135 | 136 | /* Search at the input next oid */ 137 | mib_getnext(xdg, sr_in, &ret_oid); 138 | 139 | val_len = agentx_value_enc_try(length(&ret_oid.var), tag(&ret_oid.var)); 140 | vb_out = xmalloc(sizeof(*vb_out) + val_len); 141 | vb_out->oid = ret_oid.oid; 142 | vb_out->oid_len = ret_oid.id_len; 143 | vb_out->val_type = tag(&ret_oid.var); 144 | vb_out->val_len = agentx_value_enc(value(&ret_oid.var), length(&ret_oid.var), tag(&ret_oid.var), vb_out->value); 145 | 146 | /* Error status */ 147 | if (ret_oid.err_stat) { 148 | if (!xdg->u.response.error) { 149 | /* Report the first object error status in search range */ 150 | xdg->u.response.error = ret_oid.err_stat; 151 | xdg->u.response.index = sr_in_cnt; 152 | } 153 | } 154 | 155 | /* Add into list. */ 156 | list_add_tail(&vb_out->link, &xdg->vb_out_list); 157 | xdg->vb_out_cnt++; 158 | } 159 | 160 | agentx_response(xdg); 161 | } 162 | 163 | static void 164 | mib_set(struct agentx_datagram *xdg, struct x_var_bind *vb_in, struct oid_search_res *ret_oid) 165 | { 166 | struct mib_view view; 167 | 168 | view.oid = agentx_dummy_view; 169 | view.id_len = elem_num(agentx_dummy_view); 170 | 171 | mib_tree_search(&view, vb_in->oid, vb_in->oid_len, ret_oid); 172 | } 173 | 174 | void 175 | agentx_set(struct agentx_datagram *xdg) 176 | { 177 | uint32_t val_len, vb_in_cnt = 0; 178 | struct list_head *curr, *next; 179 | struct x_var_bind *vb_in, *vb_out; 180 | struct oid_search_res ret_oid; 181 | 182 | memset(&ret_oid, 0, sizeof(ret_oid)); 183 | ret_oid.request = MIB_REQ_SET; 184 | 185 | list_for_each_safe(curr, next, &xdg->vb_in_list) { 186 | vb_in = list_entry(curr, struct x_var_bind, link); 187 | vb_in_cnt++; 188 | 189 | /* Decode the setting value ahead */ 190 | tag(&ret_oid.var) = vb_in->val_type; 191 | length(&ret_oid.var) = vb_in->val_len; 192 | val_len = agentx_value_enc_try(length(&ret_oid.var), tag(&ret_oid.var)); 193 | memcpy(value(&ret_oid.var), vb_in->value, val_len); 194 | 195 | /* Search at the input oid and set it */ 196 | mib_set(xdg, vb_in, &ret_oid); 197 | 198 | val_len = agentx_value_enc_try(length(&ret_oid.var), tag(&ret_oid.var)); 199 | vb_out = xmalloc(sizeof(*vb_out) + val_len); 200 | vb_out->oid = ret_oid.oid; 201 | vb_out->oid_len = ret_oid.id_len; 202 | vb_out->val_type = vb_in->val_type; 203 | vb_out->val_len = agentx_value_enc(value(&ret_oid.var), val_len, tag(&ret_oid.var), vb_out->value); 204 | 205 | /* Invalid tags convert to error status for snmpset */ 206 | if (!ret_oid.err_stat && !MIB_TAG_VALID(tag(&ret_oid.var))) { 207 | ret_oid.err_stat = AGENTX_ERR_STAT_NOT_WRITABLE; 208 | } 209 | 210 | if (ret_oid.err_stat) { 211 | /* Something wrong */ 212 | if (!xdg->u.response.error) { 213 | /* Report the first object error status in search range */ 214 | xdg->u.response.error = ret_oid.err_stat; 215 | xdg->u.response.index = vb_in_cnt; 216 | } 217 | } 218 | 219 | /* Add into list. */ 220 | list_add_tail(&vb_out->link, &xdg->vb_out_list); 221 | xdg->vb_out_cnt++; 222 | } 223 | 224 | agentx_response(xdg); 225 | } 226 | -------------------------------------------------------------------------------- /mibs/tcp.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- This file is part of SmartSNMP 3 | -- Copyright (C) 2014, Credo Semiconductor Inc. 4 | -- 5 | -- This program is free software; you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation; either version 2 of the License, or 8 | -- (at your option) any later version. 9 | -- 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | -- 15 | -- You should have received a copy of the GNU General Public License along 16 | -- with this program; if not, write to the Free Software Foundation, Inc., 17 | -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | -- 19 | 20 | local mib = require "smartsnmp" 21 | 22 | local tcp_scalar_cache = {} 23 | local tcp_conn_entry_cache = {} 24 | --[[ 25 | ["0.0.0.0.22.0.0.0.0.0"] = { conn_stat = 1 }, 26 | ["10.2.12.229.33874.91.189.92.10.443"] = { conn_stat = 8 }, 27 | ["10.2.12.229.33875.91.189.92.23.443"] = { conn_stat = 8 }, 28 | ["10.2.12.229.37700.180.149.153.11.80"] = { conn_stat = 9 }, 29 | ["10.2.12.229.46149.180.149.134.54.80"] = { conn_stat = 5 }, 30 | ["10.2.12.229.53158.123.58.181.140.80"] = { conn_stat = 5 }, 31 | ["127.0.0.1.631.0.0.0.0.0"] = { conn_stat = 1 }, 32 | ]] 33 | 34 | local tcp_snmp_conn_stat_map = { 35 | [0] = 12, -- ERROR 36 | 5, -- ESTABLISHED 37 | 3, -- SYN_SENT 38 | 4, -- SYN_RECV 39 | 6, -- FIN_WAIT1 40 | 7, -- FIN_WAIT2 41 | 11, -- TIME_WAIT 42 | 1, -- CLOSED 43 | 8, -- CLOSE_WAIT 44 | 9, -- LAST_ACK 45 | 2, -- LISTEN 46 | 10 -- CLOSING 47 | } 48 | 49 | local hextab = { 50 | ['0'] = 0, 51 | ['1'] = 1, 52 | ['2'] = 2, 53 | ['3'] = 3, 54 | ['4'] = 4, 55 | ['5'] = 5, 56 | ['6'] = 6, 57 | ['7'] = 7, 58 | ['8'] = 8, 59 | ['9'] = 9, 60 | ['A'] = 10, 61 | ['a'] = 10, 62 | ['B'] = 11, 63 | ['b'] = 11, 64 | ['C'] = 12, 65 | ['c'] = 12, 66 | ['D'] = 13, 67 | ['d'] = 13, 68 | ['E'] = 14, 69 | ['e'] = 14, 70 | ['F'] = 15, 71 | ['f'] = 15, 72 | } 73 | 74 | local function ip_hex2num(hex) 75 | assert(type(hex) == 'string') 76 | local num = {} 77 | for i = #hex, 1, -2 do 78 | local msb = string.char(string.byte(hex, i - 1)) 79 | local lsb = string.char(string.byte(hex, i)) 80 | table.insert(num, hextab[msb] * 16 + hextab[lsb]) 81 | end 82 | return table.concat(num, ".") 83 | end 84 | 85 | local function hex2num(hex) 86 | assert(type(hex) == 'string') 87 | local num = 0 88 | for i = 1, #hex do 89 | num = num * 16 + hextab[string.char(string.byte(hex, i))] 90 | end 91 | return num 92 | end 93 | 94 | local function __load_config() 95 | tcp_scalar_cache = {} 96 | tcp_conn_entry_cache = {} 97 | for line in io.lines("/proc/net/snmp") do 98 | if string.match(line, "%w+") == 'Tcp' then 99 | for w in string.gmatch(line, "%d+") do 100 | table.insert(tcp_scalar_cache, tonumber(w)) 101 | end 102 | end 103 | end 104 | for line in io.lines("/proc/net/tcp") do 105 | local loc_addr = string.match(line, ".-:%s+(.-):") 106 | local loc_port = string.match(line, ".-:%s+.-:(.-)%s+") 107 | local rem_addr = string.match(line, ".-:.-:.-%s+(.-):") 108 | local rem_port = string.match(line, ".-:%s+.-:.-:(.-)%s+") 109 | local conn_stat = string.match(line, ".-:%s+.-:.-:.-%s+(.-)%s+") 110 | local key = {} 111 | if loc_addr ~= nil and loc_port ~= nil and rem_addr ~= nil and rem_port ~= nil and conn_stat ~= nil then 112 | table.insert(key, ip_hex2num(loc_addr)) 113 | table.insert(key, tostring(hex2num(loc_port))) 114 | table.insert(key, ip_hex2num(rem_addr)) 115 | table.insert(key, tostring(hex2num(rem_port))) 116 | conn_stat = hex2num(conn_stat) 117 | tcp_conn_entry_cache[table.concat(key, '.')] = {} 118 | tcp_conn_entry_cache[table.concat(key, '.')].conn_stat = tcp_snmp_conn_stat_map[conn_stat] 119 | end 120 | end 121 | end 122 | 123 | local last_load_time = os.time() 124 | 125 | local function need_to_reload() 126 | if os.difftime(os.time(), last_load_time) < 3 then 127 | return false 128 | else 129 | last_load_time = os.time() 130 | return true 131 | end 132 | end 133 | 134 | local function load_config() 135 | if need_to_reload() == true then 136 | __load_config() 137 | end 138 | end 139 | 140 | __load_config() 141 | 142 | mib.module_methods.or_table_reg("1.3.6.1.2.1.6", "The MIB module for managing TCP inplementations") 143 | 144 | local function tcp_conn_entry_get(sub_oid, name) 145 | assert(type(name) == 'string') 146 | local value 147 | if type(sub_oid) == 'table' then 148 | local key = table.concat(sub_oid, ".") 149 | if tcp_conn_entry_cache[key] then 150 | value = tcp_conn_entry_cache[key][name] 151 | end 152 | end 153 | return value 154 | end 155 | 156 | local function tcp_conn_entry_set(sub_oid, value, name) 157 | assert(type(name) == 'string') 158 | if type(sub_oid) == 'table' then 159 | local key = table.concat(sub_oid, ".") 160 | if tcp_conn_entry_cache[key] then 161 | tcp_conn_entry_cache[key][name] = value 162 | end 163 | end 164 | end 165 | 166 | local tcpGroup = { 167 | [1] = mib.ConstInt(function () load_config() return tcp_scalar_cache[1] end), 168 | [2] = mib.ConstInt(function () load_config() return tcp_scalar_cache[2] end), 169 | [3] = mib.ConstInt(function () load_config() return tcp_scalar_cache[3] end), 170 | [4] = mib.ConstInt(function () load_config() return tcp_scalar_cache[4] end), 171 | [5] = mib.ConstCount(function () load_config() return tcp_scalar_cache[5] end), 172 | [6] = mib.ConstCount(function () load_config() return tcp_scalar_cache[6] end), 173 | [7] = mib.ConstCount(function () load_config() return tcp_scalar_cache[7] end), 174 | [8] = mib.ConstCount(function () load_config() return tcp_scalar_cache[8] end), 175 | [9] = mib.ConstGauge(function () load_config() return tcp_scalar_cache[9] end), 176 | [10] = mib.ConstCount(function () load_config() return tcp_scalar_cache[10] end), 177 | [11] = mib.ConstCount(function () load_config() return tcp_scalar_cache[11] end), 178 | [12] = mib.ConstCount(function () load_config() return tcp_scalar_cache[12] end), 179 | [13] = { 180 | [1] = { 181 | indexes = tcp_conn_entry_cache, 182 | [1] = mib.Int(function (sub_oid) load_config() return tcp_conn_entry_get(sub_oid, 'conn_stat') end, 183 | function (sub_oid, value) load_config() return tcp_conn_entry_set(sub_oid, value, 'conn_stat') end), 184 | [2] = mib.ConstIpaddr(function (sub_oid) 185 | load_config() 186 | local ipaddr 187 | if type(sub_oid) == 'table' and tcp_conn_entry_cache[table.concat(sub_oid, ".")] then 188 | ipaddr = {} 189 | for i = 1, 4 do 190 | table.insert(ipaddr, sub_oid[i]) 191 | end 192 | end 193 | return ipaddr 194 | end), 195 | [3] = mib.ConstInt(function (sub_oid) 196 | load_config() 197 | if type(sub_oid) == 'table' and tcp_conn_entry_cache[table.concat(sub_oid, ".")] then 198 | return sub_oid[5] 199 | else 200 | return nil 201 | end 202 | end), 203 | [4] = mib.ConstIpaddr(function (sub_oid) 204 | load_config() 205 | local ipaddr 206 | if type(sub_oid) == 'table' and tcp_conn_entry_cache[table.concat(sub_oid, ".")] then 207 | ipaddr = {} 208 | for i = 6, 9 do 209 | table.insert(ipaddr, sub_oid[i]) 210 | end 211 | end 212 | return ipaddr 213 | end), 214 | [5] = mib.ConstInt(function (sub_oid) 215 | load_config() 216 | if type(sub_oid) == 'table' and tcp_conn_entry_cache[table.concat(sub_oid, ".")] then 217 | return sub_oid[10] 218 | else 219 | return nil 220 | end 221 | end), 222 | } 223 | }, 224 | [14] = mib.ConstCount(function () load_config() return tcp_scalar_cache[13] end), 225 | [15] = mib.ConstCount(function () load_config() return tcp_scalar_cache[14] end), 226 | } 227 | 228 | return tcpGroup 229 | -------------------------------------------------------------------------------- /core/snmp_encoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "asn1.h" 26 | 27 | /* Input: integer value 28 | * Output: none 29 | * Return: byte length. 30 | */ 31 | static uint32_t 32 | ber_int_enc_try(int value) 33 | { 34 | uint32_t i, len; 35 | union anonymous { 36 | uint8_t buf[sizeof(int)]; 37 | int tmp; 38 | } a; 39 | 40 | a.tmp = value; 41 | len = 0; 42 | 43 | #ifdef LITTLE_ENDIAN 44 | i = sizeof(int) - 1; 45 | 46 | if (value >= 0) { 47 | while (i > 0 && !a.buf[i]) { 48 | i--; 49 | } 50 | if (a.buf[i] & 0x80) { 51 | len += 1; 52 | } 53 | } else { 54 | while (i > 0 && a.buf[i] == 0xff) { 55 | i--; 56 | } 57 | if (!(a.buf[i] & 0x80)) { 58 | len += 1; 59 | } 60 | } 61 | 62 | len += i + 1; 63 | #else 64 | i = 0; 65 | 66 | if (value >= 0) { 67 | while (i < sizeof(int) && !a.buf[i]) { 68 | i++; 69 | } 70 | if (a.buf[i] & 0x80) { 71 | len += 1; 72 | } 73 | } else { 74 | while (i < sizeof(int) && a.buf[i] == 0xff) { 75 | i++; 76 | } 77 | if (!(a.buf[i] & 0x80)) { 78 | len += 1; 79 | } 80 | } 81 | 82 | len += sizeof(int) - i; 83 | #endif 84 | 85 | return len; 86 | } 87 | 88 | /* Input: unsigned integer value 89 | * Output: none 90 | * Return: byte length. 91 | */ 92 | static uint32_t 93 | ber_uint_enc_try(unsigned int value) 94 | { 95 | uint32_t i, len; 96 | union anonymous { 97 | uint8_t buf[sizeof(unsigned int)]; 98 | unsigned int tmp; 99 | } a; 100 | 101 | a.tmp = value; 102 | len = 0; 103 | 104 | #ifdef LITTLE_ENDIAN 105 | i = sizeof(unsigned int) - 1; 106 | 107 | while (i > 0 && !a.buf[i]) { 108 | i--; 109 | } 110 | if (a.buf[i] & 0x80) { 111 | len += 1; 112 | } 113 | 114 | len += i + 1; 115 | #else 116 | i = 0; 117 | 118 | while (i < sizeof(unsigned int) && !a.buf[i]) { 119 | i++; 120 | } 121 | if (a.buf[i] & 0x80) { 122 | len += 1; 123 | } 124 | 125 | len += sizeof(unsigned int) - i; 126 | #endif 127 | 128 | return len; 129 | } 130 | 131 | 132 | /* Input: oid pointer, number of elements 133 | * Output: none 134 | * Return: byte length. 135 | */ 136 | static uint32_t 137 | ber_oid_enc_try(const oid_t *oid, uint32_t len) 138 | { 139 | uint8_t i, j, tmp[10]; 140 | 141 | if (len < 2) 142 | return len; 143 | 144 | for (j = 1, i = 2; i < len; i++) { 145 | uint32_t k = 0; 146 | oid_t id = oid[i]; 147 | do { 148 | tmp[k++] = (id & 0x7f) | 0x80; 149 | id /= 128; 150 | } while (id); 151 | tmp[0] &= 0x7f; 152 | do { 153 | j++; 154 | k--; 155 | } while (k); 156 | } 157 | 158 | return j; 159 | } 160 | 161 | /* Input: value pointer, number of elements, value type 162 | * Output: none 163 | * Return: byte length. 164 | */ 165 | uint32_t 166 | ber_value_enc_try(const void *value, uint32_t len, uint8_t type) 167 | { 168 | uint32_t ret; 169 | const oid_t *oid; 170 | const int *inter; 171 | const unsigned int *uinter; 172 | 173 | switch (type) { 174 | case ASN1_TAG_INT: 175 | case ASN1_TAG_CNT: 176 | inter = (const int *)value; 177 | ret = ber_int_enc_try(*inter); 178 | break; 179 | case ASN1_TAG_GAU: 180 | case ASN1_TAG_TIMETICKS: 181 | uinter = (const unsigned int *)value; 182 | ret = ber_uint_enc_try(*uinter); 183 | break; 184 | case ASN1_TAG_OBJID: 185 | oid = (const oid_t *)value; 186 | ret = ber_oid_enc_try(oid, len); 187 | break; 188 | case ASN1_TAG_OCTSTR: 189 | case ASN1_TAG_IPADDR: 190 | ret = len; 191 | break; 192 | case ASN1_TAG_SEQ: 193 | case ASN1_TAG_NUL: 194 | default: 195 | ret = 0; 196 | break; 197 | } 198 | 199 | return ret; 200 | } 201 | 202 | /* Input: integer value 203 | * Output: buffer 204 | * Return: byte length. 205 | */ 206 | static uint32_t 207 | ber_int_enc(int value, uint8_t *buf) 208 | { 209 | int i, j; 210 | union anonymous { 211 | uint8_t buf[sizeof(int)]; 212 | int tmp; 213 | } a; 214 | 215 | a.tmp = value; 216 | j = 0; 217 | 218 | #ifdef LITTLE_ENDIAN 219 | i = sizeof(int) - 1; 220 | 221 | if (value >= 0) { 222 | while (i > 0 && !a.buf[i]) { 223 | i--; 224 | } 225 | if (a.buf[i] & 0x80) { 226 | buf[j++] = 0x0; 227 | } 228 | } else { 229 | while (i > 0 && a.buf[i] == 0xff) { 230 | i--; 231 | } 232 | if (!(a.buf[i] & 0x80)) { 233 | buf[j++] = 0xff; 234 | } 235 | } 236 | 237 | while (i >= 0) { 238 | buf[j++] = a.buf[i--]; 239 | } 240 | #else 241 | i = 0; 242 | 243 | if (value >= 0) { 244 | while (i < sizeof(int) && !a.buf[i]) { 245 | i++; 246 | } 247 | if (a.buf[i] & 0x80) { 248 | buf[j++] = 0x0; 249 | } 250 | } else { 251 | while (i < sizeof(int) && a.buf[i] == 0xff) { 252 | i++; 253 | } 254 | if (!(a.buf[i] & 0x80)) { 255 | buf[j++] = 0xff; 256 | } 257 | } 258 | 259 | while (i < sizeof(int)) { 260 | buf[j++] = a.buf[i++]; 261 | } 262 | #endif 263 | 264 | return j; 265 | } 266 | 267 | /* Input: unsigned integer value 268 | * Output: buffer 269 | * Return: byte length. 270 | */ 271 | static uint32_t 272 | ber_uint_enc(int value, uint8_t *buf) 273 | { 274 | int i, j; 275 | union anonymous { 276 | uint8_t buf[sizeof(unsigned int)]; 277 | unsigned int tmp; 278 | } a; 279 | 280 | a.tmp = value; 281 | j = 0; 282 | 283 | #ifdef LITTLE_ENDIAN 284 | i = sizeof(unsigned int) - 1; 285 | 286 | while (i > 0 && !a.buf[i]) { 287 | i--; 288 | } 289 | if (a.buf[i] & 0x80) { 290 | buf[j++] = 0x0; 291 | } 292 | 293 | while (i >= 0) { 294 | buf[j++] = a.buf[i--]; 295 | } 296 | #else 297 | i = 0; 298 | 299 | while (i < sizeof(unsigned int) && !a.buf[i]) { 300 | i++; 301 | } 302 | if (a.buf[i] & 0x80) { 303 | buf[j++] = 0x0; 304 | } 305 | 306 | while (i < sizeof(unsigned int)) { 307 | buf[j++] = a.buf[i++]; 308 | } 309 | #endif 310 | 311 | return j; 312 | } 313 | 314 | 315 | /* Input: oid pointer, number of elements 316 | * Output: buffer 317 | * Return: byte length. 318 | */ 319 | static uint32_t 320 | ber_oid_enc(const oid_t *oid, uint32_t len, uint8_t *buf) 321 | { 322 | uint32_t i, j; 323 | uint8_t tmp[10]; 324 | 325 | if (len == 0) { 326 | return 0; 327 | } else if (len == 1) { 328 | buf[0] = oid[0]; 329 | return 1; 330 | } 331 | 332 | buf[0] = oid[0] * 40 + oid[1]; 333 | 334 | for (j = 1, i = 2; i < len; i++) { 335 | uint32_t k = 0; 336 | oid_t id = oid[i]; 337 | do { 338 | tmp[k++] = (id & 0x7f) | 0x80; 339 | id /= 128; 340 | } while (id); 341 | tmp[0] &= 0x7f; 342 | do { 343 | buf[j++] = tmp[--k]; 344 | } while (k); 345 | } 346 | 347 | return j; 348 | } 349 | 350 | /* Input: value pointer, number of elements, value type 351 | * Output: buffer 352 | * Return: byte length. 353 | */ 354 | uint32_t 355 | ber_value_enc(const void *value, uint32_t len, uint8_t type, uint8_t *buf) 356 | { 357 | uint32_t ret; 358 | const uint8_t *str; 359 | const oid_t *oid; 360 | const int *inter; 361 | const unsigned int *uinter; 362 | 363 | switch (type) { 364 | case ASN1_TAG_INT: 365 | case ASN1_TAG_CNT: 366 | inter = (const int *)value; 367 | ret = ber_int_enc(*inter, buf); 368 | break; 369 | case ASN1_TAG_GAU: 370 | case ASN1_TAG_TIMETICKS: 371 | uinter = (const unsigned int *)value; 372 | ret = ber_uint_enc(*uinter, buf); 373 | break; 374 | case ASN1_TAG_OBJID: 375 | oid = (const oid_t *)value; 376 | ret = ber_oid_enc(oid, len, buf); 377 | break; 378 | case ASN1_TAG_OCTSTR: 379 | case ASN1_TAG_IPADDR: 380 | str = (const uint8_t *)value; 381 | memcpy(buf, str, len); 382 | ret = len; 383 | break; 384 | case ASN1_TAG_SEQ: 385 | case ASN1_TAG_NUL: 386 | default: 387 | ret = 0; 388 | break; 389 | } 390 | 391 | return ret; 392 | } 393 | 394 | /* Input: length value 395 | * Output: none 396 | * Return: byte length. 397 | */ 398 | uint32_t 399 | ber_length_enc_try(uint32_t value) 400 | { 401 | uint32_t i, len; 402 | union anonymous { 403 | uint8_t buf[sizeof(uint32_t)]; 404 | uint32_t tmp; 405 | } a; 406 | 407 | a.tmp = value; 408 | len = 0; 409 | 410 | #ifdef LITTLE_ENDIAN 411 | i = sizeof(uint32_t) - 1; 412 | while (i > 0 && !a.buf[i]) { 413 | i--; 414 | } 415 | 416 | if (a.tmp > 127) { 417 | len += 1; 418 | } 419 | 420 | len += i + 1; 421 | #else 422 | i = 0; 423 | while (i < sizeof(uint32_t) && !a.buf[i]) { 424 | i++; 425 | } 426 | 427 | if (a.tmp > 127) { 428 | len += 1; 429 | } 430 | 431 | len += sizeof(uint32_t) - i; 432 | #endif 433 | 434 | return len; 435 | } 436 | 437 | /* Input: length value 438 | * Output: buffer 439 | * Return: byte length. 440 | */ 441 | uint32_t 442 | ber_length_enc(uint32_t value, uint8_t *buf) 443 | { 444 | int i, j; 445 | union anonymous { 446 | uint8_t buf[sizeof(uint32_t)]; 447 | uint32_t tmp; 448 | } a; 449 | 450 | a.tmp = value; 451 | j = 0; 452 | 453 | #ifdef LITTLE_ENDIAN 454 | i = sizeof(uint32_t) - 1; 455 | while (i > 0 && !a.buf[i]) { 456 | i--; 457 | } 458 | 459 | if (a.tmp > 127) { 460 | buf[j++] = 0x80 | (i + 1); 461 | } 462 | 463 | while (i >= 0) { 464 | buf[j++] = a.buf[i--]; 465 | } 466 | #else 467 | i = 0; 468 | while (i < sizeof(uint32_t) && !a.buf[i]) { 469 | i++; 470 | } 471 | 472 | if (a.tmp > 127) { 473 | buf[j++] = 0x80 | (sizeof(uint32_t) - i); 474 | } 475 | 476 | while (i < sizeof(uint32_t)) { 477 | buf[j++] = a.buf[i++]; 478 | } 479 | #endif 480 | 481 | return j; 482 | } 483 | -------------------------------------------------------------------------------- /core/snmp_msg_out.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of SmartSNMP 3 | * Copyright (C) 2014, Credo Semiconductor Inc. 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License as published by 7 | * the Free Software Foundation; either version 2 of the License, or 8 | * (at your option) any later version. 9 | * 10 | * This program is distributed in the hope that it will be useful, 11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | * GNU General Public License for more details. 14 | * 15 | * You should have received a copy of the GNU General Public License along 16 | * with this program; if not, write to the Free Software Foundation, Inc., 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 18 | * 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "mib.h" 26 | #include "snmp.h" 27 | #include "protocol.h" 28 | #include "util.h" 29 | 30 | static octstr_t snmpv3_engine_id[] = { 31 | 0x80, 0x00, 0x00, 0x00, 32 | /* Text */ 33 | 0x04, 34 | /* 'S', 'm', 'a', 'r', 't', 'S', 'N', 'M', 'P' */ 35 | 0x53, 0x6d, 0x61, 0x72, 0x74, 0x53, 0x4e, 0x4d, 0x50, 36 | }; 37 | 38 | static uint32_t 39 | global_data_encode_try(struct snmp_datagram *sdg) 40 | { 41 | const uint32_t tag_len = 1; 42 | uint32_t len_len, msg_len; 43 | 44 | len_len = ber_length_enc_try(sdg->msg_id_len); 45 | msg_len = tag_len + len_len + sdg->msg_id_len; 46 | 47 | len_len = ber_length_enc_try(sdg->msg_size_len); 48 | msg_len += tag_len + len_len + sdg->msg_size_len; 49 | 50 | len_len = ber_length_enc_try(sdg->msg_flags_len); 51 | msg_len += tag_len + len_len + sdg->msg_flags_len; 52 | 53 | len_len = ber_length_enc_try(sdg->msg_model_len); 54 | msg_len += tag_len + len_len + sdg->msg_model_len; 55 | 56 | return msg_len; 57 | } 58 | 59 | static uint8_t * 60 | global_data_encode(struct snmp_datagram *sdg, uint8_t *buf) 61 | { 62 | /* Global data sequence */ 63 | *buf++ = ASN1_TAG_SEQ; 64 | buf += ber_length_enc(sdg->msg_len, buf); 65 | 66 | /* Messege ID */ 67 | *buf++ = ASN1_TAG_INT; 68 | buf += ber_length_enc(sdg->msg_id_len, buf); 69 | buf += ber_value_enc(&sdg->msg_id, 1, ASN1_TAG_INT, buf); 70 | 71 | /* Messege max size */ 72 | *buf++ = ASN1_TAG_INT; 73 | buf += ber_length_enc(sdg->msg_size_len, buf); 74 | buf += ber_value_enc(&sdg->msg_max_size, 1, ASN1_TAG_INT, buf); 75 | 76 | /* Messege flags */ 77 | *buf++ = ASN1_TAG_OCTSTR; 78 | buf += ber_length_enc(sdg->msg_flags_len, buf); 79 | sdg->msg_flags = 0; 80 | buf += ber_value_enc(&sdg->msg_flags, sdg->msg_flags_len, ASN1_TAG_OCTSTR, buf); 81 | 82 | /* Messege security model */ 83 | *buf++ = ASN1_TAG_INT; 84 | buf += ber_length_enc(sdg->msg_model_len, buf); 85 | buf += ber_value_enc(&sdg->msg_security_model, 1, ASN1_TAG_INT, buf); 86 | 87 | return buf; 88 | } 89 | 90 | static uint32_t 91 | security_parameter_encode_try(struct snmp_datagram *sdg) 92 | { 93 | const uint32_t tag_len = 1; 94 | uint32_t len_len, secur_para_len; 95 | 96 | sdg->engine_id_len = sizeof(snmpv3_engine_id); 97 | len_len = ber_length_enc_try(sdg->engine_id_len); 98 | secur_para_len = tag_len + len_len + sdg->engine_id_len; 99 | 100 | len_len = ber_length_enc_try(sdg->engine_boots_len); 101 | secur_para_len += tag_len + len_len + sdg->engine_boots_len; 102 | 103 | len_len = ber_length_enc_try(sdg->engine_time_len); 104 | secur_para_len += tag_len + len_len + sdg->engine_time_len; 105 | 106 | len_len = ber_length_enc_try(sdg->user_name_len); 107 | secur_para_len += tag_len + len_len + sdg->user_name_len; 108 | 109 | len_len = ber_length_enc_try(sdg->auth_para_len); 110 | secur_para_len += tag_len + len_len + sdg->auth_para_len; 111 | 112 | len_len = ber_length_enc_try(sdg->priv_para_len); 113 | secur_para_len += tag_len + len_len + sdg->priv_para_len; 114 | 115 | return secur_para_len; 116 | } 117 | 118 | static uint8_t * 119 | security_parameter_encode(struct snmp_datagram *sdg, uint8_t *buf) 120 | { 121 | /* Security parameter sequence */ 122 | *buf++ = ASN1_TAG_SEQ; 123 | buf += ber_length_enc(sdg->secur_para_len, buf); 124 | 125 | /* Engine ID */ 126 | *buf++ = ASN1_TAG_OCTSTR; 127 | buf += ber_length_enc(sdg->engine_id_len, buf); 128 | buf += ber_value_enc(snmpv3_engine_id, sdg->engine_id_len, ASN1_TAG_OCTSTR, buf); 129 | 130 | /* Engine boots */ 131 | *buf++ = ASN1_TAG_INT; 132 | buf += ber_length_enc(sdg->engine_boots_len, buf); 133 | buf += ber_value_enc(&sdg->engine_boots, 1, ASN1_TAG_INT, buf); 134 | 135 | /* Engine time */ 136 | *buf++ = ASN1_TAG_INT; 137 | buf += ber_length_enc(sdg->engine_time_len, buf); 138 | buf += ber_value_enc(&sdg->engine_time, 1, ASN1_TAG_INT, buf); 139 | 140 | /* User name */ 141 | *buf++ = ASN1_TAG_OCTSTR; 142 | buf += ber_length_enc(sdg->user_name_len, buf); 143 | buf += ber_value_enc(&sdg->user_name, sdg->user_name_len, ASN1_TAG_OCTSTR, buf); 144 | 145 | /* Authotative parameter */ 146 | *buf++ = ASN1_TAG_OCTSTR; 147 | buf += ber_length_enc(sdg->auth_para_len, buf); 148 | buf += ber_value_enc(&sdg->auth_para, sdg->auth_para_len, ASN1_TAG_OCTSTR, buf); 149 | 150 | /* Privative parameter */ 151 | *buf++ = ASN1_TAG_OCTSTR; 152 | buf += ber_length_enc(sdg->priv_para_len, buf); 153 | buf += ber_value_enc(&sdg->priv_para, sdg->priv_para_len, ASN1_TAG_OCTSTR, buf); 154 | 155 | return buf; 156 | } 157 | 158 | static uint8_t * 159 | asn1_encode(struct snmp_datagram *sdg) 160 | { 161 | struct pdu_hdr *ph; 162 | uint8_t *buf; 163 | const uint32_t tag_len = 1; 164 | uint32_t len_len; 165 | 166 | ph = &sdg->pdu_hdr; 167 | sdg->data_len = 0; 168 | 169 | len_len = ber_length_enc_try(sdg->vb_list_len); 170 | ph->pdu_len = tag_len + len_len + sdg->vb_list_len; 171 | 172 | len_len = ber_length_enc_try(ph->req_id_len); 173 | ph->pdu_len += tag_len + len_len + ph->req_id_len; 174 | 175 | len_len = ber_length_enc_try(ph->err_stat_len); 176 | ph->pdu_len += tag_len + len_len + ph->err_stat_len; 177 | 178 | len_len = ber_length_enc_try(ph->err_idx_len); 179 | ph->pdu_len += tag_len + len_len + ph->err_idx_len; 180 | 181 | len_len = ber_length_enc_try(ph->pdu_len); 182 | sdg->scope_len = tag_len + len_len + ph->pdu_len; 183 | 184 | len_len = ber_length_enc_try(sdg->context_name_len); 185 | sdg->scope_len += tag_len + len_len + sdg->context_name_len; 186 | 187 | if (sdg->version >= 3) { 188 | /* SNMPv3 */ 189 | sdg->context_id_len = sizeof(snmpv3_engine_id); 190 | len_len = ber_length_enc_try(sdg->context_id_len); 191 | sdg->scope_len += tag_len + len_len + sdg->context_id_len; 192 | 193 | sdg->secur_para_len = security_parameter_encode_try(sdg); 194 | 195 | len_len = ber_length_enc_try(sdg->secur_para_len); 196 | sdg->secur_str_len = tag_len + len_len + sdg->secur_para_len; 197 | 198 | sdg->msg_len = global_data_encode_try(sdg); 199 | 200 | len_len = ber_length_enc_try(sdg->msg_len); 201 | sdg->data_len += tag_len + len_len + sdg->msg_len; 202 | 203 | len_len = ber_length_enc_try(sdg->secur_str_len); 204 | sdg->data_len += tag_len + len_len + sdg->secur_str_len; 205 | 206 | len_len = ber_length_enc_try(sdg->scope_len); 207 | sdg->data_len += tag_len + len_len; 208 | } 209 | 210 | sdg->data_len += sdg->scope_len; 211 | 212 | len_len = ber_length_enc_try(sdg->ver_len); 213 | sdg->data_len += tag_len + len_len + sdg->ver_len; 214 | 215 | len_len = ber_length_enc_try(sdg->data_len); 216 | sdg->send_buf = xmalloc(tag_len + len_len + sdg->data_len); 217 | 218 | buf = sdg->send_buf; 219 | 220 | /* Datagram sequence */ 221 | *buf++ = ASN1_TAG_SEQ; 222 | buf += ber_length_enc(sdg->data_len, buf); 223 | 224 | /* Version */ 225 | *buf++ = ASN1_TAG_INT; 226 | buf += ber_length_enc(sdg->ver_len, buf); 227 | buf += ber_value_enc(&sdg->version, 1, ASN1_TAG_INT, buf); 228 | 229 | if (sdg->version >= 3) { 230 | /* Global data */ 231 | buf = global_data_encode(sdg, buf); 232 | 233 | /* Security string */ 234 | *buf++ = ASN1_TAG_OCTSTR; 235 | buf += ber_length_enc(sdg->secur_str_len, buf); 236 | 237 | /* Security parameter */ 238 | buf = security_parameter_encode(sdg, buf); 239 | 240 | /* Context sequence */ 241 | *buf++ = ASN1_TAG_SEQ; 242 | buf += ber_length_enc(sdg->scope_len, buf); 243 | 244 | /* Context ID */ 245 | *buf++ = ASN1_TAG_OCTSTR; 246 | buf += ber_length_enc(sizeof(snmpv3_engine_id), buf); 247 | buf += ber_value_enc(snmpv3_engine_id, sizeof(snmpv3_engine_id), ASN1_TAG_OCTSTR, buf); 248 | } 249 | 250 | /* Context_name */ 251 | *buf++ = ASN1_TAG_OCTSTR; 252 | buf += ber_length_enc(sdg->context_name_len, buf); 253 | buf += ber_value_enc(sdg->context_name, sdg->context_name_len, ASN1_TAG_OCTSTR, buf); 254 | 255 | /* PDU header */ 256 | *buf++ = ph->pdu_type; 257 | buf += ber_length_enc(ph->pdu_len, buf); 258 | 259 | /* Request ID */ 260 | *buf++ = ASN1_TAG_INT; 261 | buf += ber_length_enc(ph->req_id_len, buf); 262 | buf += ber_value_enc(&ph->req_id, 1, ASN1_TAG_INT, buf); 263 | 264 | /* Error status */ 265 | *buf++ = ASN1_TAG_INT; 266 | buf += ber_length_enc(ph->err_stat_len, buf); 267 | buf += ber_value_enc(&ph->err_stat, 1, ASN1_TAG_INT, buf); 268 | 269 | /* Error index */ 270 | *buf++ = ASN1_TAG_INT; 271 | buf += ber_length_enc(ph->err_idx_len, buf); 272 | buf += ber_value_enc(&ph->err_idx, 1, ASN1_TAG_INT, buf); 273 | 274 | /* Varbind list */ 275 | *buf++ = ASN1_TAG_SEQ; 276 | buf += ber_length_enc(sdg->vb_list_len, buf); 277 | 278 | return buf; 279 | } 280 | 281 | void 282 | snmp_response(struct snmp_datagram *sdg) 283 | { 284 | struct var_bind *vb_out; 285 | struct list_head *curr, *next; 286 | uint8_t *buf; 287 | uint32_t oid_len, len_len; 288 | const uint32_t tag_len = 1; 289 | 290 | buf = asn1_encode(sdg); 291 | 292 | list_for_each_safe(curr, next, &sdg->vb_out_list) { 293 | vb_out = list_entry(curr, struct var_bind, link); 294 | 295 | *buf++ = ASN1_TAG_SEQ; 296 | buf += ber_length_enc(vb_out->vb_len, buf); 297 | 298 | /* oid */ 299 | *buf++ = ASN1_TAG_OBJID; 300 | oid_len = ber_value_enc_try(vb_out->oid, vb_out->oid_len, ASN1_TAG_OBJID); 301 | buf += ber_length_enc(oid_len, buf); 302 | buf += ber_value_enc(vb_out->oid, vb_out->oid_len, ASN1_TAG_OBJID, buf); 303 | 304 | /* value */ 305 | *buf++ = vb_out->value_type; 306 | buf += ber_length_enc(vb_out->value_len, buf); 307 | memcpy(buf, vb_out->value, vb_out->value_len); 308 | buf += vb_out->value_len; 309 | } 310 | 311 | len_len = ber_length_enc_try(sdg->data_len); 312 | /* This callback will free send_buf */ 313 | snmp_prot_ops.send(sdg->send_buf, tag_len + len_len + sdg->data_len); 314 | } 315 | --------------------------------------------------------------------------------