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