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 signal(SIGHUP, sigHupHandler);
163 signal(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();
186 /*** enter infinite loop of packet inspection ***/
189 /* if sig-hup flag is on, reload this program */
190 /* sig-hup flag is set in sigHupHandler, when HUP signal arrives */
191 if(sigHupArrived)execlp(argv[0], argv[0], NULL);
193 /* if sig-IO flag is on, get mac addresses from UDP port */
194 /* and remove the addresses from caches (renew info at next capture) */
195 /* sig-IO flag is on in sigIoHandler, when a packet arrives. */
196 /* the packet includes renewed mac addresses sent from management program. */
199 while(GetDataFromUdpPort(macAddrInUdp, ADDRMAXLN, clientIpAddress)>0){
200 if(IsUdpClientTrusted(clientIpAddress)){
201 DelCacheItem(macAddrInUdp,"");
202 DelMacCacheItem(macAddrInUdp);
207 /* get one packet from pcap */
208 ret=GetNextPacketFromPcap(macAndIpAddressRaw, &addrLen, &ttl);
213 /* when long time passed from previous check, check&delete useless sessions */
214 if( time(NULL) - uselessCheckTime > checkInterval ){
215 uselessCheckTime = time(NULL);
216 DelUselessSessions();
219 /* and return to loop top */
223 /* ignore not-ip packet */
224 if(ret==-1) continue;
226 /* ignore local packet */
229 /* ignore the packet tha is checked recently */
230 if( IsRecentlyCheckedAddress(macAndIpAddressRaw, addrLen) ) continue;
232 /**** no more processing for recently checked packets ****/
233 /**** only cache timeout packets proceeds to below ****/
235 /* convert address from network-raw form to presentation form */
236 ConvertMacFromRawToDisplay(macAndIpAddressRaw, macAddress);
237 ConvertIpFromRawToDisplay(macAndIpAddressRaw+MACADDRLN,
238 addrLen-MACADDRLN, ipAddress);
240 /* if the address is included in watchlist, report the detection */
241 /* watchlist is the address list needing specific reporting */
242 /* (e.g., suspicion of illegal access). no need for normal operation */
243 if(IsFoundInWatchlistCache(macAddress)==TRUE){
244 err_msg_warn("WARN: find mac=%s ip=%s", macAddress, ipAddress);
247 /* check NAT/Router and save info to db */
248 /* when NAT/Router is inserted, the acquired MAC is the address of NAT/Router */
249 /* thus the MAC based control is failed */
250 /* at now, not denying the packet but reporting the detection. */
251 isNatOrRouter=IsSentViaNatOrRouter(ipAddress, macAddress, ttl);
252 if(isNatOrRouter) PutLogAtNatOrRouter(isNatOrRouter,ipAddress,macAddress,ttl);
253 PutMacInfoToWorkDb(macAddress, ttl, isNatOrRouter);
255 /*** get the status of the terminal from session table and DB ***/
257 /* search the captured address in session table */
258 sessionFound = IsMatchedSessionFound(macAddress);
260 /* search the captured address in cache of MAC DB */
261 macFound = QueryMacFromMacCache(macAddress, userId, extraId);
263 /* if failed, access MAC DB */
265 macFound = QueryMacFromMngDb(macAddress, userId, extraId);
267 /* if db access is failed, set not-found and retry at next capture */
268 /* if db access is successed (found or not-found), save it to cache */
269 if(macFound==ERROR) macFound=FALSE;
270 else AddMacCacheItem(macAddress, userId, extraId, macFound);
273 /*** depending the status, add/del/renew the session ***/
275 /* if valid mac and no session, start session */
276 if(macFound && !sessionFound){
277 AddSession(macAddress, userId, extraId);
279 /* save MAC and IP address pair to work db */
280 SetMacIpPair(macAddress, ipAddress, userId, extraId);
283 /* if no mac and started session, stop session */
284 /* (MAC-IP pair in work db is removed in delSession) */
285 if(!macFound && sessionFound){
286 DelSession(macAddress);
289 /* if valid mac and started session, renew check time */
290 if(macFound && sessionFound){
292 /* in normal case, ipfw rule exists. */
293 if(IsMacAddressFoundInIpfw(macAddress)){
294 RenewSession(macAddress);
297 /* when no ipfw rule exists, reset the session */
299 DelSession(macAddress);
300 AddSession(macAddress, userId, extraId);
303 /* save MAC-IP pair to work db (new ip is found for a mac in session). */
304 SetMacIpPair(macAddress, ipAddress, userId, extraId);
307 /* check useless sessions at some interval */
308 /* (MAC and IP pairs are removed in stop session) */
309 if( time(NULL) - uselessCheckTime > checkInterval ){
310 uselessCheckTime = time(NULL);
311 DelUselessSessions();
314 /*** end of infinite loop ***/
316 /* clear data structures (can't reach here, but coded for debugging) */
319 FreeWatchlistCache();
328 /********************************
329 open and lock proc lock file
330 to prevent overlapped daemon exec
331 ********************************/
332 int lockDaemonLockFile(void){
337 /* open process lock file */
338 lockFd = open(GetConfValue("DaemonLockFile"), O_RDWR|O_CREAT, 0644);
340 /* if cannot open or cannot lock, return false */
342 err_msg("ERR at %s#%d: cannot open daemon lock file:%s",__FILE__,__LINE__,
346 if(lockf(lockFd, F_TLOCK, 0)<0) return FALSE;
349 snprintf(str, WORDMAXLN, "%d", getpid());
350 write(lockFd, str, strlen(str)+1);
352 /* normally locked */
357 /******************************
358 daemonize the process
359 ******************************/
360 void daemonize(void){
365 /* detach from parent */
368 err_msg("ERR at %s#%d: cannot fork",__FILE__,__LINE__);
372 /* parent process exits */
375 /* child process runs from here */
376 /* set new prosess group */
379 /* close all file descriptors */
380 for(i=getdtablesize(); i>=0; i--) close(i);
382 /* connect stdin/out/err to null */
383 i=open("/dev/null", O_RDWR); dup(i); dup(i);
385 /* set newly created file permission */
388 if(debug>0) err_msg("INFO: Deamon started");
391 /****************************************
393 ****************************************/
394 void showHelp(char* procName){
396 printf("firewall control by mac address\n");
397 printf(" see detail in Opengate Homepage\n");
399 printf(" format: %s [arg] \n", procName);
400 printf(" arg : -c = run on console (run as daemon in default)\n");
401 printf(" : -e = close all sessions and end service\n");
402 printf(" : -r = reload deamon(not close sessions)\n");
403 printf(" : -s = stop deamon (not close sessions)\n");
404 printf(" : -v = show make dir to check version\n");
407 /*********************************
408 signal handler for SIGIO
409 *********************************/
410 void sigIoHandler(int sig){
415 /*********************************
416 signal handler for SIGHUP
417 *********************************/
418 void sigHupHandler(int sig){
420 if(debug>0)err_msg("INFO: Deamon receives HUP signal");
424 /*********************************
425 signal handler for SIGTERM
426 *********************************/
427 void sigTermHandler(int sig){
429 if(debug>0)err_msg("INFO: Deamon receives TERM signal");
433 /*********************************
435 *********************************/
436 void killDaemon(void){
442 /* get lock file name */
443 lockFileMd=GetConfValue("DaemonLockFile");
445 /* if lock file does not exist, skip */
446 if(stat(lockFileMd, &st)!=0){
450 /*else (file exists), read pid from the file */
451 else if((file=fopen(lockFileMd, "r"))==NULL){
452 err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
455 else if(fscanf(file, "%d", &pid)==0){
456 err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
459 if(file!=NULL) fclose(file);
461 /* send kill signal to the 'pid' process */
463 seteuid(0); /* get root privilege */
465 seteuid(getuid()); /* drop root privilege */
468 /* remove the lockfile */
472 /*********************************
473 reload daemon process
474 *********************************/
475 void reloadDaemon(void){
481 /* get lock file name */
482 lockFileMd=GetConfValue("DaemonLockFile");
484 /* if lock file does not exists, skip */
485 if(stat(lockFileMd, &st)!=0){
489 /* else (file exists), read pid from the file */
490 else if((file=fopen(lockFileMd, "r"))==NULL){
491 err_msg("ERR at %s#%d: cannot open proc lock file:%s",__FILE__,__LINE__
494 else if(fscanf(file, "%d", &pid)==0){
495 err_msg("ERR at %s#%d: cannot read proc lock file:%s",__FILE__,__LINE__
498 if(file!=NULL)fclose(file);
500 /* send hup signal to the 'pid' process */
502 seteuid(0); /* get root privilege */
504 seteuid(getuid()); /* drop root privilege */
508 /*************************************
509 put out end message and exit
510 *************************************/
511 void terminateProg(int ret){
513 /* close work db (opengatemd.db) */
516 if(debug>0) err_msg("INFO: Terminated");
521 /************************************
522 routines for debugging output
523 ***********************************/
524 int LockDaemonLockFile(void){
526 if(debug>1) err_msg("DEBUG:=>lockDaemonLockFile( )");
527 ret = lockDaemonLockFile();
528 if(debug>1) err_msg("DEBUG:(%d)<=lockDaemonLockFile( )",ret);
532 void Daemonize(void){
533 if(debug>1) err_msg("DEBUG:=>daemonize( )");
535 if(debug>1) err_msg("DEBUG:<=daemonize( )");
538 void ShowHelp(char* procName){
539 if(debug>1) err_msg("DEBUG:=>showHelp(%s)", procName);
541 if(debug>1) err_msg("DEBUG:<=showhelp( )");
543 void KillDaemon(void){
544 if(debug>1) err_msg("DEBUG:=>killDaemon( )");
546 if(debug>1) err_msg("DEBUG:<=killDaemon( )");
548 void ReloadDaemon(void){
549 if(debug>1) err_msg("DEBUG:=>reloadDaemon( )");
551 if(debug>1) err_msg("DEBUG:<=reloadDaemon( )");