OSDN Git Service

c5cf6f75068fd438e306333c74d678e850fa18c9
[opengatem/opengatem.git] / mdsrc / opengatemd.c
1 /**************************************************
2 OpengateM - a MAC address authentication system
3 daemon main module 
4
5 Copyright (C) 2011 Opengate Project Team
6 Written by Yoshiaki Watanabe
7
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 as published by the Free Software Foundation; either version 2
11 of the License, or (at your option) any later version.
12
13 This program is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21
22 Email: watanaby@is.saga-u.ac.jp
23 **************************************************/
24
25 /*************************************
26 This program uses following data structures 
27 to maintain the state of each terminal.
28
29    1. Packet Check Cache
30        To speed up the packet check process, the address checked once 
31          is ignored for a while.
32        The cache (key: MAC&IP address pair) is used to decide 
33          the necessity of checking.
34        The cache is maintained the information of arrived packets 
35          by using hash-table and queue in the memory of local machine.
36    2. Session Table
37        The table (key: MAC address) maintains temporal information 
38          of terminals allowing the use of network now. 
39        The data are stored in the DB of local machine.
40    3. MAC address Table
41        The table (key: MAC address) maintains MAC addresses of terminals
42          and the owners' information.
43        The data are stored in the DB of a central machine
44          and accessed via network.
45    4. Cache of MAC address Table
46        As the network DB access is time-consuming, the access cache 
47          (key: MAC address) is maintained in the memory of local machine.
48
49 MAC address is used as main key for the user terminal.
50 IP address is kept only for information.
51 The packet cache uses the MAC-IP pair, as to record all IP. 
52 *************************************/
53
54 #include        "opengatemd.h"
55
56 void sigHupHandler(int sig);
57 void sigIoHandler(int sig);
58 void sigTermHandler(int sig);
59
60 int sigHupArrived=FALSE;
61 int sigIoArrived=FALSE;
62
63 /*********************/
64 /*  main routine     */
65 /*********************/
66 int  main(int argc, char **argv)
67 {
68   char ipAddress[ADDRMAXLN];  /* packet source ip address */
69   char macAddress[ADDRMAXLN]; /* packet source mac address */
70     unsigned char macAndIpAddressRaw[ADDRMAXLN];/* mac&ip addr in raw form */
71   /* above is network raw binary concatenating MAC(6bytes) and IP(4or16Bytes) */
72   int addrLen;               /* ip address byte length 6+4 or 6+16 */         
73
74   char userId[USERMAXLN];     /* user id related to the mac address */
75   char extraId[USERMAXLN];    /* optional id for the user */
76   int macFound;               /* flag: mac address is resistered in db */
77   int sessionFound;           /* flag: session for the address exists */
78   int consoleMode=FALSE;      /* flag: start with console mode option */
79   int endServiceMode=FALSE;   /* flag: start with end service option */
80   int reloadServiceMode=FALSE;/* flag: start with reload option */ 
81   int stopServiceMode=FALSE;  /* flag: start with stop service option */ 
82   int showVersionMode=FALSE;  /* flag: show version */
83   int helpMode=FALSE;         /* flag: start with help mode */
84
85   int ttl;                    /* packet ttl(time to live) or hlim(hop limit) */
86   int i;                      /* for loop control */
87   int uselessCheckTime=0;     /* the last time for useless check */
88   int checkInterval;          /* useless session check interval */
89   int isNatOrRouter=FALSE;    /* the packet is sent from nat/router */
90   char macAddrInUdp[ADDRMAXLN]; /* mac address sent from udp port */
91   int ret;
92   char clientIpAddress[ADDRMAXLN]; /* udp client ip address */
93
94   /* analyze arguments */
95   for(i=1; i<argc; i++){
96     if(*argv[i]=='-' && *(argv[i]+1)=='c') consoleMode=TRUE;
97     else if(*argv[i]=='-' && *(argv[i]+1)=='e') endServiceMode=TRUE;
98     else if(*argv[i]=='-' && *(argv[i]+1)=='r') reloadServiceMode=TRUE;
99     else if(*argv[i]=='-' && *(argv[i]+1)=='s') stopServiceMode=TRUE;
100     else if(*argv[i]=='-' && *(argv[i]+1)=='v') showVersionMode=TRUE;
101     else helpMode=TRUE;
102   }
103
104   /* if unknown args, show help and exit */
105   if(helpMode){
106     ShowHelp(argv[0]);
107     terminateProg(0);
108   }
109
110   /* if '-v' option, show makedir and exit */  
111   if(showVersionMode){
112     printf("makedir: %s\n", MAKEDIR);
113     terminateProg(0);
114   }
115
116   /* drop root privilege */
117   seteuid(getuid());
118
119   /* prepare config file */
120   if(OpenConfFile()==-1) terminateProg(0);
121  
122   /* start log */
123   errToSyslog(atoi(GetConfValue("Syslog/Enable")));
124   openlog(GetConfValue("MacCheckDaemon"), LOG_PID, atoi(GetConfValue("Syslog/Facility")));
125
126   /* initialize config */
127   InitConf();
128
129   /* if reloadServiceMode, send HUP to resident daemon */
130   if(reloadServiceMode) ReloadDaemon();
131
132   /* if stopServiceMode, stop resident deamon and this process */
133   if(stopServiceMode){
134     KillDaemon();
135     terminateProg(0);
136   }
137
138   /* if endServiceMode, stop deamon and close sessions after setup */
139   if(endServiceMode) KillDaemon();
140
141   /* get runmode from command argument and set as daemon */
142   if(consoleMode)  errToSyslog(FALSE);    /* console mode (no syslog) */
143   else   Daemonize();                     /* daemon mode (fork&exit) */
144
145   /* set lock file to prevent overlapped execution */
146   if(!LockDaemonLockFile()){
147     terminateProg(0);
148   }
149
150   /* set signal functions */
151   signal(SIGHUP, sigHupHandler);
152   signal(SIGTERM, sigTermHandler);
153
154   /* initializing */
155   InitPcap();
156   InitCache();
157   InitMacCache();
158   InitWorkDb();
159   if(!InitMngDb()) terminateProg(0);
160   InitTtlCheck();
161   PrepareUdpPort(sigIoHandler); /* UDP port runs as asynchronous */
162
163   /* if endService is indicated, close all sessions, and exit */
164   if(endServiceMode){
165     DelAllSessions();
166     terminateProg(0);
167   }
168   
169   /* set check interval and remove residue sessions that are useless */
170   checkInterval=atoi(GetConfValue("UselessCheckInterval"));
171   uselessCheckTime=time(NULL);
172   DelUselessSessions();
173
174   /*** enter packet inspection loop ***/
175   while(1){
176
177     /* if sig-hup flag is on, reload this program */
178     if(sigHupArrived)execlp(argv[0], argv[0], NULL);
179
180     /* if mac addresses are received from management program through udp port, 
181        remove the addresses from caches */
182     if(sigIoArrived){
183       sigIoArrived=FALSE;
184       while(GetDataFromUdpPort(macAddrInUdp, ADDRMAXLN, clientIpAddress)>0){
185         if(IsUdpClientTrusted(clientIpAddress)){
186           DelCacheItem(macAddrInUdp,"");
187           DelMacCacheItem(macAddrInUdp);
188         }
189       }
190     }      
191
192     /* get one packet from pcap */
193     ret=GetNextPacketFromPcap(macAndIpAddressRaw, &addrLen, &ttl);
194
195     /* if no packet */
196     if(ret==0){
197
198       /* check&delete useless sessions */
199       /* when long time passed from previous check */
200       if( time(NULL) - uselessCheckTime > checkInterval ){
201         uselessCheckTime = time(NULL);
202         DelUselessSessions();
203       }
204
205       /* and return to loop top */
206       continue;
207     }
208
209     /* ignore not-ip packet */
210     if(ret==-1) continue;
211
212     /* ignore local packet */
213     if(ttl<=1) continue;
214    
215     /* ignore the packet checked recently */
216     if( IsRecentlyCheckedAddress(macAndIpAddressRaw, addrLen) ) continue;
217
218     /**** only cache timeout packets proceeds to below ****/
219
220     /* convert address from network-raw form to presentation form */
221     ConvertMacFromRawToDisplay(macAndIpAddressRaw, macAddress);
222     ConvertIpFromRawToDisplay(macAndIpAddressRaw+MACADDRLN,
223                               addrLen-MACADDRLN, ipAddress);
224
225     /* check nat/router and save info to db */
226     isNatOrRouter=IsSentViaNatOrRouter(ipAddress, macAddress, ttl);
227     if(isNatOrRouter) PutLogAtNatOrRouter(isNatOrRouter,ipAddress,macAddress,ttl);
228     PutMacInfoToWorkDb(macAddress, ttl, isNatOrRouter);
229
230     /*** get the status of the terminal from session table and DB ***/
231
232     /* search the address in session table */
233     sessionFound = IsMatchedSessionFound(macAddress);
234
235     /* search the address in cache of MAC DB */ 
236     macFound = QueryMacFromMacCache(macAddress, userId, extraId);
237
238     /* if accessing cache of MAC DB is failed, access MAC DB */
239     if(macFound==ERROR){
240       macFound = QueryMacFromMngDb(macAddress, userId, extraId);
241       
242       /* if db access is failed, set not-found and retry next time */
243       /* if db access is successed (found or not), save it to cache */ 
244       if(macFound==ERROR) macFound=FALSE;
245       else AddMacCacheItem(macAddress, userId, extraId, macFound);
246     }
247
248     /*** depending the states, add/del/renew the session ***/
249
250     /* if valid mac and no session, start session */
251     if(macFound && !sessionFound){
252         AddSession(macAddress, userId, extraId);
253
254         /* save MAC and IP address pair */
255         SetMacIpPair(macAddress, ipAddress, userId, extraId);
256     }
257
258     /* if no mac and started session, stop session */
259     /* (MAC and IP pairs are removed in stop session) */
260     if(!macFound && sessionFound){
261       DelSession(macAddress);
262     }
263
264     /* if valid mac and started session, renew check time */
265     if(macFound && sessionFound){
266
267       /* in normal case, ipfw rule exists. */
268       if(IsMacAddressFoundInIpfw(macAddress)) RenewSession(macAddress);
269
270       /* when no ipfw rule exists, reset the session */
271       else{
272         DelSession(macAddress);
273         AddSession(macAddress, userId, extraId);        
274       }
275
276       /* save MAC and IP address pair */
277       /* only when new pair is found.  */
278       SetMacIpPair(macAddress, ipAddress, userId, extraId);
279     }
280
281     /*  check useless sessions at some interval */
282     /* (MAC and IP pairs are removed in stop session) */
283     if( time(NULL) - uselessCheckTime > checkInterval ){
284       uselessCheckTime = time(NULL);
285       DelUselessSessions();
286     }
287   }
288
289   /* clear data structures (can't reach here, but coded for debugging) */
290   FreeCache();
291   FreeMacCache();
292   ClosePcap();
293   CloseConfFile();
294   CloseMngDb();
295
296   terminateProg(0);
297   return 0;
298 }
299
300 /********************************
301  open and lock proc lock file 
302  to prevent overlapped daemon exec
303 ********************************/
304 int lockDaemonLockFile(void){
305   
306   static int lockFd;
307   char str[WORDMAXLN];
308  
309   /* open process lock file */
310   lockFd = open(GetConfValue("DaemonLockFile"), O_RDWR|O_CREAT, 0644);
311   
312   /* if cannot open or cannot lock, return false */
313   if(lockFd<0){
314     err_msg("ERR at %s#%d: cannot open daemon lock file:%s",__FILE__,__LINE__,
315             lockFd);
316     return FALSE;
317   }
318   if(lockf(lockFd, F_TLOCK, 0)<0) return FALSE;
319
320   /* record pid */
321   snprintf(str, WORDMAXLN, "%d", getpid());
322   write(lockFd, str, strlen(str)+1);
323
324   /* normally locked */
325   return TRUE;
326 }
327
328
329 /******************************
330 daemonize the process
331 ******************************/
332 void daemonize(void){
333
334   int pid;
335   int i;
336
337   /* detach from parent */
338   pid = fork();
339   if(pid < 0){
340    err_msg("ERR at %s#%d: cannot fork",__FILE__,__LINE__);
341     terminateProg(0);
342   }
343
344   /* parent process exits */
345   if(pid > 0) exit(0);
346
347   /* child process runs from here */
348   /* set new prosess group */
349   setsid();
350
351   /* close all file descriptors */
352   for(i=getdtablesize(); i>=0; i--) close(i);
353
354   /* connect stdin/out/err to null */
355   i=open("/dev/null", O_RDWR); dup(i); dup(i);
356
357   /* set newly created file permission */
358   /* umask(033);   */
359
360   if(debug>0) err_msg("INFO: Deamon started");
361 }
362
363 /****************************************
364 show help message
365 ****************************************/
366 void showHelp(char* procName){
367    printf("\n");
368    printf("firewall control by mac address\n");
369    printf(" see detail in Opengate Homepage\n");
370    printf("\n");
371    printf(" format: %s [arg] \n", procName);
372    printf(" arg : -c = run on console (default is daemon)\n");
373    printf("     : -e = close all sessions and end service\n");
374    printf("     : -r = reload deamon\n");
375    printf("     : -s = stop deamon (not close sessions)\n");
376    printf("     : -v = show make dir to check version\n");
377  }
378
379 /*********************************
380 signal handler for SIGIO
381 *********************************/
382 void sigIoHandler(int sig){
383
384   sigIoArrived=TRUE;
385 }
386
387 /*********************************
388 signal handler for SIGHUP
389 *********************************/
390 void sigHupHandler(int sig){
391
392   if(debug>0)err_msg("INFO: Deamon receives HUP signal");
393   sigHupArrived=TRUE;
394 }
395
396 /*********************************
397 signal handler for SIGTERM
398 *********************************/
399 void sigTermHandler(int sig){
400
401   if(debug>0)err_msg("INFO: Deamon receives TERM signal");
402   terminateProg(0);
403 }
404
405 /*********************************
406 kill daemon process
407 *********************************/
408 void killDaemon(void){
409   FILE* file=NULL;
410   struct stat st;
411   int pid=0;
412   char* lockFileMd;
413
414   /* get lock file name */
415   lockFileMd=GetConfValue("DaemonLockFile");
416
417   /* if lock file is not exists, skip */
418   if(stat(lockFileMd, &st)!=0){
419     ;
420   }
421
422   /* read pid from the file */
423   else if((file=fopen(lockFileMd, "r"))==NULL){
424     err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
425             ,lockFileMd);
426   }
427
428   else if(fscanf(file, "%d", &pid)==0){
429     err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
430             ,lockFileMd);
431   }
432   if(file!=NULL) fclose(file);
433
434   /* send kill signal to the pid process */
435   if(pid!=0){
436     seteuid(0);   /* get root privilege */
437     kill(pid, SIGKILL);
438     seteuid(getuid());   /* drop root privilege */
439   }
440
441   /* remove the lockfile */
442   remove(lockFileMd);
443 }
444
445 /*********************************
446 reload daemon process
447 *********************************/
448 void reloadDaemon(void){
449   FILE* file=NULL;
450   struct stat st;
451   int pid=0;
452   char* lockFileMd;
453
454   /* get lock file name */
455   lockFileMd=GetConfValue("DaemonLockFile");
456
457   /* if lock file is not exists, skip */
458   if(stat(lockFileMd, &st)!=0){
459     ;
460   }
461
462   /* read pid from the file */
463   else if((file=fopen(lockFileMd, "r"))==NULL){
464     err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
465             ,lockFileMd);
466   }
467
468   else if(fscanf(file, "%d", &pid)==0){
469     err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
470             ,lockFileMd);
471   }
472   if(file!=NULL)fclose(file);
473
474   /* send hup signal to the pid process */
475   if(pid!=0){
476     seteuid(0);   /* get root privilege */
477     kill(pid, SIGHUP);
478     seteuid(getuid());   /* drop root privilege */
479   }
480 }
481
482 /*************************************
483 put out end message and exit 
484 *************************************/
485 void terminateProg(int ret){
486
487   /* close opengatemd.db */
488   FinalizeWorkDb();
489
490   if(debug>0) err_msg("INFO: Terminated");
491   exit(ret);
492 }
493
494
495 /************************************
496  routines for debugging output
497  ***********************************/
498 int LockDaemonLockFile(void){
499   int ret;
500   if(debug>1) err_msg("DEBUG:=>lockDaemonLockFile( )");
501   ret = lockDaemonLockFile();
502   if(debug>1) err_msg("DEBUG:(%d)<=lockDaemonLockFile( )",ret);
503   return ret;
504 }
505
506 void Daemonize(void){
507   if(debug>1) err_msg("DEBUG:=>daemonize( )");
508   daemonize();
509   if(debug>1) err_msg("DEBUG:<=daemonize( )");
510 }
511
512 void ShowHelp(char* procName){
513   if(debug>1) err_msg("DEBUG:=>showHelp(%s)", procName);
514   showHelp(procName);
515   if(debug>1) err_msg("DEBUG:<=showhelp( )");
516 }
517 void KillDaemon(void){
518   if(debug>1) err_msg("DEBUG:=>killDaemon( )");
519   killDaemon();
520   if(debug>1) err_msg("DEBUG:<=killDaemon( )");
521 }
522 void ReloadDaemon(void){
523   if(debug>1) err_msg("DEBUG:=>reloadDaemon( )");
524   reloadDaemon();
525   if(debug>1) err_msg("DEBUG:<=reloadDaemon( )");
526 }