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