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