d8eaef2d44f4dee405ef9d603ce9b082cffdca43
[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
556 free(cmd);
557 }
558
559 /* ========== This function executes, logs OR ALSO prints command ========== */
560
561 void safe_run(char *cmd)
562 {
563 if(dry_run) printf("\n=>%s\n",cmd); else system(cmd);
564 if(log_file) fprintf(log_file,"%s\n",cmd);
565 }
566
567 void save_line(char *line)
568 {
569 fprintf(iptables_file,"%s\n",line);
570 }
571
572 void run_restore(void)
573 {
574 char *restor, *str;
575 string(restor,STRLEN);
576
577 /*-----------------------------------------------------------------*/
578 printf("Running %s <%s ...\n",iptablesrestore,iptablesfile);
579 /*-----------------------------------------------------------------*/
580
581 save_line("COMMIT");
582 fclose(iptables_file);
583 if(dry_run)
584 {
585 parse(iptablesfile)
586 {
587 str=_;
588 printf("%s\n", str);
589 }done;
590 }
591
592 sprintf(restor,"%s <%s",iptablesrestore, iptablesfile);
593 safe_run(restor);
594
595 free(restor);
596 }
597
598 /* == This function strips extra characters after IP address and stores it = */
599
600 void parse_ip(char *str)
601 {
602 char *ptr=str,*ipaddr=NULL,*ipname=NULL;;
603
604 while(*ptr && *ptr!=' ' && *ptr!=9)
605 ptr++;
606
607 *ptr=0;
608 ipaddr=str;
609 ptr++;
610 while(*ptr && (*ptr==' ' || *ptr==9))
611 ptr++;
612 ipname=ptr;
613 while(*ptr && *ptr!=' ' && *ptr!=9)
614 ptr++;
615 *ptr=0;
616
617 find(ip,ips,eq(ip->addr,ipaddr)); else TheIP();
618 ip->addr=ipaddr;
619 ip->name=ipname;
620 }
621
622 char *parse_datafile_line(char *str)
623 {
624 char *ptr=strchr(str,' ');
625
626 if(ptr)
627 {
628 *ptr=0;
629 ptr++;
630 return ptr;
631 }
632 else
633 return NULL;
634 }
635
636 struct IpLog
637 {
638 char *name;
639 long traffic;
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;
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
651 string(str,STRLEN);
652
653 if(argv[1][1]=='l') /* -l */
654 {
655 if(argc<4)
656 {
657 puts("Missing parameter(s)!\nUsage: prometheus -l Mmm YYYY (Mmm=Jan-Dec or Year, YYYY=year)");
658 exit(-1);
659 }
660 else
661 {
662 month=argv[2];
663 if(eq(month,"Year")) any_month=1;
664 year=argv[3];
665 }
666 }
667 else
668 {
669 time_t t = time(NULL) - 3600*24 ; /* yesterday's timestamp*/
670 struct tm *timep = localtime(&t);
671
672 if(argv[1][1]=='m') /* -m yestarday - month */
673 {
674 strftime(mstr, 4, "%b", timep);
675 month=mstr;
676 strftime(ystr, 5, "%Y", timep);
677 year=ystr;
678 }
679 else /* -y yesterday - year */
680 {
681 month="Year";
682 any_month=1;
683 strftime(ystr, 5, "%Y", timep);
684 year=ystr;
685 }
686 }
687 printf("Analysing traffic for %s %s ...\n",month,year);
688
689 sprintf(str,"%s %s/*.log",ls,log_dir);
690 shell(str);
691
692 input(str,STRLEN)
693 {
694 ptr=strrchr(str,'\n');
695 if(ptr) *ptr='\0';
696 printf("Parsing %s ...",str);
697 accept_month=0;
698 traffic_month=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 9...*/
707 case 7:
708 case 8:
709 case 9: if (isalnum(*ptr)) /* alphanumeric string = date, just one*/
710 {
711 valid_columns(ptr2,ptr,' ',col2) switch(col2)
712 {
713 case 2: if(any_month || eq(ptr2,month)) m_ok=1; break;
714 case 5: if(eq(ptr2,year)) y_ok=1; break;
715 }
716 }
717 }
718
719 if(y_ok && m_ok)
720 {
721 traffic_month+=traffic;
722 accept_month=1;
723 }
724 }
725 done;
726
727 if(accept_month)
728 {
729 create(iplog,IpLog);
730 iplog->name=name;
731 iplog->traffic=traffic_month;
732 insert(iplog,iplogs,desc_order_by,traffic);
733 printf(" %ld MB\n",iplog->traffic);
734 }
735 else
736 puts(" no records.");
737 }
738 sprintf(str,"%s/%s-%s.html",html_log_dir,year,month);
739 printf("Writing %s ...",str);
740 f=fopen(str,"w");
741 if(f)
742 {
743 fprintf(f,"<table border><tr><th colspan=\"4\">Data transfers - %s %s</th></tr>\n ",month,year);
744 every(iplog,iplogs)
745 if(iplog->traffic)
746 {
747 fprintf(f,"<tr><td align=\"right\">%d</td><th>%s</td><td align=\"right\">%ld MB</td><th align=\"right\">%ld GB</th></tr>\n",i++,iplog->name,iplog->traffic,iplog->traffic>>10);
748 total+=iplog->traffic>>10;
749 }
750 fprintf(f,"<tr><th colspan=\"3\" align=\"left\">Total:</th><th align=\"right\">%ld GB</th></tr>\n",total);
751 fputs("</table>\n",f);
752 fclose(f);
753 puts(" done.");
754 }
755 }
756
757
758 /*-----------------------------------------------------------------*/
759 /* Are you looking for int main (int argc, char **argv) ? :-)) */
760 /*-----------------------------------------------------------------*/
761
762 program
763 {
764 int i=0;
765 FILE *f=NULL;
766 char *str, *ptr, *d;
767 char *substring;
768 int class_count=0,ip_count=0;
769 int parent=1;
770 int just_flush=0;
771 int nodelay=0;
772 int just_preview=0; /* preview - generate just stats */
773 int just_logs=0; /* just parse logs */
774
775 char *chain_forward, *chain_postrouting;
776 char *althosts=NULL;
777
778 printf("\n\
779 Prometheus QoS - \"fair-per-IP\" Quality of Service setup utility.\n\
780 Version %s - Copyright (C)2005-2008 Michael Polak (xChaos)\n\
781 iptables-restore & burst tunning & classify modification 0.7d by Ludva\n\
782 Credit: CZFree.Net, Martin Devera, Netdave, Aquarius, Gandalf\n\n",version);
783
784 /*----- Boring... we have to check command line options first: ----*/
785
786 arguments
787 {
788 argument("-c") { nextargument(config); }
789 argument("-h") { nextargument(althosts);}
790 argument("-d") { dry_run=1; }
791 argument("-f") { just_flush=1; }
792 argument("-9") { just_flush=9; }
793 argument("-p") { just_preview=1; }
794 argument("-n") { nodelay=1; }
795 argument("-l") { just_logs=1; }
796 argument("-m") { just_logs=1; }
797 argument("-y") { just_logs=1; }
798 argument("-?") { help(); exit(0); }
799 argument("--help") { help(); exit(0); }
800 argument("-v") { exit(0); }
801 argument("--version") { exit(0); }
802 }
803
804 if(dry_run)
805 puts("*** THIS IS JUST DRY RUN ! ***\n");
806
807 date(d); /* this is typical cll1.h macro */
808
809 /*-----------------------------------------------------------------*/
810 printf("Parsing configuration file %s ...\n", config);
811 /*-----------------------------------------------------------------*/
812 get_config(config);
813
814 if(just_logs)
815 {
816 parse_ip_log(argc,argv);
817 exit(0);
818 }
819
820 if(althosts) hosts=althosts;
821
822 if(just_flush<9)
823 {
824 /*-----------------------------------------------------------------*/
825 puts("Parsing iptables verbose output ...");
826 /*-----------------------------------------------------------------*/
827 get_traffic_statistics();
828 }
829
830 /*-----------------------------------------------------------------*/
831 printf("Parsing class defintion file %s ...\n", hosts);
832 /*-----------------------------------------------------------------*/
833 int groupidx = FIRSTGROUPID;
834 parse(hosts)
835 {
836 str=_;
837
838 if(*str<'0' || *str>'9')
839 continue;
840
841 //Does this IP share QoS class with some other ?
842 substring=strstr(str,"sharing-");
843 if(substring)
844 {
845 substring+=8; //"sharing-"
846 parse_ip(str);
847 ip_count++;
848 ip->sharing=substring;
849 ip->keyword=defaultkeyword; /* settings for default keyword */
850 while(*substring && *substring!='\n')
851 substring++;
852 *substring=0;
853 }
854 else
855 {
856 //Do we have to create new QoS class for this IP ?
857
858 find(keyword,keywords,(substring=strstr(str,keyword->key)))
859 {
860 parse_ip(str);
861 ip_count++;
862 ip->keyword=keyword;
863 keyword->ip_count++;
864 ip->prio=keyword->default_prio;
865 substring+=strlen(keyword->key)+1;
866 ptr=substring;
867 while(*ptr && *ptr!='-')
868 ptr++;
869 if(*ptr=='-')
870 {
871 *ptr=0;
872 ip->max=ip->desired=atoi(ptr+1);
873 }
874 ip->min=atoi(substring);
875 if(ip->min<=0)
876 {
877 printf(" %s: Illegal value of minimum bandwidth 0 kbps, using %d kbps\n",str,free_min);
878 ip->min=free_min;
879 }
880 if(ip->max<=ip->min)
881 {
882 ip->fixedprio=1;
883 ip->max=ip->min+ip->keyword->reserve_min;
884 }
885 else
886 {
887 ip->max-=ip->keyword->reserve_max;
888
889 /*
890 if(ip->keyword->divide_max>1)
891 ip->max=ip->min+(ip->max-ip->min)/ip->keyword->divide_max;
892 if(ip->keyword->htb_ceil_bonus_divide>0)
893 ip->max+=ip->max/ip->keyword->htb_ceil_bonus_divide;
894 */
895 if(ip->max<ip->min)
896 ip->max=ip->min;
897 }
898 ip->mark=FIRSTIPCLASS+1+class_count++;
899
900 find(group,groups,group->min==ip->min)
901 {
902 group->count++;
903 group->desired+=ip->min;
904 ip->group = group->id;
905 }
906 else
907 {
908 create(group,Group);
909 group->min=ip->min;
910 group->id = groupidx++;
911 ip->group = group->id;
912
913 if(group->min<8) group->min=8;
914 /* Warning - this is maybe because of primitive tc namespace, can be fixed */
915 /* it is because class IDs are derived from min. bandwidth. - xCh */
916 //if(group->min>MAX_GUARANTED_KBPS) group->min=MAX_GUARANTED_KBPS;
917
918 group->count=1;
919 group->desired=ip->min;
920 insert(group,groups,desc_order_by,min);
921 }
922 }//endif keyword-
923 }//endif sharing-
924 }
925 fail
926 {
927 perror(hosts);
928 exit(-1);
929 }
930 done;
931
932 /*-----------------------------------------------------------------*/
933 /* cll1.h - let's allocate brand new character buffer... */
934 /*-----------------------------------------------------------------*/
935 string(str,STRLEN);
936
937 /*-----------------------------------------------------------------*/
938 puts("Resolving shared connections ...");
939 /*-----------------------------------------------------------------*/
940 search(ip,ips,ip->sharing)
941 {
942 search(sharedip,ips,eq(sharedip->name,ip->sharing))
943 {
944 sharedip->traffic+=ip->traffic;
945 ip->traffic=0;
946 ip->mark=sharedip->mark;
947 break;
948 }
949 if(!sharedip)
950 printf("Unresolved shared connection: %s %s sharing-%s\n",ip->addr,ip->name,ip->sharing);
951 }
952
953 if(enable_credit && just_flush<9)
954 {
955 /*-----------------------------------------------------------------*/
956 printf("Parsing credit file %s ...\n", credit);
957 /*-----------------------------------------------------------------*/
958 parse(credit)
959 {
960 ptr=parse_datafile_line(_);
961 if(ptr)
962 {
963 find(ip,ips,eq(ip->addr,_))
964 sscanf(ptr,"%Lu",&(ip->credit));
965 }
966 }
967 done;
968 }
969
970 if(!just_preview)
971 {
972 /*-----------------------------------------------------------------*/
973 puts("Initializing iptables and tc classes ...");
974 /*-----------------------------------------------------------------*/
975
976 iptables_file=fopen(iptablesfile,"w");
977 if (iptables_file == NULL) {
978 puts("Cannot open iptablesfile!");
979 exit(-1);
980 }
981
982 log_file=fopen(cmdlog,"w");
983 if (log_file == NULL) {
984 puts("Cannot open logfile!");
985 exit(-1);
986 }
987
988 save_line(iptablespreamble);
989 run_restore();
990
991 sprintf(str,"%s qdisc del dev %s root 2>/dev/null",tc,lan);
992 safe_run(str);
993
994 sprintf(str,"%s qdisc del dev %s root 2>/dev/null",tc,wan);
995 safe_run(str);
996
997 iptables_file=fopen(iptablesfile,"w");
998 save_line(iptablespreamble);
999
1000 if(qos_free_zone && *qos_free_zone!='0')
1001 {
1002 char *chain;
1003
1004 sprintf(str,"-A FORWARD -d %s -o %s -j ACCEPT", qos_free_zone, wan);
1005 save_line(str);
1006
1007 if(qos_proxy)
1008 {
1009 save_line(":post_noproxy - [0:0]");
1010 sprintf(str,"-A POSTROUTING -p ! tcp -o %s -j post_noproxy", lan);
1011 save_line(str);
1012 sprintf(str,"-A POSTROUTING -s ! %s -o %s -j post_noproxy", proxy_ip, lan);
1013 save_line(str);
1014 sprintf(str,"-A POSTROUTING -s %s -p tcp --sport ! %d -o %s -j post_noproxy", proxy_ip, proxy_port, lan);
1015 save_line(str);
1016
1017 chain="post_noproxy";
1018 }
1019 else
1020 chain="POSTROUTING";
1021
1022 sprintf(str,"-A %s -s %s -o %s -j ACCEPT", chain, qos_free_zone, lan);
1023 save_line(str);
1024 }
1025
1026 if(ip_count>idxtable_treshold1 && !just_flush)
1027 {
1028 int idxcount=0, bitmask=32-idxtable_bitmask1; /* default net mask: 255.255.255.240 */
1029 char *subnet, *buf;
1030 /*-----------------------------------------------------------------*/
1031 printf("Detected %d addresses - indexing iptables rules to improve performance...\n",ip_count);
1032 /*-----------------------------------------------------------------*/
1033
1034 save_line(":post_common - [0:0]");
1035 save_line(":forw_common - [0:0]");
1036
1037 search(ip,ips,ip->addr && *(ip->addr) && !eq(ip->addr,"0.0.0.0/0"))
1038 {
1039 buf=hash_id(ip->addr,bitmask);
1040 find(idx,idxs,eq(idx->id,buf))
1041 idx->children++;
1042 else
1043 {
1044 create(idx,Index);
1045 idx->addr=ip->addr;
1046 idx->id=buf;
1047 idx->bitmask=bitmask;
1048 idx->parent=NULL;
1049 idx->children=0;
1050 idxcount++;
1051 push(idx,idxs);
1052 }
1053 }
1054
1055 /* brutal perfomance optimalization */
1056 while(idxcount>idxtable_treshold2 && bitmask>2*idxtable_bitmask2)
1057 {
1058 bitmask-=idxtable_bitmask2;
1059 idxcount=0;
1060 search(idx,idxs,idx->parent==NULL)
1061 {
1062 buf=hash_id(idx->addr,bitmask);
1063 find(metaindex,idxs,eq(metaindex->id,buf))
1064 metaindex->children++;
1065 else
1066 {
1067 create(metaindex,Index);
1068 metaindex->addr=idx->addr;
1069 metaindex->id=buf;
1070 metaindex->bitmask=bitmask;
1071 metaindex->parent=NULL;
1072 metaindex->children=0;
1073 idxcount++;
1074 push(metaindex,idxs);
1075 }
1076 idx->parent=metaindex;
1077 }
1078 }
1079
1080 /* this should slightly optimize throughout ... */
1081 sort(idx,idxs,desc_order_by,children);
1082 sort(idx,idxs,order_by,bitmask);
1083
1084 i=0;
1085 every(idx,idxs)
1086 {
1087 subnet=subnet_id(idx->addr,idx->bitmask);
1088 printf("%d: %s/%d\n",++i,subnet,idx->bitmask);
1089
1090 sprintf(str,":post_%s - [0:0]", idx->id);
1091 save_line(str);
1092
1093 sprintf(str,":forw_%s - [0:0]", idx->id);
1094 save_line(str);
1095
1096 if(idx->parent)
1097 {
1098 string(buf,strlen(idx->parent->id)+6);
1099 sprintf(buf,"post_%s",idx->parent->id);
1100 }
1101 else
1102 buf="POSTROUTING";
1103
1104 sprintf(str,"-A %s -d %s/%d -o %s -j post_%s", buf, subnet, idx->bitmask, lan, idx->id);
1105 save_line(str);
1106
1107 sprintf(str,"-A %s -d %s/%d -o %s -j post_common", buf, subnet, idx->bitmask, lan);
1108 save_line(str);
1109
1110 if(idx->parent)
1111 {
1112 string(buf,strlen(idx->parent->id)+6);
1113 sprintf(buf,"forw_%s",idx->parent->id);
1114 }
1115 else
1116 buf="FORWARD";
1117
1118 sprintf(str,"-A %s -s %s/%d -o %s -j forw_%s", buf, subnet, idx->bitmask, wan, idx->id);
1119 save_line(str);
1120
1121 sprintf(str,"-A %s -s %s/%d -o %s -j forw_common", buf, subnet, idx->bitmask, wan);
1122 save_line(str);
1123 }
1124 printf("Total indexed iptables chains created: %d\n", i);
1125
1126 sprintf(str,"-A FORWARD -o %s -j forw_common", wan);
1127 save_line(str);
1128
1129 sprintf(str,"-A POSTROUTING -o %s -j post_common", lan);
1130 save_line(str);
1131 }
1132
1133 }
1134
1135 if(just_flush)
1136 {
1137 fclose(iptables_file);
1138 if (log_file) fclose(log_file);
1139 puts("Just flushed iptables and tc classes - now exiting ...");
1140 exit(0);
1141 }
1142
1143 if(!just_preview)
1144 {
1145 if(!dry_run && !nodelay && qos_free_delay)
1146 {
1147 printf("Flushed iptables and tc classes - now sleeping for %d seconds...\n",qos_free_delay);
1148 sleep(qos_free_delay);
1149 }
1150
1151 sprintf(str,"%s qdisc add dev %s root handle 1: htb r2q %d default 1",tc,lan,htb_r2q);
1152 safe_run(str);
1153
1154 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);
1155 safe_run(str);
1156
1157 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);
1158 safe_run(str);
1159
1160 sprintf(str,"%s qdisc add dev %s root handle 1: htb r2q %d default 1",tc,wan,htb_r2q);
1161 safe_run(str);
1162
1163 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);
1164 safe_run(str);
1165
1166 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);
1167 safe_run(str);
1168 }
1169
1170 /*-----------------------------------------------------------------*/
1171 puts("Locating suckers and generating root classes ...");
1172 /*-----------------------------------------------------------------*/
1173 sort(ip,ips,desc_order_by,traffic);
1174
1175
1176 /*-----------------------------------------------------------------*/
1177 /* sub-scope - local variables */
1178 {
1179 long long int rate=line;
1180 long long int max=line;
1181 int group_count=0;
1182 FILE *credit_file=NULL;
1183
1184 if(!just_preview && !dry_run && enable_credit) credit_file=fopen(credit,"w");
1185
1186 every(group,groups)
1187 {
1188 if(!just_preview)
1189 {
1190
1191 //download
1192 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",
1193 tc, lan, parent, group->id, rate, max, burst_group, group->desired);
1194 safe_run(str);
1195
1196 //upload
1197 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",
1198 tc, wan, parent, group->id, rate*up/line, max*up/line, burst_group, group->desired);
1199 safe_run(str);
1200 }
1201
1202 if(group_count++<max_nesting) parent=group->id;
1203
1204 rate-=digital_divide*group->min;
1205 if(rate<group->min)rate=group->min;
1206
1207 /*shaping of aggresive downloaders, with credit file support */
1208 if(use_credit)
1209 {
1210 int group_rate=group->min, priority_sequence=magic_priorities+1;
1211
1212 search(ip, ips, ip->min==group->min && ip->max>ip->min)
1213 {
1214 if( ip->keyword->data_limit && !ip->fixedprio &&
1215 ip->traffic>ip->credit+
1216 (ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20)) )
1217 {
1218 if(group_rate<ip->max) ip->max=group_rate;
1219 group_rate+=magic_treshold;
1220 ip->prio=magic_priorities+2;
1221 if(ip->prio<3) ip->prio=3;
1222 }
1223 else
1224 {
1225 if( ip->keyword->data_prio && !ip->fixedprio &&
1226 ip->traffic>ip->credit+
1227 (ip->min*ip->keyword->data_prio+(ip->keyword->fixed_prio<<20)) )
1228 {
1229 ip->prio=priority_sequence--;
1230 if(ip->prio<2) ip->prio=2;
1231 }
1232
1233 if(credit_file)
1234 {
1235 unsigned long long lcredit=0;
1236
1237 if((ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))>ip->traffic)
1238 lcredit=(ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))-ip->traffic;
1239 fprintf(credit_file,"%s %Lu\n",ip->addr,lcredit);
1240 }
1241 }
1242 }
1243
1244 }
1245 }
1246 if(credit_file)fclose(credit_file);
1247 }
1248
1249 if(just_preview)
1250 {
1251 f=fopen(preview,"w");
1252 ptr=preview;
1253 }
1254 else if(!dry_run && !just_flush)
1255 {
1256 /*-----------------------------------------------------------------*/
1257 printf("Writing data transfer database ...\n");
1258 /*-----------------------------------------------------------------*/
1259 f=fopen("/var/run/prometheus.previous","w");
1260 if(f)
1261 {
1262 search(ip,ips,ip->traffic || ip->direct || ip->proxy ||ip->upload)
1263 fprintf(f,"%s %Lu %Lu %Lu %Lu\n",ip->addr,ip->traffic,ip->direct,ip->proxy,ip->upload);
1264 fclose(f);
1265 }
1266
1267 f=fopen(html,"w");
1268 ptr=html;
1269 }
1270
1271 if(f)
1272 {
1273 int total=0;
1274 int count=1;
1275 i=0;
1276
1277 /*-----------------------------------------------------------------*/
1278 printf("Sorting data and generating statistics page %s ...\n",ptr);
1279 /*-----------------------------------------------------------------*/
1280
1281 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);
1282 fprintf(f,"<th colspan=\"%d\">data limits</th>\n",keywordcount);
1283 fputs("</tr>\n",f);
1284 every(group,groups)
1285 {
1286 #ifdef DEBUG
1287 printf("%d k group: %d bandwidth requested: %d k\n",group->min,group->count,group->desired);
1288 #endif
1289 fprintf(f,"<tr><td align=\"right\">%d</td><td align=\"right\">%d k</td>",count,group->min);
1290 fprintf(f,"<td align=\"right\">%d</td><td align=\"right\">%d k</td>",group->count,group->desired);
1291
1292 every(keyword,keywords)
1293 fprintf(f,"<td align=\"right\"><font color=\"#%s\">%d M</font></td>",keyword->html_color,group->min*keyword->data_limit);
1294
1295 i+=group->desired;
1296 total+=group->count;
1297 count++;
1298 }
1299 #ifdef DEBUG
1300 printf("Total groups: %d Total bandwidth requested: %d k\nAGGREGATION: 1/%d\n",count,i,i/line);
1301 #endif
1302 fprintf(f,"<tr><th colspan=\"2\" align=\"left\">Line %Ld k</td>",line);
1303 fprintf(f,"<th align=\"right\">%d</td><th align=\"right\">%d k</td>",total,i);
1304
1305 every(keyword,keywords)
1306 fprintf(f,"<th align=\"right\">%d IPs</th>",keyword->ip_count);
1307
1308 fprintf(f,"</tr><tr><th colspan=\"4\">Aggregation 1/%d</th>\n",(int)(0.5+i/line));
1309 fprintf(f,"<th colspan=\"%d\">%d traffic classes</th></tr>\n",keywordcount,total);
1310
1311 fputs("</table>\n",f);
1312 }
1313 else if(!dry_run && !just_flush)
1314 perror(html);
1315
1316 i=1;
1317 if(f)
1318 {
1319 unsigned long long total=0, total_direct=0, total_proxy=0, total_upload=0, tmp_sum=0;
1320 int active_classes=0;
1321 int colspan;
1322 FILE *iplog;
1323 struct Sum {unsigned long long l; int i; list(Sum);} *sum,*sums=NULL;
1324
1325 if(qos_proxy)
1326 colspan=12;
1327 else
1328 colspan=11;
1329
1330 fprintf(f,"<p><table border>\n<tr><th colspan=\"%d\">%s",colspan,title);
1331 fprintf(f," (%s)</th></tr>\n", d);
1332 fputs("<tr><td align=\"right\">#</td><td>hostname</td>\
1333 <td align=\"right\">credit</td>\
1334 <td align=\"right\">limit</td>\
1335 <td align=\"right\">total</td>\
1336 <td align=\"right\">direct</td>\n",f);
1337 if(qos_proxy)
1338 fputs("<td align=\"right\">proxy</td>\n",f);
1339 fputs("<td align=\"right\">upload</td>\
1340 <td align=\"right\">minimum</td>\
1341 <td align=\"right\">desired</td>\
1342 <td align=\"right\">maximum</td>\
1343 <td>prio</td></tr>\n",f);
1344
1345 every(ip,ips)
1346 {
1347 char *f1="", *f2="";
1348 if(ip->max<ip->desired)
1349 {
1350 f1="<font color=\"red\">";
1351 f2="</font>";
1352 }
1353 else if(ip->prio>1)
1354 {
1355 f1="<font color=\"brown\">";
1356 f2="</font>";
1357 }
1358
1359 #ifdef DEBUG
1360 printf("%03d. %-22s %10Lu (%d/%d)\n",i ,ip->name, ip->traffic, ip->min, ip->max);
1361 #endif
1362 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",
1363 ip->name, i, log_url, ip->name, ip->name, ip->credit);
1364 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)));
1365 fprintf(f,"<td align=\"right\">%s%Lu M%s</td><td align=\"right\">%Lu M</td>\n", f1, ip->traffic, f2, ip->direct);
1366 if(qos_proxy)
1367 fprintf(f,"<td align=\"right\">%Lu M</td>\n", ip->proxy);
1368 fprintf(f,"<td align=\"right\">%Lu M</td>\n", ip->upload);
1369 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);
1370 total+=ip->traffic;
1371 total_direct+=ip->direct;
1372 total_proxy+=ip->proxy;
1373 total_upload+=ip->upload;
1374 if(ip->traffic>0)
1375 {
1376 active_classes++;
1377 tmp_sum+=ip->traffic;
1378 create(sum,Sum);
1379 sum->l=tmp_sum;
1380 sum->i=active_classes;
1381 insert(sum,sums,order_by,i);
1382 }
1383
1384 i++;
1385
1386 if(!just_preview)
1387 {
1388 sprintf(str,"%s/%s.log",log_dir,ip->name);
1389 iplog=fopen(str,"a");
1390 if(iplog)
1391 {
1392 fprintf(iplog,"%ld\t%s\t%Lu\t%Lu\t%Lu\t%Lu\t%d\t%d\t%d\t%s",
1393 time(NULL),ip->name,ip->traffic,ip->direct,ip->proxy,ip->upload,ip->min,ip->max,ip->desired,d); /* d = date*/
1394 fclose(iplog);
1395 }
1396 }
1397
1398 }
1399 fprintf(f,"<tr><th colspan=\"4 \"align=\"left\">SUMMARY:</td>");
1400 fprintf(f,"<th align=\"right\">%Lu M</th>\
1401 <th align=\"right\">%Lu M</th>\n", total, total_direct);
1402 if(qos_proxy)
1403 fprintf(f,"<th align=\"right\">%Lu M</th>\n", total_proxy);
1404 fprintf(f,"<th align=\"right\">%Lu M</th>", total_upload);
1405 fputs("<td colspan=\"4\"></td></th>\n</table>\n",f);
1406
1407 if(active_classes>10)
1408 {
1409 fputs("<a name=\"erp\"></a><p><table border><tr><th colspan=\"5\">Enterprise Research and Planning (ERP)</th></tr>\n",f);
1410 fputs("<tr><td>Analytic category</td>\n",f);
1411 fputs("<td colspan=\"2\" align=\"center\">Active Classes</td><td colspan=\"2\" align=\"center\">Data transfers</td></tr>\n",f);
1412
1413 find(sum,sums,sum->l>=total/4)
1414 {
1415 fprintf(f,"<tr><td>Top 25%% of traffic</td>\n");
1416 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);
1417 }
1418
1419 find(sum,sums,sum->i==10)
1420 {
1421 fprintf(f,"<tr><td>Top 10 downloaders</td>\n");
1422 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);
1423 }
1424
1425 find(sum,sums,sum->l>=total/2)
1426 {
1427 fprintf(f,"<tr><td>Top 50%% of traffic</td>\n");
1428 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);
1429 }
1430
1431 find(sum,sums,sum->l>=4*total/5)
1432 {
1433 fprintf(f,"<tr><td>Top 80%% of traffic</td>\n");
1434 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);
1435 }
1436
1437 find (sum,sums,sum->i>=(active_classes+1)/5)
1438 {
1439 fprintf(f,"<tr><td>Top 20%% downloaders</td>\n");
1440 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);
1441 }
1442
1443 find(sum,sums,sum->i>=(active_classes+1)/4)
1444 {
1445 fprintf(f,"<tr><td>Top 25%% downloaders</td>\n");
1446 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);
1447 }
1448
1449 find(sum,sums,sum->i>=(active_classes+1)/2)
1450 {
1451 fprintf(f,"<tr><td>Top 50%% downloaders</td>\n");
1452 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);
1453 }
1454
1455 find(sum,sums,sum->i>=4*(active_classes+1)/5)
1456 {
1457 fprintf(f,"<tr><td>Top 80%% downloaders</td>\n");
1458 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);
1459 }
1460
1461 fprintf(f,"<tr><td>All users, all traffic</td>\n");
1462 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);
1463 fputs("</table>\n",f);
1464 }
1465 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);
1466 fclose(f);
1467 }
1468
1469 if(just_preview)
1470 {
1471 puts("Statistics preview generated (-p switch) - now exiting ...");
1472 exit(0);
1473 }
1474
1475 /*-----------------------------------------------------------------*/
1476 puts("Generating iptables and tc classes ...");
1477 /*-----------------------------------------------------------------*/
1478
1479 i=0;
1480 printf("%-22s %-15s mark\n","name","ip");
1481 search(ip,ips,ip->mark>0)
1482 {
1483
1484 if(idxs)
1485 {
1486 char *buf;
1487 duplicate(ip->addr,buf);
1488 buf=hash_id(ip->addr,32-idxtable_bitmask1);
1489
1490 string(chain_forward,6+strlen(buf));
1491 strcpy(chain_forward,"forw_");
1492 strcat(chain_forward,buf);
1493
1494 string(chain_postrouting,6+strlen(buf));
1495 strcpy(chain_postrouting,"post_");
1496 strcat(chain_postrouting,buf);
1497
1498 free(buf);
1499 }
1500 else
1501 {
1502 chain_forward="FORWARD";
1503 chain_postrouting="POSTROUTING";
1504 }
1505
1506 printf("%-22s %-16s %04d ", ip->name, ip->addr, ip->mark);
1507
1508 /* -------------------------------------------------------- mark download */
1509
1510 sprintf(str,"-A %s -d %s/32 -o %s -j %s%d",chain_postrouting,ip->addr,lan,mark_iptables,ip->mark);
1511 /*sprintf(str,"-A %s -d %s/32 -o %s -j MARK --set-mark %d",chain_postrouting,ip->addr,lan,ip->mark);*/
1512 /* -m limit --limit 1/s */
1513 save_line(str);
1514
1515 if(qos_proxy)
1516 {
1517 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);
1518 /*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);*/
1519 save_line(str);
1520 }
1521
1522 sprintf(str,"-A %s -d %s/32 -o %s -j ACCEPT",chain_postrouting,ip->addr,lan);
1523 save_line(str);
1524
1525 /* -------------------------------------------------------- mark upload */
1526 sprintf(str,"-A %s -s %s/32 -o %s -j %s%d",chain_forward,ip->addr,wan,mark_iptables,ip->mark);
1527 /* sprintf(str,"-A %s -s %s/32 -o %s -j MARK --set-mark %d",chain_forward,ip->addr,wan,ip->mark);*/
1528 save_line(str);
1529
1530 sprintf(str,"-A %s -s %s/32 -o %s -j ACCEPT",chain_forward,ip->addr,wan);
1531 save_line(str);
1532
1533 if(ip->min)
1534 {
1535 /* -------------------------------------------------------- download class */
1536 printf("(down: %dk-%dk ", ip->min, ip->max);
1537
1538 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);
1539 safe_run(str);
1540
1541 if (strcmpi(ip->keyword->leaf_discipline, "none")){
1542 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*/
1543 safe_run(str);
1544 }
1545
1546 if (filter_type == 1){
1547 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d", tc, lan, ip->mark, ip->mark);
1548 safe_run(str);
1549 }
1550
1551 /* -------------------------------------------------------- upload class */
1552 printf("up: %dk-%dk)\n", (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed),
1553 (int)((ip->max/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed));
1554
1555 sprintf(str,"%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d",
1556 tc, wan, ip->group, ip->mark,
1557 (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed),
1558 (int)((ip->max/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed), burst, ip->prio);
1559 safe_run(str);
1560
1561 if (strcmpi(ip->keyword->leaf_discipline, "none")){
1562 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*/
1563 safe_run(str);
1564 }
1565
1566 if (filter_type == 1){
1567 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d",tc, wan, ip->mark, ip->mark);
1568 safe_run(str);
1569 }
1570 }
1571 else
1572 printf("(sharing %s)\n", ip->sharing);
1573 i++;
1574 }
1575
1576
1577 if(idxs)
1578 {
1579 chain_forward="forw_common";
1580 chain_postrouting="post_common";
1581 }
1582 else
1583 {
1584 chain_forward="FORWARD";
1585 chain_postrouting="POSTROUTING";
1586 }
1587
1588 /* -------------------------------------------------------- mark download */
1589
1590 if(qos_proxy)
1591 {
1592 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);
1593 save_line(str);
1594 sprintf(str,"-A %s -s %s -p tcp --sport %d -o %s -j ACCEPT",chain_postrouting,proxy_ip,proxy_port,lan);
1595 save_line(str);
1596 }
1597 sprintf(str,"-A %s -o %s -j %s%d",chain_postrouting,lan,mark_iptables,3);
1598 save_line(str);
1599 sprintf(str,"-A %s -o %s -j ACCEPT",chain_postrouting,lan);
1600 save_line(str);
1601
1602 /* -------------------------------------------------------- mark upload */
1603 sprintf(str,"-A %s -o %s -j %s%d",chain_forward,wan,mark_iptables,3);
1604 save_line(str);
1605 sprintf(str,"-A %s -o %s -j ACCEPT",chain_forward,wan);
1606 save_line(str);
1607
1608 printf("Total IP count: %d\n", i);
1609
1610 /*-----------------------------------------------------------------*/
1611 puts("Generating free bandwith classes ...");
1612 /*-----------------------------------------------------------------*/
1613
1614 /* ---------------------------------------- tc - free bandwith shared class */
1615 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);
1616 safe_run(str);
1617
1618 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);
1619 safe_run(str);
1620
1621 /* tc SFQ */
1622 if (strcmpi(qos_leaf, "none")){
1623 sprintf(str,"%s qdisc add dev %s parent 1:3 handle 3 %s",tc,lan,qos_leaf);
1624 safe_run(str);
1625
1626 sprintf(str,"%s qdisc add dev %s parent 1:3 handle 3 %s",tc,wan,qos_leaf);
1627 safe_run(str);
1628 }
1629
1630 /* tc handle 1 fw flowid */
1631 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle 3 fw flowid 1:3",tc,lan);
1632 safe_run(str);
1633
1634 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle 3 fw flowid 1:3",tc,wan);
1635 safe_run(str);
1636
1637 run_restore();
1638
1639 if (log_file) fclose(log_file);
1640 return 0;
1641
1642 /* that's all folks, thank you for reading it all the way up to this point ;-) */
1643 /* bad luck C<<1 is not yet finished, I promise no sprintf() next time... */
1644 }
This page took 1.290864 seconds and 3 git commands to generate.