Đây là một công đoạn hết sức vất vả, buộc người phát triển phải nắm được sâu lý thuyết về giao thức SNMP, cách thức mã hoá BER các dòng dữ liệu cũng như công cụ và kỹ năng lập trình để thực hiện các module này.
Trước hết, ta xem xét thành phần Engine, đây là thành phần mà nhiệm vụ đầu tiên của nó là khởi tạo tiến trình. Trong môi trường lập trình C trong hệ điều hành Linux, việc khởi tạo tiến trình chủ từ hàm trung tâm của Agent có thể được thực hiện như sau:
/*
* Description */
void snmp_agt_main(server_rec *s, pool *p, pool *ptemp){ int count;
int snmp_socket; int numfds; fd_set fdset;
snmpagent_pid = fork();
if (snmpagent_pid == -1){ //Fork error
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s, "SNMP: unable to fork agent");
} else if (snmpagent_pid > 0) { return; }
//Open the network
snmp_socket = snmp_open_connection(snmp_addr, snmp_port); if (snmp_socket == 0){
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, s, "SNMP: cannot open socket");
exit(0); } ensure_communities(); init_SNMPv2_MIB(); init_WWW_MIB(); init_APACHE_CONFIG_MIB();
init_APACHE_SCOREBOARD_MIB(); init_wwwServiceOperStatus(); init_wwwProtocolStatistics(s, p); www_services = s;
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s,
"SNMP: agent started and listens on port '%s:%d'", snmp_addr, snmp_port);
//Listen to the network while(1){ numfds = 0; FD_ZERO(&fdset); FD_SET(snmp_socket, &fdset); FD_SET(http2snmp_socket, &fdset); if (snmp_socket > http2snmp_socket) { numfds = snmp_socket+1; } else { numfds = http2snmp_socket+1; }
count = select(numfds, &fdset, 0, 0, 0); if (count > 0){
if (FD_ISSET(snmp_socket, &fdset)) { snmp_process_message(snmp_socket);
} else if (FD_ISSET(http2snmp_socket, &fdset)) { snmp_log_lastDocument(http2snmp_socket); } } else switch(count){ case 0: break; case -1: if (errno == EINTR) { continue; } else { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "SNMP: select error '%s'\n", strerror(errno)); }
default:
ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_NOTICE, s, "SNMP: select returned %d\n", count); }
} }
Trong hàm trên, các tham số truyền vào nhằm phục vụ cho các thủ tục khác của Agent. Ta thấy đầu tiên là hàm fork, hàm này có nhiệm khởi sinh tiến trình con tiếp theo nếu có nhiều hơn một kết nối. Tiếp theo là hàm
snmp_open_connection, hàm này có nhiệm vụ khởi sinh dịch vụ trên giao thức UDP với địa chỉ IP và số hiệu cổng dịch vụ UDP được truyền từ biến toàn cục vào. Tiếp theo là các hàm khởi tạo các cơ sở dữ liệu MIB. Cuối cùng là chu trình while hết sức quan trọng, vì nó cho phép khởi tạo tiến trình con nếu có nhiều hơn một kết nối, tiến trình con sau khi hoạt động xong nó sẽ tự
huỷ. Trong hàm snmp_agt_main ta cũng thấy nó có nhiệm vụ gọi các hàm khởi tạo các trạng thái dịch vụ và giao thức.
Một hàm quan trọng khác trong quá trình khởi tạo dịch vụ UDP đó là hàm snmp_open_connection. Hàm này được định nghĩa như sau:
/*
* Descriptions */
int snmp_open_connection(char* snmp_addr, int snmp_port){ int socketDescriptor;
struct sockaddr_in mySocketAddress;
socketDescriptor = socket(AF_INET, SOCK_DGRAM, 0); if (socketDescriptor < 0){
perror("socket");
printf ("error calling \"socket()\"\n."); exit(2); } mySocketAddress.sin_family = AF_INET; mySocketAddress.sin_addr.s_addr = inet_addr(snmp_addr); if (sizeof (mySocketAddress.sin_port) == 2) { *(short*)&mySocketAddress.sin_port = htons((short) snmp_port);
} else if (sizeof (mySocketAddress.sin_port) == 4) {
*(long *)&mySocketAddress.sin_port = htonl(snmp_port); } else {
abort (); }
if (bind(socketDescriptor, (struct sockaddr *)&mySocketAddress, sizeof(mySocketAddress))){ perror("bind");
printf ("error calling \"bind()\"\n."); exit(2);
}
printf("Port %u succesfully opened.\n", snmp_port); return (socketDescriptor);
}
Trong thủ tục này, các tham số truyền vào là địa chỉ IP và số hiệu cổng dịch vụ mà tiến trình sẽ lắng nghe. Thông thường các thông số này sẽ được truyền như sau:
snmp_open_connection("0.0.0.0", 161)
tức là tiến trình sẽ lắng nghe trên cổng 161 (cổng chuẩn của giao thức SNMP) và chấp nhận mọi địa chỉ kết nối đến. Hoặc ta cũng có thể chỉ định một địa chỉ (ví dụ 192.168.1.100) hay lớp địa chỉ cụ thể để giới hạn sự truy nhập từ các máy xa lạ.
Hàm hệ thống socket có nhiệm vụ khởi tạo dịch vụ trên giao thức UDP (SOCK_DGRAM tương ứng là giao thức UDP), và hàm bind có nhiệm vụ kết nối địa chỉ IP và số hiệu cổng dịch vụ trên giao thức UDP.
Phần tiếp theo ta sẽ triển khai các thủ tục khởi tạo các cơ sở dữ liệu thông tin quản lý MIB. Do cách thức triển khai các MIB là tương tự nhau, nên ở đây ta sẽ xem xét Apache-Scoreboard-MIB làm mẫu. Ta tổ chức thành hai tệp riêng biệt là tệp tiêu đề scroreboard-mib.h và tệp mã scroreboard-mib.c. Tệp tiêu đề được thể hiện như sau:
/* * scoreboard-mib.h */ #ifndef _APACHE_SCOREBOARD_MIB_ #define _APACHE_SCOREBOARD_MIB_ void init_APACHE_SCOREBOARD_MIB(); void register_subtrees_of_APACHE_SCOREBOARD_MIB(); //Defined objects in this module
//Nhánh scoreboardMIB = myApache, 5 //Đặc tả nút (node)
#define I_scoreboardMIB 5 //Đặc tả nhánh (branch)
#define O_scoreboardMIB 1, 3, 6, 1, 4, 1, 785, 5
//MIB object myApache = enterprises, 785 #define I_myApache 785
#define O_myApache 1, 3, 6, 1, 4, 1, 785
//MIB object scoreboardMIBObjects = scoreboardMIB, 1 #define I_scoreboardMIBObjects 1
#define O_scoreboardMIBObjects 1, 3, 6, 1, 4, 1, 785, 5, 1
//MIB object scoreBoardTable = scoreboardMIBObjects, 1 #define I_scoreBoardTable 1
#define O_scoreBoardTable 1, 3, 6, 1, 4, 1, 785, 5, 1, 1
//MIB object scoreBoardEntry = scoreBoardTable, 1 #define I_scoreBoardEntry 1
#define O_scoreBoardEntry 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1
//MIB object scoreBoardIndex = scoreBoardEntry, 1 #define I_scoreBoardIndex 1
#define O_scoreBoardIndex 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 1
//MIB object scoreBoardProcessId = scoreBoardEntry, 2 #define I_scoreBoardProcessId 2
#define O_scoreBoardProcessId 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 2
//MIB object scoreBoardStatus = scoreBoardEntry, 3 #define I_scoreBoardStatus 3
#define O_scoreBoardStatus 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 3
//MIB object scoreBoardStartTime = scoreBoardEntry, 4 #define I_scoreBoardStartTime 4
#define O_scoreBoardStartTime 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 4
//MIB object scoreBoardAccessCount = scoreBoardEntry, 5 #define I_scoreBoardAccessCount 5
#define O_scoreBoardAccessCount 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 5
//MIB object scoreBoardAccessBytes = scoreBoardEntry, 6 #define I_scoreBoardAccessBytes 6
#define O_scoreBoardAccessBytes 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 6
//MIB object scoreBoardClient = scoreBoardEntry, 7 #define I_scoreBoardClient 7
#define O_scoreBoardClient 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 7
//MIB object scoreBoardRequest = scoreBoardEntry, 8 #define I_scoreBoardRequest 8
#define O_scoreBoardRequest 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 8
//MIB object scoreBoardVirtualHost = scoreBoardEntry, 9 #define I_scoreBoardVirtualHost 9
#define O_scoreBoardVirtualHost 1, 3, 6, 1, 4, 1, 785, 5, 1, 1, 1, 9
//MIB object scoreboardMIBConformance = scoreboardMIB, 2 #define I_scoreboardMIBConformance 2
#define O_scoreboardMIBConformance 1, 3, 6, 1, 4, 1, 785, 5, 2
//MIB object scoreboardMIBCompliances = scoreboardMIBConformance, 1 #define I_scoreboardMIBCompliances 1
#define O_scoreboardMIBCompliances 1, 3, 6, 1, 4, 1, 785, 5, 2, 1
//MIB object scoreboardMIBGroups = scoreboardMIBConformance, 2 #define I_scoreboardMIBGroups 2
#define O_scoreboardMIBGroups 1, 3, 6, 1, 4, 1, 785, 5, 2, 2
//MIB object scoreBoardGroup = scoreboardMIBGroups, 1 #define I_scoreBoardGroup 1
#define O_scoreBoardGroup 1, 3, 6, 1, 4, 1, 785, 5, 2, 2, 1
#endif //_APACHE_SCOREBOARD_MIB_
Tiếp theo, ta thực hiện tệp mã scroreboard-mib.c. Để hoàn thiện tệp mã này ta cần có rất nhiều các hàm phụ để thực hiện các chức năng cụ thể, sau đây là mô tả tóm tắt.
/*
* scoreboard-mib.c */
#include "scoreboard-mib.h"
//Phần đầu gồm nhiều tệp tiêu đề khác như: // http.h, asn1.h, snmp.h, snmpv2-mib.h,...
//Hàm khởi tạo cơ sở dữ liệu
register_subtrees_of_APACHE_SCOREBOARD_MIB(); //insert the Object Resource in sysORTable {
static struct sysOREntry_struct value = { NULL, 1, {8,
{O_scoreboardMIB}},
"The Scoreboard MIB module", 0};
insert_sysOREntry(&value); }
}
//Định nghĩa các biến, mảng
static oid scoreBoardEntry_oid[] = { O_scoreBoardEntry }; static Object scoreBoardEntry_variables[] = {
{ SNMP_UINTEGER, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardProcessId }}},
{ SNMP_INTEGER, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardStatus }}},
{ SNMP_TIMETICKS, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardStartTime }}},
{ SNMP_UINTEGER, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardAccessCount }}},
{ SNMP_UINTEGER, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardAccessBytes }}},
{ SNMP_STRING, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardClient }}},
{ SNMP_STRING, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardRequest }}},
{ SNMP_UINTEGER, (RONLY| COLUMN), var_scoreBoardEntry, {1, { I_scoreBoardVirtualHost }}},
{ 0, 0, NULL, {0, {0}}} };
static SubTree scoreBoardEntry_tree = { NULL, scoreBoardEntry_variables, (sizeof(scoreBoardEntry_oid)/sizeof(oid)), scoreBoardEntry_oid }; //Hàm đăng ký nhánh MIB void register_subtrees_of_APACHE_SCOREBOARD_MIB(){ insert_group_in_mib(&scoreBoardEntry_tree); }
Sau khi có toàn bộ các module này, ta sẽ tiến hành biên dịch nó với hệ thống Apache. Trước hết ta cần phải có bộ mã nguồn của máy chủ web Apache, sau đó ta thực hiện các thao tác dưới hệ điều hành Linux như sau:
- Giải nén tệp apache_1.3.33.tar.gz [13] bằng lệnh: #tar –xvfz apache_1.3.33.tar.gz
- Toàn bộ mã nguồn Agent được đưa vào thư mục: apache_1.3_33/src/modules/snmg_agt
- Cấu hình máy chủ Apache:
#CFLAGS=“-DSNMP –DUSE_DB” ./configuare \
Nếu không có lỗi, ta thực hiện biên dịch lại mã nguồn hệ thống Apache bằng lệnh sau:
#make
#make install
- Sau khi cài đặt (mặc định tại thư mục “/usr/local”), ta cần sửa tệp “http.conf” và thêm vào các thông số cho Agent như sau:
<SNMP 161>
SNMPbuckets logs SNMPcommunity public
sysDescr Apache 1.3.33 HTTP/1.1 – SNMP module 1.3.6 sysContact admins@mysite.com
sysLocation cntt.dhgqhn.vn </SNMP>
Trong phần cấu hình trên “<SNMP 161>” thể hiện phần cấu hình dành cho ứng dụng sử dụng từ khoá SNMP (là Agent của ta) và 161 chính là số hiệu cổng dịch UDP của Agent. Tiếp theo là các thông số dành cho nhật ký, điều kiện truy nhập thông tin (public) và các thông tin về hệ thống. Phần sau sẽ là kết quả thử nghiệm và đánh giá hoạt động toàn bộ hệ thống tác tử trên.