1 /**************************************************
2 OpengateM - a MAC address authentication system
5 Copyright (C) 2011 Opengate Project Team
6 Written by Yoshiaki Watanabe
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.
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.
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.
22 Email: watanaby@is.saga-u.ac.jp
23 **************************************************/
25 /*************************************
26 This program uses following data structures
27 to maintain the state of each terminal.
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.
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.
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.
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 *************************************/
54 #include "opengatemd.h"
56 void sigHupHandler(int sig);
57 void sigIoHandler(int sig);
58 void sigTermHandler(int sig);
60 int sigHupArrived=FALSE;
61 int sigIoArrived=FALSE;
63 /*********************/
65 /*********************/
66 int main(int argc, char **argv)
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 */
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 */
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 */
92 char clientIpAddress[ADDRMAXLN]; /* udp client ip address */
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;
104 /* if unknown args, show help and exit */
110 /* if '-v' option, show makedir and exit */
112 printf("makedir: %s\n", MAKEDIR);
116 /* drop root privilege */
119 /* prepare config file */
120 if(OpenConfFile()==-1) terminateProg(0);
123 errToSyslog(atoi(GetConfValue("Syslog/Enable")));
124 openlog(GetConfValue("MacCheckDaemon"), LOG_PID, atoi(GetConfValue("Syslog/Facility")));
126 /* initialize config */
129 /* if reloadServiceMode, send HUP to resident daemon */
130 if(reloadServiceMode) ReloadDaemon();
132 /* if stopServiceMode, stop resident deamon and this process */
138 /* if endServiceMode, stop deamon and close sessions after setup */
139 if(endServiceMode) KillDaemon();
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) */
145 /* set lock file to prevent overlapped execution */
146 if(!LockDaemonLockFile()){
150 /* set signal functions */
151 signal(SIGHUP, sigHupHandler);
152 signal(SIGTERM, sigTermHandler);
159 if(!InitMngDb()) terminateProg(0);
161 PrepareUdpPort(sigIoHandler); /* UDP port runs as asynchronous */
163 /* if endService is indicated, close all sessions, and exit */
169 /* set check interval and remove residue sessions that are useless */
170 checkInterval=atoi(GetConfValue("UselessCheckInterval"));
171 uselessCheckTime=time(NULL);
172 DelUselessSessions();
174 /*** enter packet inspection loop ***/
177 /* if sig-hup flag is on, reload this program */
178 if(sigHupArrived)execlp(argv[0], argv[0], NULL);
180 /* if mac addresses are received from management program through udp port,
181 remove the addresses from caches */
184 while(GetDataFromUdpPort(macAddrInUdp, ADDRMAXLN, clientIpAddress)>0){
185 if(IsUdpClientTrusted(clientIpAddress)){
186 DelCacheItem(macAddrInUdp,"");
187 DelMacCacheItem(macAddrInUdp);
192 /* get one packet from pcap */
193 ret=GetNextPacketFromPcap(macAndIpAddressRaw, &addrLen, &ttl);
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();
205 /* and return to loop top */
209 /* ignore not-ip packet */
210 if(ret==-1) continue;
212 /* ignore local packet */
215 /* ignore the packet checked recently */
216 if( IsRecentlyCheckedAddress(macAndIpAddressRaw, addrLen) ) continue;
218 /**** only cache timeout packets proceeds to below ****/
220 /* convert address from network-raw form to presentation form */
221 ConvertMacFromRawToDisplay(macAndIpAddressRaw, macAddress);
222 ConvertIpFromRawToDisplay(macAndIpAddressRaw+MACADDRLN,
223 addrLen-MACADDRLN, ipAddress);
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);
230 /*** get the status of the terminal from session table and DB ***/
232 /* search the address in session table */
233 sessionFound = IsMatchedSessionFound(macAddress);
235 /* search the address in cache of MAC DB */
236 macFound = QueryMacFromMacCache(macAddress, userId, extraId);
238 /* if accessing cache of MAC DB is failed, access MAC DB */
240 macFound = QueryMacFromMngDb(macAddress, userId, extraId);
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);
248 /*** depending the states, add/del/renew the session ***/
250 /* if valid mac and no session, start session */
251 if(macFound && !sessionFound){
252 AddSession(macAddress, userId, extraId);
254 /* save MAC and IP address pair */
255 SetMacIpPair(macAddress, ipAddress, userId, extraId);
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);
264 /* if valid mac and started session, renew check time */
265 if(macFound && sessionFound){
267 /* in normal case, ipfw rule exists. */
268 if(IsMacAddressFoundInIpfw(macAddress)) RenewSession(macAddress);
270 /* when no ipfw rule exists, reset the session */
272 DelSession(macAddress);
273 AddSession(macAddress, userId, extraId);
276 /* save MAC and IP address pair */
277 /* only when new pair is found. */
278 SetMacIpPair(macAddress, ipAddress, userId, extraId);
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();
289 /* clear data structures (can't reach here, but coded for debugging) */
300 /********************************
301 open and lock proc lock file
302 to prevent overlapped daemon exec
303 ********************************/
304 int lockDaemonLockFile(void){
309 /* open process lock file */
310 lockFd = open(GetConfValue("DaemonLockFile"), O_RDWR|O_CREAT, 0644);
312 /* if cannot open or cannot lock, return false */
314 err_msg("ERR at %s#%d: cannot open daemon lock file:%s",__FILE__,__LINE__,
318 if(lockf(lockFd, F_TLOCK, 0)<0) return FALSE;
321 snprintf(str, WORDMAXLN, "%d", getpid());
322 write(lockFd, str, strlen(str)+1);
324 /* normally locked */
329 /******************************
330 daemonize the process
331 ******************************/
332 void daemonize(void){
337 /* detach from parent */
340 err_msg("ERR at %s#%d: cannot fork",__FILE__,__LINE__);
344 /* parent process exits */
347 /* child process runs from here */
348 /* set new prosess group */
351 /* close all file descriptors */
352 for(i=getdtablesize(); i>=0; i--) close(i);
354 /* connect stdin/out/err to null */
355 i=open("/dev/null", O_RDWR); dup(i); dup(i);
357 /* set newly created file permission */
360 if(debug>0) err_msg("INFO: Deamon started");
363 /****************************************
365 ****************************************/
366 void showHelp(char* procName){
368 printf("firewall control by mac address\n");
369 printf(" see detail in Opengate Homepage\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");
379 /*********************************
380 signal handler for SIGIO
381 *********************************/
382 void sigIoHandler(int sig){
387 /*********************************
388 signal handler for SIGHUP
389 *********************************/
390 void sigHupHandler(int sig){
392 if(debug>0)err_msg("INFO: Deamon receives HUP signal");
396 /*********************************
397 signal handler for SIGTERM
398 *********************************/
399 void sigTermHandler(int sig){
401 if(debug>0)err_msg("INFO: Deamon receives TERM signal");
405 /*********************************
407 *********************************/
408 void killDaemon(void){
414 /* get lock file name */
415 lockFileMd=GetConfValue("DaemonLockFile");
417 /* if lock file is not exists, skip */
418 if(stat(lockFileMd, &st)!=0){
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__
428 else if(fscanf(file, "%d", &pid)==0){
429 err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
432 if(file!=NULL) fclose(file);
434 /* send kill signal to the pid process */
436 seteuid(0); /* get root privilege */
438 seteuid(getuid()); /* drop root privilege */
441 /* remove the lockfile */
445 /*********************************
446 reload daemon process
447 *********************************/
448 void reloadDaemon(void){
454 /* get lock file name */
455 lockFileMd=GetConfValue("DaemonLockFile");
457 /* if lock file is not exists, skip */
458 if(stat(lockFileMd, &st)!=0){
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__
468 else if(fscanf(file, "%d", &pid)==0){
469 err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
472 if(file!=NULL)fclose(file);
474 /* send hup signal to the pid process */
476 seteuid(0); /* get root privilege */
478 seteuid(getuid()); /* drop root privilege */
482 /*************************************
483 put out end message and exit
484 *************************************/
485 void terminateProg(int ret){
487 /* close opengatemd.db */
490 if(debug>0) err_msg("INFO: Terminated");
495 /************************************
496 routines for debugging output
497 ***********************************/
498 int LockDaemonLockFile(void){
500 if(debug>1) err_msg("DEBUG:=>lockDaemonLockFile( )");
501 ret = lockDaemonLockFile();
502 if(debug>1) err_msg("DEBUG:(%d)<=lockDaemonLockFile( )",ret);
506 void Daemonize(void){
507 if(debug>1) err_msg("DEBUG:=>daemonize( )");
509 if(debug>1) err_msg("DEBUG:<=daemonize( )");
512 void ShowHelp(char* procName){
513 if(debug>1) err_msg("DEBUG:=>showHelp(%s)", procName);
515 if(debug>1) err_msg("DEBUG:<=showhelp( )");
517 void KillDaemon(void){
518 if(debug>1) err_msg("DEBUG:=>killDaemon( )");
520 if(debug>1) err_msg("DEBUG:<=killDaemon( )");
522 void ReloadDaemon(void){
523 if(debug>1) err_msg("DEBUG:=>reloadDaemon( )");
525 if(debug>1) err_msg("DEBUG:<=reloadDaemon( )");