log bugfix + min. speed logging in monthly/yearly stats
[svn/Prometheus-QoS/.git] / prometheus.c
1 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2 /* Prometheus QoS - you can "steal fire" from your ISP */
3 /* "fair-per-IP" quality of service (QoS) utility */
4 /* requires Linux 2.4.x or 2.6.x with HTB support */
5 /* Copyright(C) 2005-2008 Michael Polak (xChaos) */
6 /* iptables-restore support Copyright(C) 2007-2008 ludva */
7 /* Credit: CZFree.Net,Martin Devera,Netdave,Aquarius,Gandalf */
8 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
9
10 /* Modified: xChaos, 20080422
11 ludva, 20080415
12
13 Prometheus QoS is free software; you can redistribute it and/or
14 modify it under the terms of the GNU General Public License as
15 published by the Free Software Foundation; either version 2.1 of
16 the License, or (at your option) any later version.
17
18 Prometheus QoS is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with Prometheus Qos; if not, write to the Free Software
25 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
27 GNU General Public License is located in file COPYING */
28
29 #define STRLEN 256
30 #define FIRSTGROUPID 1024
31 #define FIRSTIPCLASS 2048
32 #undef DEBUG
33
34 #include "cll1-0.6.h"
35
36 const char *version="0.7.9-c";
37
38 /* Version numbers: 0.7.9 will be last development ("beta"), 0.8.0 first stable */
39 /* Debian(RPM) package versions/patchlevels: 0.7.9-2, 0.8.0-1, 0.8.0-2, etc. */
40 /* C source code development versions ("beta"): 0.7.9-a, 0.8.1-b, etc. */
41 /* C source code release versions: 0.8.0, 0.8.2, 0.8.4, etc. */
42
43 /* ======= All path names are defined here (for RPM patch) ======= */
44
45 char *tc = "/sbin/tc"; /* requires tc with HTB support */
46 char *iptables = "/sbin/iptables"; /* requires iptables utility */
47 char *iptablessave = "/sbin/iptables-save"; /* not yet required */
48 char *iptablesrestore = "/sbin/iptables-restore"; /* requires iptables-restore */
49 char *ls = "/bin/ls"; /* this is not user configurable :-) */
50
51 char *config = "/etc/prometheus/prometheus.conf"; /* main configuration file */
52 char *hosts = "/etc/prometheus/hosts"; /* per-IP bandwidth definition file */
53
54 char *iptablesfile = "/var/spool/prometheus.iptables"; /* temporary file for iptables-restore*/
55 char *credit = "/var/lib/misc/prometheus.credit"; /* credit log file */
56 char *html = "/var/www/traffic.html"; /* hall of fame filename */
57 char *preview = "/var/www/preview.html"; /* hall of fame preview */
58 char *cmdlog = "/var/log/prometheuslog"; /* command log filename */
59 char *log_dir = "/var/www/logs/"; /* log directory pathname, ended with slash */
60 char *log_url = "logs/"; /* log directory relative URI prefix (partial URL) */
61 char *html_log_dir = "/var/www/logs/html/";
62
63 /* ======= Help screen is hopefuly self-documenting part of code :-) ======= */
64
65 void help(void)
66 {
67 puts("Command line switches:\n\
68 \n\
69 -?, --help this help screen\n\
70 -v, --version show version number of this utility and exit\n\
71 -c filename force alternative /etc/prometheus.conf filename\n\
72 -h filename force alternative /etc/hosts filename (overrides hosts keyword)\n\
73 -f just flush iptables and tc classes and exit (stop shaping)\n\
74 -9 emergency iptables flush (do not read data transfer statistics)\n\
75 -p just generate preview of data transfer statistics and exit\n\
76 -n no delay (overrides qos-free-delay keyword)\n\
77 -d dry run (preview tc and iptables commands on stdout)\n\
78 -l Mmm YYYY generate HTML summary of traffic logs (Mmm=Jan-Dec or Year, YYYY=year)\n\
79 -m generate HTML summary of traffic logs for yesterday's month\n\
80 -y generate HTML summary of traffic logs for yesterday's year\n");
81 /* not yet implemented:
82 -s start shaping! (keep data transfer statistics - but apply shaping)\n\
83 -r just reload configuration (...and keep data transfer statistics)\n\
84 */
85 }
86
87 /* === Configuraration file values defaults - stored in global variables ==== */
88
89 int filter_type=1; /*1 mark, 2 classify*/
90 char *mark="MARK";
91 char *mark_iptables="MARK --set-mark ";
92 int dry_run=0; /* preview - use puts() instead of system() */
93 char *iptablespreamble="*mangle\n:PREROUTING ACCEPT [0:0]\n:POSTROUTING ACCEPT [0:0]\n:INPUT ACCEPT [0:0]\n:OUTPUT ACCEPT [0:0]\n:FORWARD ACCEPT [0:0]";
94 FILE *iptables_file=NULL;
95 int enable_credit=1; /* enable credit file */
96 int use_credit=0; /* use credit file (if enabled)*/
97 char *title="Hall of Fame - Greatest Suckers"; /* hall of fame title */
98 int hall_of_fame=1; /* enable hall of fame */
99 char *lan="eth0"; /* LAN interface */
100 char *lan_medium="100Mbit"; /* 10Mbit/100Mbit ethernet */
101 char *wan="eth1"; /* WAN/ISP interface */
102 char *wan_medium="100Mbit"; /* 10Mbit/100Mbit ethernet */
103 char *qos_leaf="sfq perturb 5"; /* leaf discipline */
104 char *qos_free_zone=NULL; /* QoS free zone */
105 int qos_proxy=1; /* include proxy port to QoS */
106 int include_upload=1; /* upload+download=total traffic */
107 char *proxy_ip="192.168.1.1/32"; /* our IP with proxy port */
108 int proxy_port=3128; /* proxy port number */
109 long long int line=1024; /* WAN/ISP download in kbps */
110 long long int up=1024; /* WAN/ISP upload in kbps */
111 int free_min=32; /* minimum guaranted bandwidth for all undefined hosts */
112 int free_max=64; /* maximum allowed bandwidth for all undefined hosts */
113 int qos_free_delay=0; /* seconds to sleep before applying new QoS rules */
114 int digital_divide=2; /* controls digital divide weirdness ratio, 1...3 */
115 int max_nesting=3; /* maximum nesting of HTB clases, built-in maximum seems to be 4 */
116 int htb_r2q=1;
117 int burst=8; /* HTB burst (in kbits) */
118 int burst_main=64;
119 int burst_group=32;
120 int magic_priorities=8; /* number of priority levels (soft shaping) */
121 int magic_treshold=8; /* reduce ceil by X*magic_treshhold kbps (hard shaping) */
122 int keywordcount=0;
123
124 /* not yet implemented:
125 int fixed_packets=0; maximum number of pps per IP address (not class!)
126 int packet_limit=5; maximum number of pps to htn CEIL, not rate !!!
127 */
128 FILE *log_file=NULL;
129
130 char *kwd="via-prometheus"; /* /etc/hosts comment, eg. #qos-64-128 */
131
132 const int idxtable_treshold1=24; /* this is no longer configurable */
133 const int idxtable_treshold2=12; /* this is no longer configurable */
134 const int idxtable_bitmask1=3; /* this is no longer configurable */
135 const int idxtable_bitmask2=3; /* this is no longer configurable */
136
137 /* ==== This is C<<1 stuff - learn C<<1 first! http://cll1.arachne.cz ==== */
138
139 struct IP
140 {
141 char *addr;
142 char *name;
143 char *sharing;
144 int min;
145 int desired;
146 int max;
147 int mark;
148 int prio;
149 int fixedprio;
150 int group;
151 unsigned long long direct;
152 unsigned long long proxy;
153 unsigned long long upload;
154 unsigned long long traffic;
155 unsigned long long credit;
156 unsigned long pktsup;
157 unsigned long pktsdown;
158 struct Keyword *keyword;
159 list(IP);
160 } *ips=NULL, *ip, *sharedip;
161
162 struct Group
163 {
164 int min;
165 int count;
166 int desired;
167 int id;
168 list(Group);
169 } *groups=NULL, *group;
170
171 struct Index
172 {
173 char *addr;
174 char *id;
175 struct Index *parent;
176 int bitmask;
177 int children;
178 list(Index);
179 } *idxs=NULL, *idx, *metaindex;
180
181 struct Keyword
182 {
183 char *key;
184
185 int asymetry_ratio; /* ratio for ADSL-like upload */
186 int asymetry_fixed; /* fixed treshold for ADSL-like upload */
187 int data_limit; /* hard shaping: apply magic_treshold if max*data_limit MB exceeded */
188 int data_prio; /* soft shaping (qos): reduce HTB prio if max*data_prio MB exceeded */
189 long fixed_limit; /* fixed data limit for setting lower HTB ceil */
190 long fixed_prio; /* fixed data lmit for setting lower HTB prio */
191 int reserve_min; /* bonus for nominal HTB rate bandwidth (in kbps) */
192 int reserve_max; /* malus for nominal HTB ceil (in kbps) */
193 // int divide_max; /* relative malus: new_ceil=rate+(old_ceil-rate)/divide_max */
194 // int htb_ceil_bonus_divide; /* relative bonus: new_ceil=old_ceil+old_ceil/htb_ceil_bonus_divide */
195 int default_prio; /* default HTB priority for this keyword */
196 char *html_color;
197 int ip_count;
198 char *leaf_discipline;
199
200 list(Keyword);
201 } *keyword,*defaultkeyword=NULL,*keywords=NULL;
202
203 /* Damned, this must be object oriented! This looks almost like constructor ;-) */
204
205 void TheIP(void)
206 {
207 create(ip,IP);
208 ip->name="";
209 ip->addr="";
210 ip->sharing=NULL;
211 ip->prio=1;
212 ip->fixedprio=0;
213 ip->mark=ip->min=ip->max=ip->desired=ip->credit=0;
214 ip->upload=ip->proxy=ip->direct=ip->traffic=0;
215 ip->pktsup=ip->pktsdown=0;
216 ip->keyword=keywords;
217 push(ip,ips);
218 }
219
220 /* ====== iptables indexes are used to reduce complexity to log8(N) ===== */
221
222 char *very_ugly_ipv4_code(char *inip,int bitmask,int format_as_chainname)
223 {
224 /* warning: this function was debugged only for bitmask values 20,24,28 !!!*/
225 int dot=0,n;
226 char *ip,*outip,*outptr,*fmt;
227
228 duplicate(inip,ip);
229 /* debug printf("(%s,%d) -> ",ip,bitmask); */
230
231 if(ip && *ip && bitmask>=0 && bitmask<=32)
232 string(outip,strlen(ip)+10); /*fuck unicode? assertion: 10>strlen("_%d_%d") */
233 else
234 /* should never exit here */
235 return "undefined";
236 outptr=outip;
237 while(ip && *ip)
238 {
239 if(*ip=='.')
240 {
241 if(dot<(bitmask/8-1))
242 {
243 if(format_as_chainname)
244 *outptr='_';
245 else
246 *outptr='.';
247 outptr++;
248 dot++;
249 }
250 else
251 {
252 char *cutdot=strchr(ip+1,'.'); /*for bitmask<24*/
253 if(cutdot)*cutdot='\0';
254 if(format_as_chainname)
255 fmt="_%d_%d";
256 else
257 fmt=".%d";
258 if(bitmask%8)
259 n=atoi(ip+1)-atoi(ip+1)%(1<<(8-bitmask%8));
260 else
261 n=0;
262
263 /*debug printf("%d/%d => [_%d_%d]\n",atoi(ip+1),bitmask,n,bitmask); */
264 sprintf(outptr,fmt,n,bitmask);
265 if(!format_as_chainname) while(bitmask<24)
266 {
267 strcat(outip,".0");
268 bitmask+=8;
269 }
270 /* debug printf("[%s]\n",outip); */
271 return outip;
272 }
273 }
274 else
275 {
276 *outptr=*ip;
277 outptr++;
278 }
279 ip++;
280 }
281 /*should never exit here*/
282 *outptr='\0';
283 return outip;
284 }
285
286 char *hash_id(char *ip,int bitmask)
287 { return very_ugly_ipv4_code(ip,bitmask,1); }
288
289 char *subnet_id(char *ip,int bitmask)
290 { return very_ugly_ipv4_code(ip,bitmask,0); }
291
292 /* ================= Let's parse configuration file here =================== */
293
294 void reject_config_and_exit(char *filename)
295 {
296 printf("Configuration file %s rejected - abnormal exit.",filename);
297 exit(-1);
298 }
299
300 void get_config(char *config_filename)
301 {
302 char *cnf="mark";
303
304 printf("Configured keywords: ");
305 parse(config_filename)
306 {
307 option("keyword",kwd);
308 if(kwd)
309 {
310 printf("%s ",kwd);
311
312 create(keyword,Keyword);
313 keyword->key=kwd;
314 keyword->asymetry_ratio=1; /* ratio for ADSL-like upload */
315 keyword->asymetry_fixed=0; /* fixed treshold for ADSL-like upload */
316 keyword->data_limit=8; /* hard shaping: apply magic_treshold if max*data_limit MB exceeded */
317 keyword->data_prio=4; /* soft shaping (qos): reduce HTB prio if max*data_prio MB exceeded */
318 keyword->fixed_limit=0; /* fixed data limit for setting lower HTB ceil */
319 keyword->fixed_prio=0; /* fixed data limit for setting lower HTB prio */
320 keyword->reserve_min=8; /* bonus for nominal HTB rate bandwidth (in kbps) */
321 keyword->reserve_max=0; /* malus for nominal HTB ceil (in kbps) */
322 /* obsolete:
323 keyword->divide_max=0; relative malus: new_ceil=rate+(old_ceil-rate)/divide_max
324 keyword->htb_ceil_bonus_divide=0; relative bonus: new_ceil=old_ceil+old_ceil/htb_ceil_bonus_divide
325 */
326 keyword->default_prio=1;
327 keyword->html_color="000000";
328 keyword->ip_count=0;
329 keyword->leaf_discipline="";
330
331 push(keyword,keywords);
332 if(!defaultkeyword) defaultkeyword=keyword;
333 keywordcount++;
334
335 kwd=NULL;
336 }
337 else every(keyword,keywords)
338 {
339 int l=strlen(keyword->key);
340
341
342 if(!strncmp(keyword->key,_,l) && strlen(_)>l+2)
343 {
344 char *tmptr=_; /* <---- l+1 ----> */
345 _+=l+1; /* via-prometheus-asymetry-ratio, etc. */
346 ioption("asymetry-ratio",keyword->asymetry_ratio);
347 ioption("asymetry-treshold",keyword->asymetry_fixed);
348 ioption("magic-relative-limit",keyword->data_limit);
349 ioption("magic-relative-prio",keyword->data_prio);
350 loption("magic-fixed-limit",keyword->fixed_limit);
351 loption("magic-fixed-prio",keyword->fixed_prio);
352 ioption("htb-default-prio",keyword->default_prio);
353 ioption("htb-rate-bonus",keyword->reserve_min);
354 ioption("htb-ceil-malus",keyword->reserve_max);
355 /* obsolete:
356 ioption("htb-ceil-divide",keyword->divide_max);
357 ioption("htb-ceil-bonus-divide",keyword->htb_ceil_bonus_divide);
358 */
359 option("leaf-discipline",keyword->leaf_discipline);
360 option("html-color",keyword->html_color);
361 _=tmptr;
362
363 if(keyword->data_limit || keyword->fixed_limit ||
364 keyword->data_prio || keyword->fixed_prio)
365 use_credit=1;
366 }
367 }
368
369 option("tc",tc);
370 option("iptables",iptables);
371 option("iptables-save",iptablessave); /* new */
372 option("iptables-restore",iptablesrestore); /* new */
373 option("iptables-file",iptablesfile); /* new */
374 option("hosts",hosts);
375 option("lan-interface",lan);
376 option("wan-interface",wan);
377 option("lan-medium",lan_medium);
378 option("wan-medium",wan_medium);
379 lloption("wan-download",line);
380 lloption("wan-upload",up);
381 ioption("hall-of-fame-enable",hall_of_fame);
382 option("hall-of-fame-title",title);
383 option("hall-of-fame-filename",html);
384 option("hall-of-fame-preview",preview);
385 option("log-filename",cmdlog);
386 option("credit-filename",credit);
387 ioption("credit-enable",enable_credit);
388 option("log-traffic-directory",log_dir);
389 option("log-traffic-html-directory",html_log_dir);
390 option("log-traffic-url-path",log_url);
391 option("qos-free-zone",qos_free_zone);
392 ioption("qos-free-delay",qos_free_delay);
393 ioption("qos-proxy-enable",qos_proxy);
394 option("qos-proxy-ip",proxy_ip);
395 option("htb-leaf-discipline",qos_leaf);
396 ioption("qos-proxy-port",proxy_port);
397 ioption("free-rate",free_min);
398 ioption("free-ceil",free_max);
399 ioption("htb-burst",burst);
400 ioption("htb-burst-main",burst_main);
401 ioption("htb-burst-group",burst_group);
402 ioption("htb-nesting-limit",max_nesting);
403 ioption("htb-r2q",htb_r2q);
404 ioption("magic-include-upload",include_upload);
405 ioption("magic-priorities",magic_priorities);
406 ioption("magic-treshold",magic_treshold);
407 option("filter-type", cnf);
408
409 /* not yet implemented:
410 ioption("magic-fixed-packets",fixed_packets);
411 ioption("magic-relative-packets",packet_limit);
412 */
413 }
414 fail
415 {
416 perror(config_filename);
417 puts("Warning - using built-in defaults instead ...");
418 }
419 done;
420 printf("\n");
421
422 /*leaf discipline for keywords*/
423 every(keyword,keywords)
424 {
425 if (!strcmpi(keyword->leaf_discipline, "")){
426 keyword->leaf_discipline = qos_leaf;
427 }
428 }
429
430 if (strcmpi(cnf, "mark")){
431 filter_type = 2;
432 mark = "CLASSIFY";
433 mark_iptables = "CLASSIFY --set-class 1:";
434 }else{
435 filter_type = 1;
436 mark = "MARK";
437 mark_iptables = "MARK --set-mark ";
438 }
439
440 /* are supplied values meaningful ?*/
441 if(line<=0 || up<=0)
442 {
443 puts("Illegal value of LAN or WAN bandwidth: 0 kbps.");
444 reject_config_and_exit(config_filename);
445 }
446 }
447
448 /* ===================== traffic analyser - uses iptables ================ */
449
450 void get_traffic_statistics(void)
451 {
452 char *str,*cmd;
453 int downloadflag=0;
454
455 textfile(Pipe,str) *line,*lines=NULL;
456 string(str,STRLEN);
457 string(cmd,STRLEN);
458
459 sprintf(cmd,"%s -L -v -x -n -t mangle",iptables);
460 shell(cmd);
461 input(str,STRLEN)
462 {
463 create(line,Pipe);
464 line->str=str;
465 string(str,STRLEN);
466 append(line,lines);
467 }
468
469 every(line,lines)
470 {
471 int col, accept=0,proxyflag=0,valid=1,setchainname=0,commonflag=0;
472 unsigned long long traffic=0;
473 unsigned long pkts=0;
474 char *ipaddr=NULL,*ptr;
475
476 /* debug puts(line->str); */
477 valid_columns(ptr,line->str,' ',col)
478 if(valid) switch(col)
479 {
480 case 1: if(eq(ptr,"Chain"))
481 setchainname=1;
482 else if(eq(ptr,"pkts"))
483 valid=0;
484 else
485 sscanf(ptr,"%lu",&pkts);
486 break;
487 case 2: if(setchainname)
488 {
489 if(!strncmp(ptr,"post_",5) || eq(ptr,"POSTROUTING"))
490 downloadflag=1;
491 else
492 if(!strncmp(ptr,"forw_",5) || eq(ptr,"FORWARD"))
493 downloadflag=0;
494
495 if(eq(ptr,"post_common") || eq(ptr,"forw_common"))
496 commonflag=1;
497 }
498 else
499 sscanf(ptr,"%Lu",&traffic); traffic+=(1<<19); traffic>>=20;
500 break;
501 case 3: if((strncmp(ptr,"post_",5) && strncmp(ptr,"forw_",5)) || commonflag)
502 accept=eq(ptr,mark);
503 /*if (filter_type==1) accept=eq(ptr,"MARK"); else accept=eq(ptr,"CLASSIFY");*/
504 break;
505 case 8: if(downloadflag)
506 {
507 if(strstr(proxy_ip,ptr))proxyflag=1;
508 }
509 else
510 ipaddr=ptr;
511 break;
512 case 9: if(downloadflag)ipaddr=ptr;break;
513 }
514
515 if(accept && traffic>0 && ipaddr)
516 {
517 if(proxyflag)printf("(proxy) ");
518 else if(!downloadflag) printf("(upload) ");
519 printf("IP %s: %Lu M (%ld pkts)\n", ipaddr, traffic, pkts);
520 find(ip,ips,eq(ip->addr,ipaddr));
521 else
522 {
523 TheIP();
524 ip->addr=ipaddr;
525 if(eq(ip->addr,"0.0.0.0/0"))
526 {
527 ip->name="(unregistered)";
528 ip->min=free_min;
529 ip->max=ip->desired=free_max;
530 }
531 }
532
533 if(downloadflag)
534 {
535 if(proxyflag)
536 ip->proxy=traffic;
537 else
538 ip->traffic+=traffic;
539 ip->direct=ip->traffic-ip->upload-ip->proxy;
540 ip->pktsdown=pkts;
541 }
542 else
543 {
544 ip->upload=traffic;
545 ip->pktsup=pkts;
546 if(include_upload)
547 ip->traffic+=traffic;
548 else
549 if(traffic>ip->traffic)
550 ip->traffic=traffic;
551 }
552 }
553 }
554
555 free(cmd);
556 }
557
558 /* ========== This function executes, logs OR ALSO prints command ========== */
559
560 void safe_run(char *cmd)
561 {
562 if(dry_run) printf("\n=>%s\n",cmd); else system(cmd);
563 if(log_file) fprintf(log_file,"%s\n",cmd);
564 }
565
566 void save_line(char *line)
567 {
568 fprintf(iptables_file,"%s\n",line);
569 }
570
571 void run_restore(void)
572 {
573 char *restor, *str;
574 string(restor,STRLEN);
575
576 /*-----------------------------------------------------------------*/
577 printf("Running %s <%s ...\n",iptablesrestore,iptablesfile);
578 /*-----------------------------------------------------------------*/
579
580 save_line("COMMIT");
581 fclose(iptables_file);
582 if(dry_run)
583 {
584 parse(iptablesfile)
585 {
586 str=_;
587 printf("%s\n", str);
588 }done;
589 }
590
591 sprintf(restor,"%s <%s",iptablesrestore, iptablesfile);
592 safe_run(restor);
593
594 free(restor);
595 }
596
597 /* == This function strips extra characters after IP address and stores it = */
598
599 void parse_ip(char *str)
600 {
601 char *ptr=str,*ipaddr=NULL,*ipname=NULL;;
602
603 while(*ptr && *ptr!=' ' && *ptr!=9)
604 ptr++;
605
606 *ptr=0;
607 ipaddr=str;
608 ptr++;
609 while(*ptr && (*ptr==' ' || *ptr==9))
610 ptr++;
611 ipname=ptr;
612 while(*ptr && *ptr!=' ' && *ptr!=9)
613 ptr++;
614 *ptr=0;
615
616 find(ip,ips,eq(ip->addr,ipaddr)); else TheIP();
617 ip->addr=ipaddr;
618 ip->name=ipname;
619 }
620
621 char *parse_datafile_line(char *str)
622 {
623 char *ptr=strchr(str,' ');
624
625 if(ptr)
626 {
627 *ptr=0;
628 ptr++;
629 return ptr;
630 }
631 else
632 return NULL;
633 }
634
635 struct IpLog
636 {
637 char *name;
638 long traffic;
639 long guaranted;
640 list(IpLog);
641 } *iplog,*iplogs;
642
643 void parse_ip_log(int argc, char **argv)
644 {
645 char *month, *year, *str, *name, *ptr, *ptr2;
646 long traffic, traffic_month, total=0, guaranted;
647 int col, col2, y_ok, m_ok, accept_month, i=1, any_month=0;
648 char mstr[4], ystr[5];
649 FILE *f;
650 string(str,STRLEN);
651
652 if(argv[1][1]=='l') /* -l */
653 {
654 if(argc<4)
655 {
656 puts("Missing parameter(s)!\nUsage: prometheus -l Mmm YYYY (Mmm=Jan-Dec or Year, YYYY=year)");
657 exit(-1);
658 }
659 else
660 {
661 month=argv[2];
662 if(eq(month,"Year")) any_month=1;
663 year=argv[3];
664 }
665 }
666 else
667 {
668 time_t t = time(NULL) - 3600*24 ; /* yesterday's timestamp*/
669 struct tm *timep = localtime(&t);
670
671 if(argv[1][1]=='m') /* -m yestarday - month */
672 {
673 strftime(mstr, 4, "%b", timep);
674 month=mstr;
675 strftime(ystr, 5, "%Y", timep);
676 year=ystr;
677 }
678 else /* -y yesterday - year */
679 {
680 month="Year";
681 any_month=1;
682 strftime(ystr, 5, "%Y", timep);
683 year=ystr;
684 }
685 }
686 printf("Analysing traffic for %s %s ...\n",month,year);
687
688 sprintf(str,"%s %s/*.log",ls,log_dir);
689 shell(str);
690
691 input(str,STRLEN)
692 {
693 ptr=strrchr(str,'\n');
694 if(ptr) *ptr='\0';
695 printf("Parsing %s ...",str);
696 accept_month=0;
697 traffic_month=0;
698 guaranted = 0;
699 parse(str)
700 {
701 y_ok=m_ok=0;
702 valid_columns(ptr,_,'\t',col) switch(col)
703 {
704 case 2: name = ptr;break;
705 case 3: traffic = atol(ptr);break;
706 /* column number - was 7, now 10...*/
707 case 7:
708 case 8:
709 case 9:
710 case 10: if (isalnum(*ptr)) /* alphanumeric string = date, just one*/
711 {
712 valid_columns(ptr2,ptr,' ',col2) switch(col2)
713 {
714 case 2: if(any_month || eq(ptr2,month)) m_ok = 1; break;
715 case 5: if(eq(ptr2,year)) y_ok = 1; break;
716 }
717 }
718 else
719 {
720 if(col == 7) guaranted = atol(ptr);
721 }
722 }
723
724 if(y_ok && m_ok)
725 {
726 traffic_month += traffic;
727 accept_month = 1;
728 }
729 }
730 done;
731
732 if(accept_month)
733 {
734 create(iplog,IpLog);
735 iplog->name = name;
736 iplog->guaranted = guaranted;
737 iplog->traffic = traffic_month;
738 insert(iplog,iplogs,desc_order_by,traffic);
739 printf(" %ld MB\n",iplog->traffic);
740 }
741 else
742 puts(" no records.");
743 }
744 sprintf(str,"%s/%s-%s.html",html_log_dir,year,month);
745 printf("Writing %s ...",str);
746 f=fopen(str,"w");
747 if(f)
748 {
749 fprintf(f,"<table border><tr><th colspan=\"4\">Data transfers - %s %s</th><th align=\"right\">Min.speed</th></tr>\n ",month,year);
750 every(iplog,iplogs)
751 if(iplog->traffic)
752 {
753 fprintf(f,"<tr><td align=\"right\">%d</td><th>%s</td><td align=\"right\">%ld MB</td><th align=\"right\">%ld GB</th><th align=\"right\">%ld kbps</th></tr>\n",
754 i++, iplog->name, iplog->traffic, iplog->traffic>>10, iplog->guaranted);
755 total+=iplog->traffic>>10;
756 }
757 fprintf(f,"<tr><th colspan=\"3\" align=\"left\">Total:</th><th align=\"right\">%ld GB</th><th align=\"right\">%Ld kbps</th></tr>\n", total, line);
758 fputs("</table>\n",f);
759 fclose(f);
760 puts(" done.");
761 }
762 }
763
764
765 /*-----------------------------------------------------------------*/
766 /* Are you looking for int main (int argc, char **argv) ? :-)) */
767 /*-----------------------------------------------------------------*/
768
769 program
770 {
771 int i=0;
772 FILE *f=NULL;
773 char *str, *ptr, *d;
774 char *substring;
775 int class_count=0,ip_count=0;
776 int parent=1;
777 int just_flush=0;
778 int nodelay=0;
779 int just_preview=0; /* preview - generate just stats */
780 int just_logs=0; /* just parse logs */
781
782 char *chain_forward, *chain_postrouting;
783 char *althosts=NULL;
784
785 printf("\n\
786 Prometheus QoS - \"fair-per-IP\" Quality of Service setup utility.\n\
787 Version %s - Copyright (C)2005-2008 Michael Polak (xChaos)\n\
788 iptables-restore & burst tunning & classify modification 0.7d by Ludva\n\
789 Credit: CZFree.Net, Martin Devera, Netdave, Aquarius, Gandalf\n\n",version);
790
791 /*----- Boring... we have to check command line options first: ----*/
792
793 arguments
794 {
795 argument("-c") { nextargument(config); }
796 argument("-h") { nextargument(althosts);}
797 argument("-d") { dry_run=1; }
798 argument("-f") { just_flush=1; }
799 argument("-9") { just_flush=9; }
800 argument("-p") { just_preview=1; }
801 argument("-n") { nodelay=1; }
802 argument("-l") { just_logs=1; }
803 argument("-m") { just_logs=1; }
804 argument("-y") { just_logs=1; }
805 argument("-?") { help(); exit(0); }
806 argument("--help") { help(); exit(0); }
807 argument("-v") { exit(0); }
808 argument("--version") { exit(0); }
809 }
810
811 if(dry_run)
812 puts("*** THIS IS JUST DRY RUN ! ***\n");
813
814 date(d); /* this is typical cll1.h macro */
815
816 /*-----------------------------------------------------------------*/
817 printf("Parsing configuration file %s ...\n", config);
818 /*-----------------------------------------------------------------*/
819 get_config(config);
820
821 if(just_logs)
822 {
823 parse_ip_log(argc,argv);
824 exit(0);
825 }
826
827 if(althosts) hosts=althosts;
828
829 if(just_flush<9)
830 {
831 /*-----------------------------------------------------------------*/
832 puts("Parsing iptables verbose output ...");
833 /*-----------------------------------------------------------------*/
834 get_traffic_statistics();
835 }
836
837 /*-----------------------------------------------------------------*/
838 printf("Parsing class defintion file %s ...\n", hosts);
839 /*-----------------------------------------------------------------*/
840 int groupidx = FIRSTGROUPID;
841 parse(hosts)
842 {
843 str=_;
844
845 if(*str<'0' || *str>'9')
846 continue;
847
848 //Does this IP share QoS class with some other ?
849 substring=strstr(str,"sharing-");
850 if(substring)
851 {
852 substring+=8; //"sharing-"
853 parse_ip(str);
854 ip_count++;
855 ip->sharing=substring;
856 ip->keyword=defaultkeyword; /* settings for default keyword */
857 while(*substring && *substring!='\n')
858 substring++;
859 *substring=0;
860 }
861 else
862 {
863 //Do we have to create new QoS class for this IP ?
864
865 find(keyword,keywords,(substring=strstr(str,keyword->key)))
866 {
867 parse_ip(str);
868 ip_count++;
869 ip->keyword=keyword;
870 keyword->ip_count++;
871 ip->prio=keyword->default_prio;
872 substring+=strlen(keyword->key)+1;
873 ptr=substring;
874 while(*ptr && *ptr!='-')
875 ptr++;
876 if(*ptr=='-')
877 {
878 *ptr=0;
879 ip->max=ip->desired=atoi(ptr+1);
880 }
881 ip->min=atoi(substring);
882 if(ip->min<=0)
883 {
884 printf(" %s: Illegal value of minimum bandwidth 0 kbps, using %d kbps\n",str,free_min);
885 ip->min=free_min;
886 }
887 if(ip->max<=ip->min)
888 {
889 ip->fixedprio=1;
890 ip->max=ip->min+ip->keyword->reserve_min;
891 }
892 else
893 {
894 ip->max-=ip->keyword->reserve_max;
895
896 /*
897 if(ip->keyword->divide_max>1)
898 ip->max=ip->min+(ip->max-ip->min)/ip->keyword->divide_max;
899 if(ip->keyword->htb_ceil_bonus_divide>0)
900 ip->max+=ip->max/ip->keyword->htb_ceil_bonus_divide;
901 */
902 if(ip->max<ip->min)
903 ip->max=ip->min;
904 }
905 ip->mark=FIRSTIPCLASS+1+class_count++;
906
907 find(group,groups,group->min==ip->min)
908 {
909 group->count++;
910 group->desired+=ip->min;
911 ip->group = group->id;
912 }
913 else
914 {
915 create(group,Group);
916 group->min=ip->min;
917 group->id = groupidx++;
918 ip->group = group->id;
919
920 if(group->min<8) group->min=8;
921 /* Warning - this is maybe because of primitive tc namespace, can be fixed */
922 /* it is because class IDs are derived from min. bandwidth. - xCh */
923 //if(group->min>MAX_GUARANTED_KBPS) group->min=MAX_GUARANTED_KBPS;
924
925 group->count=1;
926 group->desired=ip->min;
927 insert(group,groups,desc_order_by,min);
928 }
929 }//endif keyword-
930 }//endif sharing-
931 }
932 fail
933 {
934 perror(hosts);
935 exit(-1);
936 }
937 done;
938
939 /*-----------------------------------------------------------------*/
940 /* cll1.h - let's allocate brand new character buffer... */
941 /*-----------------------------------------------------------------*/
942 string(str,STRLEN);
943
944 /*-----------------------------------------------------------------*/
945 puts("Resolving shared connections ...");
946 /*-----------------------------------------------------------------*/
947 search(ip,ips,ip->sharing)
948 {
949 search(sharedip,ips,eq(sharedip->name,ip->sharing))
950 {
951 sharedip->traffic+=ip->traffic;
952 ip->traffic=0;
953 ip->mark=sharedip->mark;
954 break;
955 }
956 if(!sharedip)
957 printf("Unresolved shared connection: %s %s sharing-%s\n",ip->addr,ip->name,ip->sharing);
958 }
959
960 if(enable_credit && just_flush<9)
961 {
962 /*-----------------------------------------------------------------*/
963 printf("Parsing credit file %s ...\n", credit);
964 /*-----------------------------------------------------------------*/
965 parse(credit)
966 {
967 ptr=parse_datafile_line(_);
968 if(ptr)
969 {
970 find(ip,ips,eq(ip->addr,_))
971 sscanf(ptr,"%Lu",&(ip->credit));
972 }
973 }
974 done;
975 }
976
977 if(!just_preview)
978 {
979 /*-----------------------------------------------------------------*/
980 puts("Initializing iptables and tc classes ...");
981 /*-----------------------------------------------------------------*/
982
983 iptables_file=fopen(iptablesfile,"w");
984 if (iptables_file == NULL) {
985 puts("Cannot open iptablesfile!");
986 exit(-1);
987 }
988
989 log_file=fopen(cmdlog,"w");
990 if (log_file == NULL) {
991 puts("Cannot open logfile!");
992 exit(-1);
993 }
994
995 save_line(iptablespreamble);
996 run_restore();
997
998 sprintf(str,"%s qdisc del dev %s root 2>/dev/null",tc,lan);
999 safe_run(str);
1000
1001 sprintf(str,"%s qdisc del dev %s root 2>/dev/null",tc,wan);
1002 safe_run(str);
1003
1004 iptables_file=fopen(iptablesfile,"w");
1005 save_line(iptablespreamble);
1006
1007 if(qos_free_zone && *qos_free_zone!='0')
1008 {
1009 char *chain;
1010
1011 sprintf(str,"-A FORWARD -d %s -o %s -j ACCEPT", qos_free_zone, wan);
1012 save_line(str);
1013
1014 if(qos_proxy)
1015 {
1016 save_line(":post_noproxy - [0:0]");
1017 sprintf(str,"-A POSTROUTING -p ! tcp -o %s -j post_noproxy", lan);
1018 save_line(str);
1019 sprintf(str,"-A POSTROUTING -s ! %s -o %s -j post_noproxy", proxy_ip, lan);
1020 save_line(str);
1021 sprintf(str,"-A POSTROUTING -s %s -p tcp --sport ! %d -o %s -j post_noproxy", proxy_ip, proxy_port, lan);
1022 save_line(str);
1023
1024 chain="post_noproxy";
1025 }
1026 else
1027 chain="POSTROUTING";
1028
1029 sprintf(str,"-A %s -s %s -o %s -j ACCEPT", chain, qos_free_zone, lan);
1030 save_line(str);
1031 }
1032
1033 if(ip_count>idxtable_treshold1 && !just_flush)
1034 {
1035 int idxcount=0, bitmask=32-idxtable_bitmask1; /* default net mask: 255.255.255.240 */
1036 char *subnet, *buf;
1037 /*-----------------------------------------------------------------*/
1038 printf("Detected %d addresses - indexing iptables rules to improve performance...\n",ip_count);
1039 /*-----------------------------------------------------------------*/
1040
1041 save_line(":post_common - [0:0]");
1042 save_line(":forw_common - [0:0]");
1043
1044 search(ip,ips,ip->addr && *(ip->addr) && !eq(ip->addr,"0.0.0.0/0"))
1045 {
1046 buf=hash_id(ip->addr,bitmask);
1047 find(idx,idxs,eq(idx->id,buf))
1048 idx->children++;
1049 else
1050 {
1051 create(idx,Index);
1052 idx->addr=ip->addr;
1053 idx->id=buf;
1054 idx->bitmask=bitmask;
1055 idx->parent=NULL;
1056 idx->children=0;
1057 idxcount++;
1058 push(idx,idxs);
1059 }
1060 }
1061
1062 /* brutal perfomance optimalization */
1063 while(idxcount>idxtable_treshold2 && bitmask>2*idxtable_bitmask2)
1064 {
1065 bitmask-=idxtable_bitmask2;
1066 idxcount=0;
1067 search(idx,idxs,idx->parent==NULL)
1068 {
1069 buf=hash_id(idx->addr,bitmask);
1070 find(metaindex,idxs,eq(metaindex->id,buf))
1071 metaindex->children++;
1072 else
1073 {
1074 create(metaindex,Index);
1075 metaindex->addr=idx->addr;
1076 metaindex->id=buf;
1077 metaindex->bitmask=bitmask;
1078 metaindex->parent=NULL;
1079 metaindex->children=0;
1080 idxcount++;
1081 push(metaindex,idxs);
1082 }
1083 idx->parent=metaindex;
1084 }
1085 }
1086
1087 /* this should slightly optimize throughout ... */
1088 sort(idx,idxs,desc_order_by,children);
1089 sort(idx,idxs,order_by,bitmask);
1090
1091 i=0;
1092 every(idx,idxs)
1093 {
1094 subnet=subnet_id(idx->addr,idx->bitmask);
1095 printf("%d: %s/%d\n",++i,subnet,idx->bitmask);
1096
1097 sprintf(str,":post_%s - [0:0]", idx->id);
1098 save_line(str);
1099
1100 sprintf(str,":forw_%s - [0:0]", idx->id);
1101 save_line(str);
1102
1103 if(idx->parent)
1104 {
1105 string(buf,strlen(idx->parent->id)+6);
1106 sprintf(buf,"post_%s",idx->parent->id);
1107 }
1108 else
1109 buf="POSTROUTING";
1110
1111 sprintf(str,"-A %s -d %s/%d -o %s -j post_%s", buf, subnet, idx->bitmask, lan, idx->id);
1112 save_line(str);
1113
1114 sprintf(str,"-A %s -d %s/%d -o %s -j post_common", buf, subnet, idx->bitmask, lan);
1115 save_line(str);
1116
1117 if(idx->parent)
1118 {
1119 string(buf,strlen(idx->parent->id)+6);
1120 sprintf(buf,"forw_%s",idx->parent->id);
1121 }
1122 else
1123 buf="FORWARD";
1124
1125 sprintf(str,"-A %s -s %s/%d -o %s -j forw_%s", buf, subnet, idx->bitmask, wan, idx->id);
1126 save_line(str);
1127
1128 sprintf(str,"-A %s -s %s/%d -o %s -j forw_common", buf, subnet, idx->bitmask, wan);
1129 save_line(str);
1130 }
1131 printf("Total indexed iptables chains created: %d\n", i);
1132
1133 sprintf(str,"-A FORWARD -o %s -j forw_common", wan);
1134 save_line(str);
1135
1136 sprintf(str,"-A POSTROUTING -o %s -j post_common", lan);
1137 save_line(str);
1138 }
1139
1140 }
1141
1142 if(just_flush)
1143 {
1144 fclose(iptables_file);
1145 if (log_file) fclose(log_file);
1146 puts("Just flushed iptables and tc classes - now exiting ...");
1147 exit(0);
1148 }
1149
1150 if(!just_preview)
1151 {
1152 if(!dry_run && !nodelay && qos_free_delay)
1153 {
1154 printf("Flushed iptables and tc classes - now sleeping for %d seconds...\n",qos_free_delay);
1155 sleep(qos_free_delay);
1156 }
1157
1158 sprintf(str,"%s qdisc add dev %s root handle 1: htb r2q %d default 1",tc,lan,htb_r2q);
1159 safe_run(str);
1160
1161 sprintf(str,"%s class add dev %s parent 1: classid 1:2 htb rate %s ceil %s burst %dk prio 0",tc,lan,lan_medium,lan_medium,burst_main);
1162 safe_run(str);
1163
1164 sprintf(str,"%s class add dev %s parent 1:2 classid 1:1 htb rate %Ldkbit ceil %Ldkbit burst %dk prio 0",tc,lan,line,line,burst_main);
1165 safe_run(str);
1166
1167 sprintf(str,"%s qdisc add dev %s root handle 1: htb r2q %d default 1",tc,wan,htb_r2q);
1168 safe_run(str);
1169
1170 sprintf(str,"%s class add dev %s parent 1: classid 1:2 htb rate %s ceil %s burst %dk prio 0",tc,wan,wan_medium,wan_medium,burst_main);
1171 safe_run(str);
1172
1173 sprintf(str,"%s class add dev %s parent 1:2 classid 1:1 htb rate %Ldkbit ceil %Ldkbit burst %dk prio 0",tc,wan,up,up,burst_main);
1174 safe_run(str);
1175 }
1176
1177 /*-----------------------------------------------------------------*/
1178 puts("Locating suckers and generating root classes ...");
1179 /*-----------------------------------------------------------------*/
1180 sort(ip,ips,desc_order_by,traffic);
1181
1182
1183 /*-----------------------------------------------------------------*/
1184 /* sub-scope - local variables */
1185 {
1186 long long int rate=line;
1187 long long int max=line;
1188 int group_count=0;
1189 FILE *credit_file=NULL;
1190
1191 if(!just_preview && !dry_run && enable_credit) credit_file=fopen(credit,"w");
1192
1193 every(group,groups)
1194 {
1195 if(!just_preview)
1196 {
1197
1198 //download
1199 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %Ldkbit ceil %Ldkbit burst %dk prio 1 #down desired %d",
1200 tc, lan, parent, group->id, rate, max, burst_group, group->desired);
1201 safe_run(str);
1202
1203 //upload
1204 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %Ldkbit ceil %Ldkbit burst %dk prio 1 #up desired %d",
1205 tc, wan, parent, group->id, rate*up/line, max*up/line, burst_group, group->desired);
1206 safe_run(str);
1207 }
1208
1209 if(group_count++<max_nesting) parent=group->id;
1210
1211 rate-=digital_divide*group->min;
1212 if(rate<group->min)rate=group->min;
1213
1214 /*shaping of aggresive downloaders, with credit file support */
1215 if(use_credit)
1216 {
1217 int group_rate=group->min, priority_sequence=magic_priorities+1;
1218
1219 search(ip, ips, ip->min==group->min && ip->max>ip->min)
1220 {
1221 if( ip->keyword->data_limit && !ip->fixedprio &&
1222 ip->traffic>ip->credit+
1223 (ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20)) )
1224 {
1225 if(group_rate<ip->max) ip->max=group_rate;
1226 group_rate+=magic_treshold;
1227 ip->prio=magic_priorities+2;
1228 if(ip->prio<3) ip->prio=3;
1229 }
1230 else
1231 {
1232 if( ip->keyword->data_prio && !ip->fixedprio &&
1233 ip->traffic>ip->credit+
1234 (ip->min*ip->keyword->data_prio+(ip->keyword->fixed_prio<<20)) )
1235 {
1236 ip->prio=priority_sequence--;
1237 if(ip->prio<2) ip->prio=2;
1238 }
1239
1240 if(credit_file)
1241 {
1242 unsigned long long lcredit=0;
1243
1244 if((ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))>ip->traffic)
1245 lcredit=(ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))-ip->traffic;
1246 fprintf(credit_file,"%s %Lu\n",ip->addr,lcredit);
1247 }
1248 }
1249 }
1250
1251 }
1252 }
1253 if(credit_file)fclose(credit_file);
1254 }
1255
1256 if(just_preview)
1257 {
1258 f=fopen(preview,"w");
1259 ptr=preview;
1260 }
1261 else if(!dry_run && !just_flush)
1262 {
1263 /*-----------------------------------------------------------------*/
1264 printf("Writing data transfer database ...\n");
1265 /*-----------------------------------------------------------------*/
1266 f=fopen("/var/run/prometheus.previous","w");
1267 if(f)
1268 {
1269 search(ip,ips,ip->traffic || ip->direct || ip->proxy ||ip->upload)
1270 fprintf(f,"%s %Lu %Lu %Lu %Lu\n",ip->addr,ip->traffic,ip->direct,ip->proxy,ip->upload);
1271 fclose(f);
1272 }
1273
1274 f=fopen(html,"w");
1275 ptr=html;
1276 }
1277
1278 if(f)
1279 {
1280 int total=0;
1281 int count=1;
1282 i=0;
1283
1284 /*-----------------------------------------------------------------*/
1285 printf("Sorting data and generating statistics page %s ...\n",ptr);
1286 /*-----------------------------------------------------------------*/
1287
1288 fputs("<table border>\n<tr><th align=\"right\">#</th><th align=\"right\">group</th><th align=\"right\">IPs</th><th align=\"right\">requested</th>\n",f);
1289 fprintf(f,"<th colspan=\"%d\">data limits</th>\n",keywordcount);
1290 fputs("</tr>\n",f);
1291 every(group,groups)
1292 {
1293 #ifdef DEBUG
1294 printf("%d k group: %d bandwidth requested: %d k\n",group->min,group->count,group->desired);
1295 #endif
1296 fprintf(f,"<tr><td align=\"right\">%d</td><td align=\"right\">%d k</td>",count,group->min);
1297 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d k</td>",group->count,group->desired);
1298
1299 every(keyword,keywords)
1300 fprintf(f,"<td align=\"right\"><font color=\"#%s\">%d M</font></td>",keyword->html_color,group->min*keyword->data_limit);
1301
1302 i+=group->desired;
1303 total+=group->count;
1304 count++;
1305 }
1306 #ifdef DEBUG
1307 printf("Total groups: %d Total bandwidth requested: %d k\nAGGREGATION: 1/%d\n",count,i,i/line);
1308 #endif
1309 fprintf(f,"<tr><th colspan=\"2\" align=\"left\">Line %Ld k</td>",line);
1310 fprintf(f,"<th align=\"right\">%d</td><th align=\"right\">%d k</td>",total,i);
1311
1312 every(keyword,keywords)
1313 fprintf(f,"<th align=\"right\">%d IPs</th>",keyword->ip_count);
1314
1315 fprintf(f,"</tr><tr><th colspan=\"4\">Aggregation 1/%d</th>\n",(int)(0.5+i/line));
1316 fprintf(f,"<th colspan=\"%d\">%d traffic classes</th></tr>\n",keywordcount,total);
1317
1318 fputs("</table>\n",f);
1319 }
1320 else if(!dry_run && !just_flush)
1321 perror(html);
1322
1323 i=1;
1324 if(f)
1325 {
1326 unsigned long long total=0, total_direct=0, total_proxy=0, total_upload=0, tmp_sum=0;
1327 int active_classes=0;
1328 int colspan;
1329 FILE *iplog;
1330 struct Sum {unsigned long long l; int i; list(Sum);} *sum,*sums=NULL;
1331
1332 if(qos_proxy)
1333 colspan=12;
1334 else
1335 colspan=11;
1336
1337 fprintf(f,"<p><table border>\n<tr><th colspan=\"%d\">%s",colspan,title);
1338 fprintf(f," (%s)</th></tr>\n", d);
1339 fputs("<tr><td align=\"right\">#</td><td>hostname</td>\
1340 <td align=\"right\">credit</td>\
1341 <td align=\"right\">limit</td>\
1342 <td align=\"right\">total</td>\
1343 <td align=\"right\">direct</td>\n",f);
1344 if(qos_proxy)
1345 fputs("<td align=\"right\">proxy</td>\n",f);
1346 fputs("<td align=\"right\">upload</td>\
1347 <td align=\"right\">minimum</td>\
1348 <td align=\"right\">desired</td>\
1349 <td align=\"right\">maximum</td>\
1350 <td>prio</td></tr>\n",f);
1351
1352 every(ip,ips)
1353 {
1354 char *f1="", *f2="";
1355 if(ip->max<ip->desired)
1356 {
1357 f1="<font color=\"red\">";
1358 f2="</font>";
1359 }
1360 else if(ip->prio>1)
1361 {
1362 f1="<font color=\"brown\">";
1363 f2="</font>";
1364 }
1365
1366 #ifdef DEBUG
1367 printf("%03d. %-22s %10Lu (%d/%d)\n",i ,ip->name, ip->traffic, ip->min, ip->max);
1368 #endif
1369 fprintf(f,"<tr><td align=\"right\"><a name=\"%s\"></a>%d</td><td><a href=\"%s%s.log\">%s</a></td><td align=\"right\">%Lu M</td>\n",
1370 ip->name, i, log_url, ip->name, ip->name, ip->credit);
1371 fprintf(f,"<td align=\"right\"><font color=\"#%s\">%Lu M</font></td>",ip->keyword->html_color,ip->credit+(ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20)));
1372 fprintf(f,"<td align=\"right\">%s%Lu M%s</td><td align=\"right\">%Lu M</td>\n", f1, ip->traffic, f2, ip->direct);
1373 if(qos_proxy)
1374 fprintf(f,"<td align=\"right\">%Lu M</td>\n", ip->proxy);
1375 fprintf(f,"<td align=\"right\">%Lu M</td>\n", ip->upload);
1376 fprintf(f,"<td align=\"right\">%d k</td><td align=\"right\">%d k</td><td align=\"right\">%s%d k%s</td><td>%s%d%s</td></tr>\n",ip->min,ip->desired,f1,ip->max,f2,f1,ip->prio,f2);
1377 total+=ip->traffic;
1378 total_direct+=ip->direct;
1379 total_proxy+=ip->proxy;
1380 total_upload+=ip->upload;
1381 if(ip->traffic>0)
1382 {
1383 active_classes++;
1384 tmp_sum+=ip->traffic;
1385 create(sum,Sum);
1386 sum->l=tmp_sum;
1387 sum->i=active_classes;
1388 insert(sum,sums,order_by,i);
1389 }
1390
1391 i++;
1392
1393 if(!just_preview)
1394 {
1395 sprintf(str,"%s/%s.log",log_dir,ip->name);
1396 iplog=fopen(str,"a");
1397 if(iplog)
1398 {
1399 fprintf(iplog,"%ld\t%s\t%Lu\t%Lu\t%Lu\t%Lu\t%d\t%d\t%d\t%s",
1400 time(NULL),ip->name,ip->traffic,ip->direct,ip->proxy,ip->upload,ip->min,ip->max,ip->desired,d); /* d = date*/
1401 fclose(iplog);
1402 }
1403 }
1404
1405 }
1406 fprintf(f,"<tr><th colspan=\"4 \"align=\"left\">SUMMARY:</td>");
1407 fprintf(f,"<th align=\"right\">%Lu M</th>\
1408 <th align=\"right\">%Lu M</th>\n", total, total_direct);
1409 if(qos_proxy)
1410 fprintf(f,"<th align=\"right\">%Lu M</th>\n", total_proxy);
1411 fprintf(f,"<th align=\"right\">%Lu M</th>", total_upload);
1412 fputs("<td colspan=\"4\"></td></th>\n</table>\n",f);
1413
1414 if(active_classes>10)
1415 {
1416 fputs("<a name=\"erp\"></a><p><table border><tr><th colspan=\"5\">Enterprise Research and Planning (ERP)</th></tr>\n",f);
1417 fputs("<tr><td>Analytic category</td>\n",f);
1418 fputs("<td colspan=\"2\" align=\"center\">Active Classes</td><td colspan=\"2\" align=\"center\">Data transfers</td></tr>\n",f);
1419
1420 find(sum,sums,sum->l>=total/4)
1421 {
1422 fprintf(f,"<tr><td>Top 25%% of traffic</td>\n");
1423 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1424 }
1425
1426 find(sum,sums,sum->i==10)
1427 {
1428 fprintf(f,"<tr><td>Top 10 downloaders</td>\n");
1429 fprintf(f,"<th align=\"right\">10</th><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1430 }
1431
1432 find(sum,sums,sum->l>=total/2)
1433 {
1434 fprintf(f,"<tr><td>Top 50%% of traffic</td>\n");
1435 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><th align=\"right\">%Ld %%</th></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1436 }
1437
1438 find(sum,sums,sum->l>=4*total/5)
1439 {
1440 fprintf(f,"<tr><td>Top 80%% of traffic</td>\n");
1441 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><th align=\"right\">%Ld %%</th></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1442 }
1443
1444 find (sum,sums,sum->i>=(active_classes+1)/5)
1445 {
1446 fprintf(f,"<tr><td>Top 20%% downloaders</td>\n");
1447 fprintf(f,"<td align=\"right\">%d</td><th align=\"right\">%d %%</th><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1448 }
1449
1450 find(sum,sums,sum->i>=(active_classes+1)/4)
1451 {
1452 fprintf(f,"<tr><td>Top 25%% downloaders</td>\n");
1453 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1454 }
1455
1456 find(sum,sums,sum->i>=(active_classes+1)/2)
1457 {
1458 fprintf(f,"<tr><td>Top 50%% downloaders</td>\n");
1459 fprintf(f,"<td align=\"right\">%d</td><th align=\"right\">%d %%</th><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1460 }
1461
1462 find(sum,sums,sum->i>=4*(active_classes+1)/5)
1463 {
1464 fprintf(f,"<tr><td>Top 80%% downloaders</td>\n");
1465 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d %%</td><td align=\"right\">%Lu M</td><td align=\"right\">%Ld %%</td></tr>\n",sum->i,(100*sum->i+50)/active_classes,sum->l,(100*sum->l+50)/total);
1466 }
1467
1468 fprintf(f,"<tr><td>All users, all traffic</td>\n");
1469 fprintf(f,"<th align=\"right\">%d</th><th align=\"right\">100 %%</th><th align=\"right\">%Lu M</th><th align=\"right\">100 %%</th></tr>\n",active_classes,total);
1470 fputs("</table>\n",f);
1471 }
1472 fprintf(f,"<small>Statistics generated by Prometheus QoS version %s<br>GPL+Copyright(C)2005-2008 Michael Polak, <a href=\"http://www.arachne.cz/\">Arachne Labs</a></small>\n",version);
1473 fclose(f);
1474 }
1475
1476 if(just_preview)
1477 {
1478 puts("Statistics preview generated (-p switch) - now exiting ...");
1479 exit(0);
1480 }
1481
1482 /*-----------------------------------------------------------------*/
1483 puts("Generating iptables and tc classes ...");
1484 /*-----------------------------------------------------------------*/
1485
1486 i=0;
1487 printf("%-22s %-15s mark\n","name","ip");
1488 search(ip,ips,ip->mark>0)
1489 {
1490
1491 if(idxs)
1492 {
1493 char *buf;
1494 duplicate(ip->addr,buf);
1495 buf=hash_id(ip->addr,32-idxtable_bitmask1);
1496
1497 string(chain_forward,6+strlen(buf));
1498 strcpy(chain_forward,"forw_");
1499 strcat(chain_forward,buf);
1500
1501 string(chain_postrouting,6+strlen(buf));
1502 strcpy(chain_postrouting,"post_");
1503 strcat(chain_postrouting,buf);
1504
1505 free(buf);
1506 }
1507 else
1508 {
1509 chain_forward="FORWARD";
1510 chain_postrouting="POSTROUTING";
1511 }
1512
1513 printf("%-22s %-16s %04d ", ip->name, ip->addr, ip->mark);
1514
1515 /* -------------------------------------------------------- mark download */
1516
1517 sprintf(str,"-A %s -d %s/32 -o %s -j %s%d",chain_postrouting,ip->addr,lan,mark_iptables,ip->mark);
1518 /*sprintf(str,"-A %s -d %s/32 -o %s -j MARK --set-mark %d",chain_postrouting,ip->addr,lan,ip->mark);*/
1519 /* -m limit --limit 1/s */
1520 save_line(str);
1521
1522 if(qos_proxy)
1523 {
1524 sprintf(str,"-A %s -s %s -p tcp --sport %d -d %s/32 -o %s -j %s%d",chain_postrouting,proxy_ip,proxy_port,ip->addr,lan,mark_iptables,ip->mark);
1525 /*sprintf(str,"-A %s -s %s -p tcp --sport %d -d %s/32 -o %s -j MARK --set-mark %d",chain_postrouting,proxy_ip,proxy_port,ip->addr,lan,ip->mark);*/
1526 save_line(str);
1527 }
1528
1529 sprintf(str,"-A %s -d %s/32 -o %s -j ACCEPT",chain_postrouting,ip->addr,lan);
1530 save_line(str);
1531
1532 /* -------------------------------------------------------- mark upload */
1533 sprintf(str,"-A %s -s %s/32 -o %s -j %s%d",chain_forward,ip->addr,wan,mark_iptables,ip->mark);
1534 /* sprintf(str,"-A %s -s %s/32 -o %s -j MARK --set-mark %d",chain_forward,ip->addr,wan,ip->mark);*/
1535 save_line(str);
1536
1537 sprintf(str,"-A %s -s %s/32 -o %s -j ACCEPT",chain_forward,ip->addr,wan);
1538 save_line(str);
1539
1540 if(ip->min)
1541 {
1542 /* -------------------------------------------------------- download class */
1543 printf("(down: %dk-%dk ", ip->min, ip->max);
1544
1545 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d", tc, lan, ip->group, ip->mark,ip->min,ip->max, burst, ip->prio);
1546 safe_run(str);
1547
1548 if (strcmpi(ip->keyword->leaf_discipline, "none")){
1549 sprintf(str,"%s qdisc add dev %s parent 1:%d handle %d %s", tc, lan, ip->mark, ip->mark, ip->keyword->leaf_discipline); /*qos_leaf*/
1550 safe_run(str);
1551 }
1552
1553 if (filter_type == 1){
1554 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d", tc, lan, ip->mark, ip->mark);
1555 safe_run(str);
1556 }
1557
1558 /* -------------------------------------------------------- upload class */
1559 printf("up: %dk-%dk)\n", (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed),
1560 (int)((ip->max/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed));
1561
1562 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d",
1563 tc, wan, ip->group, ip->mark,
1564 (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed),
1565 (int)((ip->max/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed), burst, ip->prio);
1566 safe_run(str);
1567
1568 if (strcmpi(ip->keyword->leaf_discipline, "none")){
1569 sprintf(str,"%s qdisc add dev %s parent 1:%d handle %d %s",tc, wan, ip->mark, ip->mark, ip->keyword->leaf_discipline); /*qos_leaf*/
1570 safe_run(str);
1571 }
1572
1573 if (filter_type == 1){
1574 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d",tc, wan, ip->mark, ip->mark);
1575 safe_run(str);
1576 }
1577 }
1578 else
1579 printf("(sharing %s)\n", ip->sharing);
1580 i++;
1581 }
1582
1583
1584 if(idxs)
1585 {
1586 chain_forward="forw_common";
1587 chain_postrouting="post_common";
1588 }
1589 else
1590 {
1591 chain_forward="FORWARD";
1592 chain_postrouting="POSTROUTING";
1593 }
1594
1595 /* -------------------------------------------------------- mark download */
1596
1597 if(qos_proxy)
1598 {
1599 sprintf(str,"-A %s -s %s -p tcp --sport %d -o %s -j %s%d",chain_postrouting,proxy_ip,proxy_port,lan,mark_iptables,3);
1600 save_line(str);
1601 sprintf(str,"-A %s -s %s -p tcp --sport %d -o %s -j ACCEPT",chain_postrouting,proxy_ip,proxy_port,lan);
1602 save_line(str);
1603 }
1604 sprintf(str,"-A %s -o %s -j %s%d",chain_postrouting,lan,mark_iptables,3);
1605 save_line(str);
1606 sprintf(str,"-A %s -o %s -j ACCEPT",chain_postrouting,lan);
1607 save_line(str);
1608
1609 /* -------------------------------------------------------- mark upload */
1610 sprintf(str,"-A %s -o %s -j %s%d",chain_forward,wan,mark_iptables,3);
1611 save_line(str);
1612 sprintf(str,"-A %s -o %s -j ACCEPT",chain_forward,wan);
1613 save_line(str);
1614
1615 printf("Total IP count: %d\n", i);
1616
1617 /*-----------------------------------------------------------------*/
1618 puts("Generating free bandwith classes ...");
1619 /*-----------------------------------------------------------------*/
1620
1621 /* ---------------------------------------- tc - free bandwith shared class */
1622 sprintf(str,"%s class add dev %s parent 1:%d classid 1:3 htb rate %dkbit ceil %dkbit burst %dk prio 2",tc,lan,parent,free_min,free_max,burst);
1623 safe_run(str);
1624
1625 sprintf(str,"%s class add dev %s parent 1:%d classid 1:3 htb rate %dkbit ceil %dkbit burst %dk prio 2",tc,wan,parent,free_min,free_max,burst);
1626 safe_run(str);
1627
1628 /* tc SFQ */
1629 if (strcmpi(qos_leaf, "none")){
1630 sprintf(str,"%s qdisc add dev %s parent 1:3 handle 3 %s",tc,lan,qos_leaf);
1631 safe_run(str);
1632
1633 sprintf(str,"%s qdisc add dev %s parent 1:3 handle 3 %s",tc,wan,qos_leaf);
1634 safe_run(str);
1635 }
1636
1637 /* tc handle 1 fw flowid */
1638 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle 3 fw flowid 1:3",tc,lan);
1639 safe_run(str);
1640
1641 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle 3 fw flowid 1:3",tc,wan);
1642 safe_run(str);
1643
1644 run_restore();
1645
1646 if (log_file) fclose(log_file);
1647 return 0;
1648
1649 /* that's all folks, thank you for reading it all the way up to this point ;-) */
1650 /* bad luck C<<1 is not yet finished, I promise no sprintf() next time... */
1651 }
This page took 1.397684 seconds and 5 git commands to generate.