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