1 /*
2 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5
6 #pragma ident "%Z%%M% %I% %E% SMI"
7
8
9
10 /***********************************************************
11 Copyright 1988, 1989 by Carnegie Mellon University
12
13 All Rights Reserved
14
15 Permission to use, copy, modify, and distribute this software and its
16 documentation for any purpose and without fee is hereby granted,
17 provided that the above copyright notice appear in all copies and that
18 both that copyright notice and this permission notice appear in
19 supporting documentation, and that the name of CMU not be
20 used in advertising or publicity pertaining to distribution of the
21 software without specific, written prior permission.
22
23 CMU DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
24 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
25 CMU BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
26 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
27 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
28 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
29 SOFTWARE.
30 ******************************************************************/
31
32 #include <stdlib.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <sys/time.h>
36 #include <sys/types.h>
37 #include <netinet/in.h>
38 #include <arpa/inet.h>
39 #include <sys/socket.h>
40 #include <errno.h>
41 #include <syslog.h>
42 #include <unistd.h>
43 #include <signal.h>
44
45
46 #include "impl.h"
47 #include "error.h"
48 #include "trace.h"
49 #include "signals.h"
50 #include "snmp.h"
51 #include "pdu.h"
52
53 #include "snmprelay_msg.h"
54 #include "agent.h"
55 #include "subtree.h"
56 #include "session.h"
57 #include "config.h"
58 #include "dispatcher.h"
59 #include "res.h"
60
61 #include "sea_i18n.h"
62
63 /***** LOCAL CONSTANTS ******/
64
65 /*
66 * select() will returned at least every
67 * POLL_INTERVAL seconds to test if some
68 * sessions have SNMP requests that have
69 * timeout (<==> the SNMP agnet has not
70 * responded)
71 */
72
73 #define POLL_INTERVAL 240
74
75
76 /***** GLOBAL VARIABLES ******/
77
78 IPAddress my_ip_address;
79
80 int clients_sd = -1; /* socket descriptor "connected" to SNMP application */
81 int agents_sd = -1; /* socket descriptor "connected" to SNMP agent */
82 int trap_sd = -1; /* socket descriptor for receiving traps from agents */
83 int relay_agent_trap_port = 162;
84
85 char *relay_agent_name = NULL;
86 char *sec_config_file ;
87 char *pid_file;
88 char *name_oid_file;
89
90 int relay_agent_max_agent_time_out= 987654321;
91 int relay_agent_poll_interval = POLL_INTERVAL;
92
93
94 /*
95 * the name of the directory that
96 * contains the configuration files
97 */
98
99 char *config_dir = NULL;
100 char *resource_file = NULL;
101
102 /* There are two available modes to run the snmprelayd */
103 /* 1) MODE_SPLIT: we send a request for each variable */
104 /* 2) MODE_GROUP: we try to group the variables */
105 int mode = MODE_GROUP;
106 int recovery_on = FALSE;
107
108
109
110 static int sighup = False; /* if True, we received the SIGHUP signal */
111 static int port = 0; /* relay agent port */
112
113 static char default_config_dir[] = "/etc/snmp/conf";
114 static char default_resource_file[] = "/etc/snmp/conf/snmpdx.rsrc";
115 static char default_error_file[] = "/var/snmp/snmpdx.log";
116 static char default_relay_agent_name[] = "relay-agent";
117 static char default_pid_file[] = "/var/snmp/snmpdx.st";
118 static char default_name_oid_file[] = "/etc/snmp/conf/enterprises.oid";
119
120 static void application_end();
121 static void signals_sighup(int siq);
122 static void signals_exit(int siq);
123 static void signals_child(int siq);
124 static void init_relay_agent();
125 static void dispatcher_init();
126 static void dispatcher_loop();
127 static void print_usage();
128
129
130 /*
131 * this function is called by error_exit()
132 */
133 static void
application_end()134 application_end()
135 {
136 }
137
138
139 static void
signals_sighup(int siq)140 signals_sighup(int siq)
141 {
142 if(trace_level > 0)
143 trace("received signal %d\n\n", siq);
144
145 error(MSG_SIGHUP, siq);
146
147 sighup = True;
148 }
149
150 static void
signals_exit(int sig)151 signals_exit(int sig)
152 {
153 /*
154 * SIGTERM is a normal exit, don't log it to console.
155 */
156 if (sig != SIGTERM)
157 error_exit("received signal %d", sig);
158 else
159 syslog(LOG_INFO, "received signal %d\n", sig);
160
161 exit(sig);
162 }
163
164
165 static void
init_relay_agent()166 init_relay_agent()
167 {
168 /*
169 * find agent matches the name, then modify
170 * the process id, operstatus
171 */
172
173 Agent *ap = agent_find_by_name(relay_agent_name);
174 if (ap == NULL)
175 return;
176
177 ap->agentProcessID = (int)getpid();
178 ap->agentStatus = SSA_OPER_STATUS_ACTIVE;
179 ap->first_manager = (struct _Manager *)get_curr_manager_set();
180
181
182 if (port)
183 ap->agentPortNumber = port;
184 else if (ap->agentPortNumber)
185 port = ap->agentPortNumber;
186 else
187 port = ap->agentPortNumber = SNMP_PORT;
188
189 ap->address.sin_port = port;
190 /* init the subtrees of relay agent */
191 agent_update_subtree(ap);
192 }
193
194 static void
dispatcher_init()195 dispatcher_init()
196 {
197 struct sockaddr_in me;
198 socklen_t len;
199
200 /* init my_ip_address (we need it before parsing the config file) */
201
202 if(get_my_ip_address(&my_ip_address, error_label))
203 error_exit(ERR_MSG_MY_IP_ADDRESS,
204 error_label);
205
206 if(trace_level > 0)
207 trace("Local IP Addresss : %s\n\n",
208 inet_ntoa(my_ip_address));
209
210
211 /* init the config_dir pointer and then parse the configuration files */
212
213 if(config_dir == NULL)
214 config_dir = default_config_dir;
215
216 config_init(config_dir);
217
218 /* read enterprise name-oid file */
219 if(name_oid_file == NULL)
220 name_oid_file = default_name_oid_file;
221
222 load_enterprise_oid(name_oid_file);
223
224 /* set up the relay agent name */
225 if(sec_config_file == NULL)
226 sec_config_file = default_sec_config_file;
227
228 sec_config_init(sec_config_file);
229 if(relay_agent_name == NULL)
230 relay_agent_name = default_relay_agent_name;
231
232 init_relay_agent();
233
234 /* read the resource file */
235 if(resource_file == NULL)
236 resource_file = default_resource_file;
237 if(pid_file == NULL)
238 pid_file = default_pid_file;
239 write_pid_file1(pid_file);
240 res_config_init(config_dir);
241
242 /* init clients_sd and agents_sd */
243
244 clients_sd = socket(AF_INET, SOCK_DGRAM, 0);
245 if(clients_sd < 0)
246 error_exit(ERR_MSG_SOCKET, errno_string());
247
248 memset(&me, 0, sizeof(me));
249 me.sin_family = AF_INET;
250 me.sin_addr.s_addr = htonl(INADDR_ANY);
251 me.sin_port = htons(port);
252
253 if(trace_level > 0)
254 trace("Waiting for incoming SNMP requests on UDP port %d\n\n", port);
255
256 if (bind(clients_sd, (struct sockaddr *)&me, sizeof(me)) != 0)
257 error_exit(ERR_MSG_BIND, port, errno_string());
258
259 agents_sd = socket(AF_INET, SOCK_DGRAM, 0);
260 if (agents_sd < 0)
261 error_exit(ERR_MSG_SOCKET, errno_string());
262
263 me.sin_family = AF_INET;
264 me.sin_addr.s_addr = htonl(INADDR_ANY);
265 me.sin_port = htons(0);
266
267 if (bind(agents_sd, (struct sockaddr *)&me, sizeof(me)) != 0)
268 error_exit(ERR_MSG_BIND, 0, errno_string());
269
270 trap_sd = socket(AF_INET, SOCK_DGRAM, 0);
271 if (trap_sd < 0)
272 error_exit(ERR_MSG_SOCKET, errno_string());
273
274 me.sin_family = AF_INET;
275 me.sin_addr.s_addr = htonl(INADDR_ANY);
276 me.sin_port = htons(0);
277
278 if(bind(trap_sd, (struct sockaddr *)&me, sizeof(me)) != 0)
279 error_exit(ERR_MSG_BIND, 0, errno_string());
280
281
282 len = (socklen_t)sizeof(me);
283 if (getsockname(trap_sd, (struct sockaddr *)&me, &len) == -1)
284 error_exit(ERR_MSG_BIND, 0, errno_string());
285
286 relay_agent_trap_port = ntohs(me.sin_port);
287 write_pid_file(pid_file);
288 }
289
290
dispatcher_loop()291 static void dispatcher_loop()
292 {
293 int numfds;
294 fd_set fdset;
295 int count;
296 struct timeval timeout;
297
298
299 timeout.tv_usec = 0;
300
301 while(1)
302 {
303 if(sighup)
304 {
305 resource_update(config_dir);
306 sighup = False;
307 }
308
309 if(trace_level > 1)
310 {
311 trace_sessions();
312 }
313
314 numfds = 0;
315 FD_ZERO(&fdset);
316
317 numfds = MAX(clients_sd, agents_sd);
318 numfds = MAX(numfds,trap_sd);
319 numfds++;
320 FD_SET(clients_sd, &fdset);
321 FD_SET(agents_sd, &fdset);
322 FD_SET(trap_sd, &fdset);
323
324 timeout.tv_sec = relay_agent_poll_interval;
325
326 /* we compute the timeout according to the */
327 /* timeout of the pending requests */
328 session_select_info(&timeout);
329
330 count = select(numfds, &fdset, 0, 0, &timeout);
331 if(count > 0)
332 {
333 if(FD_ISSET(agents_sd, &fdset))
334 {
335 /* we read the responses of the agents */
336 session_read();
337 continue;
338
339 }
340
341 if(FD_ISSET(trap_sd, &fdset))
342 {
343 /* working on the trap */
344 trap_processing();
345 continue;
346
347 }
348
349 if(FD_ISSET(clients_sd, &fdset))
350 {
351 /* we dispatch the requests of the application */
352 session_dispatch();
353
354 session_timeout();
355 continue;
356 }
357
358 }
359 else
360 {
361 switch(count)
362 {
363 case 0:
364 /* we check if some requests have timeout */
365 session_timeout();
366 watch_dog_in_action();
367 break;
368
369 case -1:
370 if(errno == EINTR)
371 {
372 break;
373 }
374 else
375 {
376 error_exit(ERR_MSG_SELECT, errno_string());
377 }
378 }
379 }
380 }
381 }
382
383
384 /*
385 * \t[-m GROUP | SPLIT (default GROUP)]\n\
386 */
387
print_usage()388 static void print_usage()
389 {
390
391 fprintf(stderr, FGET("%s\n"),
392 MGET("Usage: snmpdx [-h]") );
393 fprintf(stderr, FGET("\t%s\n"),
394 MGET("[-h (to get help on usage)]") );
395 fprintf(stderr, FGET("\t%s%d%s\n"),
396 MGET("[-p port (default "),
397 SNMP_PORT,
398 MGET(")]"));
399
400 fprintf(stderr, FGET("\t%s%s%s\n"),
401 MGET("[-r resource-file (default "),
402 MGET(default_resource_file),
403 MGET(")]"));
404
405 fprintf(stderr, FGET("\t%s%s%s\n"),
406 MGET("[-a access-control-file (default "),
407 MGET(default_sec_config_file),
408 MGET(")]"));
409
410 fprintf(stderr, FGET("\t%s%s%s\n"),
411 MGET("[-c config-dir (default "),
412 MGET(default_config_dir),
413 MGET(")]"));
414
415 fprintf(stderr, FGET("\t%s%s%s\n"),
416 MGET("[-i pid-file (default "),
417 MGET(default_pid_file),
418 MGET(")]"));
419
420 fprintf(stderr, FGET("\t%s%s%s\n"),
421 MGET("[-o enterprise-oid-file (default "),
422 MGET(default_name_oid_file),
423 MGET(")]"));
424
425 fprintf(stderr, FGET("\t%s\n"),
426 MGET("[-y (to invoke recovery module)]"));
427
428 fprintf(stderr, FGET("\t%s\n"),
429 MGET("[-m GROUP | SPLIT (default GROUP)]"));
430
431 fprintf(stderr, FGET("\t%s%d%s%d%s\n\n"),
432 MGET("[-d trace-level (range 0.."),
433 TRACE_LEVEL_MAX,
434 MGET(", default "),
435 trace_level,
436 MGET(")]"));
437
438 exit(1);
439 }
440
441 int
main(int argc,char * argv[])442 main(int argc, char *argv[])
443 {
444 int arg;
445 int Fails ;
446 char *str;
447 int level;
448 char *error_file = NULL;
449
450 extern char *optarg;
451 extern int optind;
452 int opt;
453
454 error_init(argv[0], application_end);
455
456 optind = 1;
457
458
459 {
460 char domain_path[MAXPATHLEN];
461
462 setlocale(LC_ALL, "");
463
464 sprintf(domain_path, SEA_LOCALE_PATH);
465
466 bindtextdomain(DOMAIN_MGET, domain_path);
467 bindtextdomain(DOMAIN_SGET, domain_path);
468 bindtextdomain(DOMAIN_LIBGET, domain_path);
469 bindtextdomain(DOMAIN_LGET, domain_path);
470 bindtextdomain(DOMAIN_FGET, domain_path); /* formatting string */
471 }
472
473
474 /* parse arguments */
475 while((opt = getopt(argc,argv,"c:i:hr:m:o:p:a:d:yf:n:?"))!=EOF){
476 switch(opt){
477 case 'h':
478 case '?':
479 print_usage();
480 break;
481 case 'f':
482 Fails = strtol (optarg, &str, 10) ;
483 if (optarg == str) {
484 fprintf (stderr, "Invalid number following the -t option: %s\n", optarg ) ;
485 print_usage () ;
486 } else {
487 SetFailThreshold (Fails) ;
488 }
489 break ;
490
491
492 case 'y':
493 recovery_on=TRUE;
494 break;
495
496 case 'p':
497 port = strtol(optarg, &str, 10);
498 if(optarg == str)
499 {
500 fprintf(stderr, "Not a valid integer following the -p option: %s\n", optarg);
501 print_usage();
502 }
503
504 break;
505
506 case 'n':
507 relay_agent_name = strdup(optarg);
508 if(relay_agent_name == NULL)
509 {
510 fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
511 exit(1);
512 }
513
514 break;
515
516 case 'o':
517 if(optind > argc)
518 {
519 fprintf(stderr, "must have the enterprise name-oid file\n");
520 print_usage();
521 }
522
523 name_oid_file= strdup(optarg);
524 if(name_oid_file == NULL)
525 {
526 fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
527 exit(1);
528 }
529
530 break;
531
532 case 'c':
533 if(optind > argc)
534 {
535 fprintf(stderr, "Must have a configuration directory name following the -c option\n");
536 print_usage();
537 }
538
539 config_dir = strdup(optarg);
540 if(config_dir == NULL)
541 {
542 fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
543 exit(1);
544 }
545
546 break;
547
548 case 'a':
549 if(optind > argc)
550 {
551 fprintf(stderr, "Must have a access control filename following the -a option\n");
552 print_usage();
553 }
554
555 sec_config_file = strdup(optarg);
556 if(sec_config_file == NULL)
557 {
558 fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
559 exit(1);
560 }
561
562 break;
563
564 case 'r':
565 if(optind > argc)
566 {
567 fprintf(stderr, "Must have a resource file name following the -r option\n");
568 print_usage();
569 }
570
571 resource_file = strdup(optarg);
572 if(resource_file == NULL)
573 {
574 fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
575 exit(1);
576 }
577
578 break;
579
580 case 'i':
581 if(optind > argc)
582 {
583 fprintf(stderr, "Must have a pid file name following the -i option\n");
584 print_usage();
585 }
586
587 pid_file = strdup(optarg);
588 if(pid_file == NULL)
589 {
590 fprintf(stderr, "%s\n", ERR_MSG_ALLOC);
591 exit(1);
592 }
593
594 break;
595
596
597 case 'd':
598 if(optind> argc)
599 {
600 fprintf(stderr, "Must have a trace-level following the -d option\n");
601 print_usage();
602 }
603
604 level = strtol(optarg, &str, 10);
605 if(optarg == str)
606 {
607 fprintf(stderr, "Not a valid integer following the -d option: %s\n", optarg);
608 print_usage();
609 }
610
611 if(trace_set(level, error_label))
612 {
613 print_usage();
614 }
615
616 break;
617
618 case 'm':
619 if(optind > argc)
620 {
621 fprintf(stderr, "Must have GROUP or SPLIT following the -m option\n");
622 print_usage();
623 }
624
625 if(strcmp(optarg, "GROUP") == 0)
626 {
627 mode = MODE_GROUP;
628 }
629 else
630 if(strcmp(optarg, "SPLIT") == 0)
631 {
632 mode = MODE_SPLIT;
633 }
634 else
635 {
636 fprintf(stderr, "Invalid mode: %s\n", optarg);
637 print_usage();
638 }
639
640 break;
641
642 default:
643 fprintf(stderr, "Invalid Option: -%c\n", optarg);
644 print_usage();
645 break;
646 }
647 }
648
649
650 /*
651 if(error_file == NULL)
652 {
653 error_file = default_error_file;
654 }
655 error_open(error_file);
656 */
657
658 if(trace_level == 0)
659 {
660 /* run the daemon in backgound */
661
662 int pid;
663
664 pid = fork();
665 switch(pid)
666 {
667 case -1:
668 error_exit(ERR_MSG_FORK, errno_string());
669 break;
670
671 case 0: /* child process */
672 break;
673
674 default: /* parent process */
675 exit(0);
676 break;
677 }
678 }
679
680 if(fclose(stdin) == EOF)
681 {
682 error(ERR_MSG_FCLOSE, "stdin", errno_string());
683 }
684
685 dispatcher_init();
686
687 if(signals_init(signals_sighup, signals_exit, error_label))
688 {
689 error_exit("signals_init() failed: %s", error_label);
690 }
691
692 if(trace_level == 0)
693 {
694 if(fclose(stdout) == EOF)
695 {
696 error(ERR_MSG_FCLOSE, "stdout", errno_string());
697 }
698 }
699
700 if(trace_level == 0)
701 {
702 /* background */
703
704 if(chdir("/") == -1)
705 {
706 error(ERR_MSG_CHDIR, "/", errno_string());
707 }
708
709 /* set process group ID */
710 setpgrp();
711
712 error_close_stderr();
713 }
714
715 dispatcher_loop();
716
717 return (0);
718 }
719