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 to maintain the state of each terminal.
27 * MAC address is used as the main key for recognizing the user terminal.
28 * IP address is kept only for information.
29 * The packet cache uses the MAC-IP pair, as to record all IPs.
32 To speed up the packet check process, the address checked once
33 is ignored for a while.
34 The cache (key: MAC&IP address pair) is used to decide
35 the necessity of checking.
36 The cache maintains the information of arrived packets
37 by using hash-table and queue in the memory of the local machine.
39 The table (key: MAC address) maintains temporal information
40 of terminals allowing the use of network at now.
41 The data are stored in the work DB(SQLite3) on the local machine
42 (table:sessionmd in db:opengatemd.db).
44 The table maintains the MAC-IP pairs where an active session has the MAC
45 (One MAC corresponds to plural IPs(ipv4 and ipv6))
46 The data are stored in the work DB on the local machine.
47 (table:macippair in db:opengatemd.db)
48 The mac-ip pair table in management db is used only for logging.
49 4. MAC address registration Table
50 The table (key: MAC address) maintains MAC addresses of terminals
51 and the owners' information.
52 The data are stored in the management DB(MySQL) on a central machine
53 and accessed via network.
54 (table: macaddrs in db:opengatem)
55 5. Cache of MAC address registration Table
56 As the network DB access is time-consuming, the access cache
57 (key: MAC address) is maintained in the memory of the local machine.
60 *************************************/
62 #include "opengatemd.h"
64 void sigHupHandler(int sig);
65 void sigIoHandler(int sig);
66 void sigTermHandler(int sig);
68 int sigHupArrived=FALSE;
69 int sigIoArrived=FALSE;
71 /*********************/
73 /*********************/
74 int main(int argc, char **argv)
76 char ipAddress[ADDRMAXLN]; /* packet source ip address */
77 char macAddress[ADDRMAXLN]; /* packet source mac address */
78 unsigned char macAndIpAddressRaw[ADDRMAXLN];/* mac&ip addr in raw form */
79 /* above is network raw binary concatenating MAC(6bytes) and IP(4or16Bytes) */
80 int addrLen; /* ip address byte length 6+4 or 6+16 */
82 char userId[USERMAXLN]; /* user id related to the mac address */
83 char extraId[USERMAXLN]; /* optional id for the user */
84 int macFound; /* flag: mac address is resistered in db */
85 int sessionFound; /* flag: session for the address exists */
86 int consoleMode=FALSE; /* flag: start with console mode option */
87 int endServiceMode=FALSE; /* flag: start with end service option */
88 int reloadServiceMode=FALSE;/* flag: start with reload option */
89 int stopServiceMode=FALSE; /* flag: start with stop service option */
90 int showVersionMode=FALSE; /* flag: show version */
91 int helpMode=FALSE; /* flag: start with help mode */
93 int ttl; /* packet ttl(time to live) or hlim(hop limit) */
94 int i; /* for loop control */
95 int uselessCheckTime=0; /* the last time for useless check */
96 int checkInterval; /* useless session check interval */
97 int isNatOrRouter=FALSE; /* the packet is sent from nat/router */
98 char macAddrInUdp[ADDRMAXLN]; /* mac address sent from udp port */
99 int ret; /* return code */
100 char clientIpAddress[ADDRMAXLN]; /* udp client ip address */
102 /* analyze arguments */
103 for(i=1; i<argc; i++){
104 if(*argv[i]=='-' && *(argv[i]+1)=='c') consoleMode=TRUE;
105 else if(*argv[i]=='-' && *(argv[i]+1)=='e') endServiceMode=TRUE;
106 else if(*argv[i]=='-' && *(argv[i]+1)=='r') reloadServiceMode=TRUE;
107 else if(*argv[i]=='-' && *(argv[i]+1)=='s') stopServiceMode=TRUE;
108 else if(*argv[i]=='-' && *(argv[i]+1)=='v') showVersionMode=TRUE;
112 /* if unknown args, show help and exit */
118 /* if '-v' option, show 'makedir' and exit */
119 /* makedir is the directory where the make command is executed */
120 /* the directory can include the string such as 'opengatem0.9.9-trial'. */
122 printf("makedir: %s\n", MAKEDIR);
126 /* drop root privilege */
129 /* prepare config file */
130 if(OpenConfFile()==-1) terminateProg(0);
133 errToSyslog(atoi(GetConfValue("Syslog/Enable")));
134 openlog(GetConfValue("MacCheckDaemon"), LOG_PID, atoi(GetConfValue("Syslog/Facility")));
136 /* initialize config */
139 /* if reloadServiceMode, send HUP to resident daemon */
140 if(reloadServiceMode) ReloadDaemon();
142 /* if stopServiceMode, stop resident deamon and this process */
148 /* if endServiceMode, stop deamon */
149 /* and close sessions(about 30-lines below in this source) */
150 if(endServiceMode) KillDaemon();
152 /* if not console mode, run as daemon */
153 if(consoleMode) errToSyslog(FALSE); /* console mode (no syslog) */
154 else Daemonize(); /* daemon mode (fork&exit) */
156 /* set lock file to prevent overlapped execution */
157 if(!LockDaemonLockFile()){
161 /* set signal functions */
162 signalx(SIGHUP, sigHupHandler);
163 signalx(SIGTERM, sigTermHandler);
170 if(!InitMngDb()) terminateProg(0);
172 InitWatchlistCache();
173 PrepareUdpPort(sigIoHandler); /* UDP port runs in asynchronous mode */
175 /* if endServiceMode is indicated, close all sessions, and exit */
181 /* set check interval and remove useless residue sessions */
182 checkInterval=atoi(GetConfValue("UselessCheckInterval"));
183 uselessCheckTime=time(NULL);
184 DelUselessSessions();
185 DelOldMacInfoInWorkDb();
186 DelOldSessionLogInMngDb();
188 /*** enter infinite loop of packet inspection ***/
192 /* if sig-hup flag is on, reload this program */
193 /* sig-hup flag is set in sigHupHandler, when HUP signal arrives */
194 if(sigHupArrived)execlp(argv[0], argv[0], NULL);
196 /* if sig-IO flag is on, get mac addresses from UDP port */
197 /* and remove the addresses from caches (renew info at next capture) */
198 /* sig-IO flag is on in sigIoHandler, when a packet arrives. */
199 /* the packet includes renewed mac addresses sent from management program. */
202 while(GetDataFromUdpPort(macAddrInUdp, ADDRMAXLN, clientIpAddress)>0){
203 if(IsUdpClientTrusted(clientIpAddress)){
204 DelCacheItem(macAddrInUdp,"");
205 DelMacCacheItem(macAddrInUdp);
210 /* get one packet from pcap */
211 ret=GetNextPacketFromPcap(macAndIpAddressRaw, &addrLen, &ttl);
216 /* when long time passed from previous check, */
217 /* check&delete useless sessions and old macinfo */
218 if( time(NULL) - uselessCheckTime > checkInterval ){
219 uselessCheckTime = time(NULL);
220 DelUselessSessions();
221 DelOldMacInfoInWorkDb();
222 DelOldSessionLogInMngDb();
225 /* and return to loop top */
229 /* ignore not-ip packet */
230 if(ret==-1) continue;
232 /* ignore local packet */
235 /* ignore the packet tha is checked recently */
236 if( IsRecentlyCheckedAddress(macAndIpAddressRaw, addrLen) ) continue;
238 /**** no more processing for recently checked packets ****/
239 /**** only cache timeout packets proceeds to below ****/
241 /* convert address from network-raw form to presentation form */
242 ConvertMacFromRawToDisplay(macAndIpAddressRaw, macAddress);
243 ConvertIpFromRawToDisplay(macAndIpAddressRaw+MACADDRLN,
244 addrLen-MACADDRLN, ipAddress);
246 /* if the address is included in watchlist, report the detection */
247 /* watchlist is the address list needing specific reporting */
248 /* (e.g., suspicion of illegal access). no need for normal operation */
249 if(IsFoundInWatchlistCache(macAddress)==TRUE){
250 err_msg_warn("WARN: find mac=%s ip=%s", macAddress, ipAddress);
253 /* check NAT/Router and save info to db */
254 /* when NAT/Router is inserted, the acquired MAC is the address of NAT/Router */
255 /* thus the MAC based control is failed */
256 /* at now, not denying the packet but reporting the detection. */
257 isNatOrRouter=IsSentViaNatOrRouter(ipAddress, macAddress, ttl);
258 if(isNatOrRouter) PutLogAtNatOrRouter(isNatOrRouter,ipAddress,macAddress,ttl);
259 PutMacInfoToWorkDb(macAddress, ttl, isNatOrRouter);
261 /*** get the status of the terminal from session table and DB ***/
263 /* search the captured address in session table */
264 sessionFound = IsMatchedSessionFound(macAddress);
266 /* search the captured address in cache of MAC DB */
267 macFound = QueryMacFromMacCache(macAddress, userId, extraId);
269 /* if failed, access MAC DB */
271 macFound = QueryMacFromMngDb(macAddress, userId, extraId);
273 /* if db access is failed, set not-found and retry at next capture */
274 /* if db access is successed (found or not-found), save it to cache */
275 if(macFound==ERROR) macFound=FALSE;
276 else AddMacCacheItem(macAddress, userId, extraId, macFound);
279 /*** depending the status, add/del/renew the session ***/
281 /* if valid mac and no session, start session */
282 if(macFound && !sessionFound){
283 AddSession(macAddress, userId, extraId);
285 /* save MAC and IP address pair to work db */
286 SetMacIpPair(macAddress, ipAddress, userId, extraId);
289 /* if no mac and started session, stop session */
290 /* (MAC-IP pair in work db is removed in delSession) */
291 if(!macFound && sessionFound){
292 DelSession(macAddress);
295 /* if valid mac and started session, renew check time */
296 if(macFound && sessionFound){
298 /* in normal case, ipfw rule exists. */
299 if(IsMacAddressFoundInIpfw(macAddress)){
300 RenewSession(macAddress);
303 /* when no ipfw rule exists, reset the session */
305 DelSession(macAddress);
306 AddSession(macAddress, userId, extraId);
309 /* save MAC-IP pair to work db (new ip is found for a mac in session). */
310 SetMacIpPair(macAddress, ipAddress, userId, extraId);
313 /* check useless sessions and old macinfo at some interval */
314 if( time(NULL) - uselessCheckTime > checkInterval ){
315 uselessCheckTime = time(NULL);
316 DelUselessSessions();
317 DelOldMacInfoInWorkDb();
318 DelOldSessionLogInMngDb();
321 /*** end of infinite loop ***/
323 /* clear data structures (can't reach here, but coded for debugging) */
326 FreeWatchlistCache();
335 /********************************
336 open and lock proc lock file
337 to prevent overlapped daemon exec
338 ********************************/
339 int lockDaemonLockFile(void){
344 /* open process lock file */
345 lockFd = open(GetConfValue("DaemonLockFile"), O_RDWR|O_CREAT, 0644);
347 /* if cannot open or cannot lock, return false */
349 err_msg("ERR at %s#%d: cannot open daemon lock file:%s",__FILE__,__LINE__,
353 if(lockf(lockFd, F_TLOCK, 0)<0) return FALSE;
356 snprintf(str, WORDMAXLN, "%d", getpid());
357 write(lockFd, str, strlen(str)+1);
359 /* normally locked */
364 /******************************
365 daemonize the process
366 ******************************/
367 void daemonize(void){
372 /* detach from parent */
375 err_msg("ERR at %s#%d: cannot fork",__FILE__,__LINE__);
379 /* parent process exits */
382 /* child process runs from here */
383 /* set new prosess group */
386 /* close all file descriptors */
387 for(i=getdtablesize(); i>=0; i--) close(i);
389 /* connect stdin/out/err to null */
390 i=open("/dev/null", O_RDWR); dup(i); dup(i);
392 /* set newly created file permission */
395 if(debug>0) err_msg("INFO: Deamon started");
398 /****************************************
400 ****************************************/
401 void showHelp(char* procName){
403 printf("firewall control by mac address\n");
404 printf(" see detail in Opengate Homepage\n");
406 printf(" format: %s [arg] \n", procName);
407 printf(" arg : -c = run on console (run as daemon in default)\n");
408 printf(" : -e = close all sessions and end service\n");
409 printf(" : -r = reload deamon(not close sessions)\n");
410 printf(" : -s = stop deamon (not close sessions)\n");
411 printf(" : -v = show make dir to check version\n");
414 /*********************************
415 signal handler for SIGIO
416 *********************************/
417 void sigIoHandler(int sig){
422 /*********************************
423 signal handler for SIGHUP
424 *********************************/
425 void sigHupHandler(int sig){
427 if(debug>0)err_msg("INFO: Deamon receives HUP signal");
431 /*********************************
432 signal handler for SIGTERM
433 *********************************/
434 void sigTermHandler(int sig){
436 if(debug>0)err_msg("INFO: Deamon receives TERM signal");
440 /*********************************
442 *********************************/
443 void killDaemon(void){
449 /* get lock file name */
450 lockFileMd=GetConfValue("DaemonLockFile");
452 /* if lock file does not exist, skip */
453 if(stat(lockFileMd, &st)!=0){
457 /*else (file exists), read pid from the file */
458 else if((file=fopen(lockFileMd, "r"))==NULL){
459 err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
462 else if(fscanf(file, "%d", &pid)==0){
463 err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
466 if(file!=NULL) fclose(file);
468 /* send kill signal to the 'pid' process */
470 seteuid(0); /* get root privilege */
472 seteuid(getuid()); /* drop root privilege */
475 /* remove the lockfile */
479 /*********************************
480 reload daemon process
481 *********************************/
482 void reloadDaemon(void){
488 /* get lock file name */
489 lockFileMd=GetConfValue("DaemonLockFile");
491 /* if lock file does not exists, skip */
492 if(stat(lockFileMd, &st)!=0){
496 /* else (file exists), read pid from the file */
497 else if((file=fopen(lockFileMd, "r"))==NULL){
498 err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
501 else if(fscanf(file, "%d", &pid)==0){
502 err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
505 if(file!=NULL)fclose(file);
507 /* send hup signal to the 'pid' process */
509 seteuid(0); /* get root privilege */
511 seteuid(getuid()); /* drop root privilege */
515 /*************************************
516 put out end message and exit
517 *************************************/
518 void terminateProg(int ret){
520 /* close work db (opengatemd.db) */
523 if(debug>0) err_msg("INFO: Terminated");
528 /************************************
529 routines for debugging output
530 ***********************************/
531 int LockDaemonLockFile(void){
533 if(debug>1) err_msg("DEBUG:=>lockDaemonLockFile( )");
534 ret = lockDaemonLockFile();
535 if(debug>1) err_msg("DEBUG:(%d)<=lockDaemonLockFile( )",ret);
539 void Daemonize(void){
540 if(debug>1) err_msg("DEBUG:=>daemonize( )");
542 if(debug>1) err_msg("DEBUG:<=daemonize( )");
545 void ShowHelp(char* procName){
546 if(debug>1) err_msg("DEBUG:=>showHelp(%s)", procName);
548 if(debug>1) err_msg("DEBUG:<=showhelp( )");
550 void KillDaemon(void){
551 if(debug>1) err_msg("DEBUG:=>killDaemon( )");
553 if(debug>1) err_msg("DEBUG:<=killDaemon( )");
555 void ReloadDaemon(void){
556 if(debug>1) err_msg("DEBUG:=>reloadDaemon( )");
558 if(debug>1) err_msg("DEBUG:<=reloadDaemon( )");