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