debug version
[svn/Prometheus-QoS/.git] / prometheus.c
... / ...
CommitLineData
1/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
2/* Prometheus QoS - you can "steal fire" from your ISP */\r
3/* "fair-per-IP" quality of service (QoS) utility */\r
4/* requires Linux 2.4.x or 2.6.x with HTB support */\r
5/* Copyright(C) 2005-2019 Michael Polak, Arachne Aerospace */\r
6/* iptables-restore support Copyright(C) 2007-2008 ludva */\r
7/* Credit: CZFree.Net,Martin Devera,Netdave,Aquarius,Gandalf */\r
8/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
9\r
10/* Modified by: xChaos, 20190212\r
11 ludva, 20080415\r
12 \r
13 Prometheus QoS is free software; you can redistribute it and/or\r
14 modify it under the terms of the GNU General Public License as \r
15 published by the Free Software Foundation; either version 2.1 of \r
16 the License, or (at your option) any later version.\r
17\r
18 Prometheus QoS is distributed in the hope that it will be useful,\r
19 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
21 General Public License for more details.\r
22\r
23 You should have received a copy of the GNU General Public License\r
24 along with Prometheus Qos; if not, write to the Free Software\r
25 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA \r
26 \r
27 GNU General Public License is located in file COPYING */\r
28\r
29#include "cll1-0.6.2.h"\r
30#include "ipstruct.h"\r
31\r
32const char *version = "0.9.1";\r
33\r
34/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
35/* Versions: 0.9.0 is development release, 1.0 will be "stable" */\r
36/* Official Trac URL: https://dev.arachne.cz/svn/prometheus */\r
37/* Official SVN URL: https://dev.arachne.cz/repos/prometheus */\r
38/* BTC donations account: 19rriLx8vR19wGefPaMhakqnCYNYwjLvxq */\r
39/* CZK donations account: 2900242944/2010 (transparent account) */\r
40/* Warning: unofficial Github mirror is not supported by author! */\r
41/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */\r
42\r
43const char *stats_html_signature = "<span class=\"small\">Statistics generated by Prometheus QoS version %s<br />GPL+Copyright(C)2005-2019 Michael Polak, <a target=\"_blank\" href=\"http://www.arachne.cz/\">Arachne Labs</a></span>\n";\r
44\r
45#define STRLEN 512\r
46#undef DEBUG\r
47\r
48/* ======= All path names are defined here (for RPM patch) ======= */\r
49\r
50const char *tc = "/sbin/tc"; /* requires tc with HTB support */\r
51const char *iptables = "/sbin/iptables"; /* requires iptables utility */\r
52const char *ip6tables = "/sbin/ip6tables"; /* requires iptables utility */\r
53const char *iptablessave = "/sbin/iptables-save"; /* not yet required */\r
54const char *iptablesrestore = "/sbin/iptables-restore"; /* requires iptables-restore */\r
55const char *ip6tablessave = "/sbin/ip6tables-save"; /* not yet required */\r
56const char *ip6tablesrestore = "/sbin/ip6tables-restore"; /* requires iptables-restore */\r
57const char *ls = "/bin/ls"; /* this is not user configurable :-) */\r
58\r
59char *config = "/etc/prometheus/prometheus.conf"; /* main configuration file */\r
60char *hosts = "/etc/prometheus/hosts"; /* per-IP bandwidth definition file */\r
61char *macrosfile = "/etc/prometheus/prometheus.macros"; /* rewrite rules for most common tariffs */\r
62char *upstreamfile = "/etc/prometheus/upstream.interfaces"; /* list of interfaces to manage */\r
63char *downstreamfile = "/etc/prometheus/downstream.interfaces"; /* list of interfaces to manage */\r
64char *qosfreefile = "/etc/prometheus/qosfree.interfaces"; /* list of interfaces to manage */\r
65char *iptablesfile = "/var/spool/prometheus.iptables"; /* temporary file for iptables-restore*/\r
66char *ip6tablesfile = "/var/spool/prometheus.ip6tables"; /* temporary file for ip6tables-restore*/\r
67char *credit = "/var/lib/misc/prometheus.credit"; /* credit log file */\r
68char *classmap = "/var/lib/misc/prometheus.classes"; /* credit log file */\r
69char *html = "/var/www/traffic.html"; /* hall of fame - html version */\r
70char *preview = "/var/www/preview.html"; /* hall of fame preview - html version */\r
71char *json_traffic = "/var/www/logs/traffic.json"; /* hall of fame - json version */\r
72char *json_preview = "/var/www/logs/preview.json"; /* hall of fame preview - json version */\r
73char *cmdlog = "/var/log/prometheuslog"; /* command log filename */\r
74char *log_dir = "/var/www/logs/"; /* log directory pathname, ended with slash */\r
75char *log_url = "/logs/"; /* log directory relative URI prefix (partial URL) */\r
76char *html_log_dir = "/var/www/logs/html/";\r
77\r
78char *jquery_url = "http://code.jquery.com/jquery-latest.js";\r
79char *lms_url = "/lms/?m=customerinfo&amp;id=";\r
80int use_jquery_popups = TRUE;\r
81int row_odd_even = 0; /*<tr class="odd/even"> */\r
82 \r
83/* === Configuraration file values defaults - stored in global variables ==== */\r
84\r
85int filter_type = 1; /*1 mark, 2 classify*/\r
86char *final_chain = "DROP"; /* REJECT would be better, but it is impossible in mangle */\r
87char *mark = "MARK";\r
88char *mark_iptables = "MARK --set-mark ";\r
89int dry_run = FALSE; /* preview - use puts() instead of system() */\r
90int mix_new_hosts = FALSE; /* execute only commands not already in log of iptables */\r
91char *log_file_mode = "w"; /* overwrite command and iptables log files, except append in -x mode */\r
92char *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]";\r
93char *ip6preamble = "-A FORWARD -p ipv6-icmp -j ACCEPT\n-A POSTROUTING -p ipv6-icmp -j ACCEPT\n-A FORWARD -s fe80::/10 -j ACCEPT\n-A FORWARD -d ff00::/8 -j ACCEPT\n-A POSTROUTING -s fe80::/10 -j ACCEPT\n-A POSTROUTING -d ff00::/8 -j ACCEPT";\r
94FILE *iptables_file = NULL;\r
95FILE *ip6tables_file = NULL;\r
96int enable_credit = TRUE; /* enable credit file */\r
97int use_credit = FALSE; /* use credit file (if enabled)*/\r
98char *title = "Hall of Fame - Greatest Suckers"; /* hall of fame title */\r
99int hall_of_fame = TRUE; /* enable hall of fame */\r
100char *medium = "1000Mbit"; /* 10Mbit/100Mbit ethernet */\r
101//obsolete: char *lan = "eth0"; /* LAN interface */\r
102//obsolete: char *lan_medium = "1000Mbit"; /* 10Mbit/100Mbit ethernet */\r
103char *ip6prefix = NULL; /* Prefix for global /48 IPv6 subnet */\r
104char *qos_leaf = "sfq perturb 5"; /* leaf discipline */\r
105char *qos_free_zone = NULL; /* QoS free zone */\r
106/* int qos_proxy = TRUE; include proxy port to QoS */\r
107int found_lmsid = FALSE; /* show links to users in LMS information system */\r
108int include_upload = TRUE; /* upload+download=total traffic */\r
109/* char *proxy_ip = "192.168.1.1/32"; our IP with proxy port */\r
110/* int proxy_port = 3128; proxy port number */\r
111//obsolete: long long int line = 1024; /* WAN/ISP download in kbps */\r
112//obsolete: long long int up = 1024; /* WAN/ISP upload in kbps */\r
113int free_min = 256; /* minimum guaranted bandwidth for all undefined hosts */\r
114int free_max = 512; /* maximum allowed bandwidth for all undefined hosts */\r
115int overlimit_min = 256; /* minimum guaranted bandwidth for all undefined hosts */\r
116int overlimit_max = 512; /* maximum allowed bandwidth for all undefined hosts */\r
117int qos_free_delay = 0; /* seconds to sleep before applying new QoS rules */\r
118int digital_divide = 2; /* controls digital divide weirdness ratio, 1...3 */ \r
119int max_nesting = 5; /* /include/uapi/linux/pkt_sched.h: #define TC_HTB_MAXDEPTH 8 [... - 3 parent classes] */\r
120int htb_r2q = 256; /* should work for leaf values 512 kbps to 8 Mbps */\r
121int burst = 8; /* HTB burst (in kbits) */\r
122int burst_main = 64;\r
123int burst_group = 32;\r
124int magic_treshold = 8; /* reduce ceil by X*magic_treshhold kbps (hard shaping) */\r
125int keywordcount = 0;\r
126int class_count = 0;\r
127int ip_count = 0;\r
128FILE *log_file = NULL;\r
129char *kwd = "via-prometheus"; /* /etc/hosts comment, eg. #qos-64-128 */\r
130\r
131const int highest_priority = 0; /* highest HTB priority (HTB built-in value is 0) */\r
132const int lowest_priority = 7; /* lowest HTB priority /include/uapi/linux/pkt_sched.h: #define TC_HTB_NUMPRIO 8 */\r
133const int idxtable_treshold1 = 24; /* this is no longer configurable */\r
134const int idxtable_treshold2 = 12; /* this is no longer configurable */\r
135const int idxtable_bitmask1 = 3; /* this is no longer configurable */\r
136const int idxtable_bitmask2 = 3; /* this is no longer configurable */\r
137\r
138struct IP *ips = NULL, *networks = NULL, *ip, *sharedip;\r
139struct Group *groups = NULL, *group;\r
140struct Keyword *keyword, *defaultkeyword=NULL, *keywords = NULL;\r
141struct Macro *macro, *macros = NULL;\r
142struct Index *idxs = NULL, *idx, *metaindex;\r
143struct Interface *interfaces = NULL, *interface;\r
144struct QosFreeInterface *qosfreeinterfaces = NULL, *qosfreeinterface;\r
145struct Textfile *previous_commands = NULL, *previous_iptables = NULL, *previous_ip6tables = NULL, *previous_classmap = NULL, *textline;\r
146\r
147#define FREE_CLASS 3\r
148#define OVERLIMIT_CLASS 4\r
149\r
150void help(void);\r
151/* implemented in help.c */
152\r
153void get_traffic_statistics(const char *whichiptables, int ipv6);\r
154/* implemented in parseiptables.c */\r
155\r
156void parse_ip_log(int argc, char **argv);\r
157/* implemented in parselog.c */\r
158\r
159void parse_hosts(char *hosts);\r
160/* implemented in parsehosts.c */\r
161\r
162void write_json_traffic(char *json);\r
163/* implemented in json.c */\r
164\r
165void write_htmlandlogs(char *html, char *d, int total, int just_preview);\r
166/* implemented in htmlandlogs.c */\r
167\r
168void analyse_topology(char *traceroute);\r
169/* implemented in networks.c */\r
170\r
171char *parse_datafile_line(char *str);\r
172/* implemented in utils.c */\r
173\r
174time_t get_mtime(const char *path);\r
175/* implemented in utils.c */\r
176\r
177const char *tr_odd_even(void)\r
178{\r
179 row_odd_even = 1 - row_odd_even;\r
180 if(row_odd_even)\r
181 {\r
182 return "<tr class=\"even\">\n";\r
183 }\r
184 else\r
185 {\r
186 return "<tr class=\"odd\">\n";\r
187 }\r
188}\r
189\r
190/* ====== iptables indexes are used to reduce complexity to log8(N) ===== */\r
191\r
192char *index_id(char *ip, int bitmask);\r
193/* function implemented in ipv4subnets.c */\r
194\r
195char *subnet_id(char *ip, int bitmask);\r
196/* function implemented in ipv4subnets.c */\r
197\r
198char *index6_id(char *ip, int bitmask);\r
199/* function implemented in ipv6subnets.c */\r
200\r
201char *subnet6_id(char *ip, int bitmask);\r
202/* function implemented in ipv6subnets.c */\r
203\r
204/* ================= Let's parse configuration file here ================ */\r
205\r
206void reject_config_and_exit(char *filename)\r
207{\r
208 printf("Configuration file %s rejected - abnormal exit.",filename);\r
209 exit(-1);\r
210}\r
211\r
212void get_config(char *config_filename)\r
213{\r
214 char *cnf="mark";\r
215 \r
216 printf("Configured keywords: ");\r
217 parse(config_filename)\r
218 {\r
219 option("keyword",kwd);\r
220 if(kwd)\r
221 {\r
222 printf("%s ",kwd);\r
223\r
224 create(keyword,Keyword);\r
225 keyword->key = kwd;\r
226 keyword->asymetry_ratio = 1; /* ratio for ADSL-like upload */\r
227 keyword->asymetry_fixed = 0; /* fixed treshold for ADSL-like upload */\r
228 keyword->data_limit = 8; /* hard shaping: apply magic_treshold if max*data_limit MB exceeded */\r
229 keyword->data_prio = 4; /* soft shaping (qos): reduce HTB prio if max*data_prio MB exceeded */\r
230 keyword->fixed_limit = 0; /* fixed data limit for setting lower HTB ceil */\r
231 keyword->fixed_prio = 0; /* fixed data limit for setting lower HTB prio */\r
232 keyword->reserve_min = 8; /* bonus for nominal HTB rate bandwidth (in kbps) */\r
233 keyword->reserve_max = 0; /* malus for nominal HTB ceil (in kbps) */\r
234 keyword->default_prio = highest_priority+1;\r
235 keyword->download_aggregation = keyword->upload_aggregation = 0; /* disable by default */\r
236 keyword->html_color = "000000";\r
237 keyword->ip_count = 0;\r
238 keyword->leaf_discipline = "";\r
239 keyword->allowed_avgmtu = 0;\r
240\r
241 push(keyword, keywords);\r
242 if(!defaultkeyword)\r
243 {\r
244 defaultkeyword = keyword;\r
245 }\r
246 keywordcount++;\r
247 \r
248 kwd=NULL;\r
249 }\r
250 else\r
251 {\r
252 for_each(keyword,keywords)\r
253 {\r
254 int l=strlen(keyword->key);\r
255\r
256 if(!strncmp(keyword->key,_,l) && strlen(_)>l+2)\r
257 {\r
258 char *tmptr=_; /* <---- l+1 ----> */\r
259 _+=l+1; /* via-prometheus-asymetry-ratio, etc. */\r
260 foption("asymetry-ratio", keyword->asymetry_ratio);\r
261 ioption("asymetry-treshold", keyword->asymetry_fixed);\r
262 ioption("magic-relative-limit", keyword->data_limit);\r
263 ioption("magic-relative-prio", keyword->data_prio);\r
264 loption("magic-fixed-limit", keyword->fixed_limit);\r
265 loption("magic-fixed-prio", keyword->fixed_prio);\r
266 ioption("htb-default-prio", keyword->default_prio);\r
267 ioption("htb-rate-bonus", keyword->reserve_min);\r
268 ioption("htb-ceil-malus", keyword->reserve_max);\r
269 ioption("download-aggregation", keyword->download_aggregation);\r
270 ioption("upload-aggregation", keyword->upload_aggregation);\r
271 option("leaf-discipline", keyword->leaf_discipline);\r
272 option("html-color", keyword->html_color);\r
273 ioption("allowed-avgmtu" ,keyword->allowed_avgmtu);\r
274 _=tmptr;\r
275 \r
276 if(keyword->data_limit || keyword->fixed_limit || \r
277 keyword->data_prio || keyword->fixed_prio)\r
278 {\r
279 use_credit=1; \r
280 }\r
281 }\r
282 }\r
283 }\r
284\r
285 option("tc",tc);\r
286 option("iptables",iptables);\r
287 option("iptables-save",iptablessave);\r
288 option("iptables-restore",iptablesrestore);\r
289 option("ip6tables",ip6tables);\r
290 option("ip6tables-save",ip6tablessave);\r
291 option("ip6tables-restore",ip6tablesrestore);\r
292 option("iptables-in-filename",iptablesfile);\r
293 option("ip6tables-in-filename",ip6tablesfile);\r
294 option("hosts",hosts);\r
295 option("downstream-interfaces-list-filename",downstreamfile);\r
296 option("upstream-interfaces-list-filename",upstreamfile);\r
297 option("qos-free-interfaces-list-filename",qosfreefile);\r
298 option("macros-filename",macrosfile);\r
299 option("ip6-prefix",ip6prefix);\r
300 option("medium",medium);\r
301 ioption("hall-of-fame-enable",hall_of_fame);\r
302 ioption("digital-divide-weirdness-ratio",digital_divide);\r
303 option("hall-of-fame-title",title);\r
304 option("hall-of-fame-filename",html);\r
305 option("json-filename",json_traffic);\r
306 option("hall-of-fame-preview",preview);\r
307 option("json-preview",json_preview);\r
308 option("log-filename",cmdlog);\r
309 option("credit-filename",credit);\r
310 option("classmap-filename",classmap);\r
311 ioption("credit-enable",enable_credit);\r
312 option("log-traffic-directory",log_dir);\r
313 option("log-traffic-html-directory",html_log_dir);\r
314 option("log-traffic-url-path",log_url);\r
315 option("jquery-url",jquery_url);\r
316 option("lms-url",lms_url);\r
317 ioption("use-jquery-popups",use_jquery_popups);\r
318 option("qos-free-zone",qos_free_zone);\r
319 ioption("qos-free-delay",qos_free_delay);\r
320/* ioption("qos-proxy-enable",qos_proxy); */\r
321/* option("qos-proxy-ip",proxy_ip);*/\r
322 option("htb-leaf-discipline",qos_leaf);\r
323/* ioption("qos-proxy-port",proxy_port); */\r
324 ioption("free-rate",free_min);\r
325 ioption("free-ceil",free_max);\r
326 ioption("overlimit-rate",overlimit_min);\r
327 ioption("overlimit-ceil",overlimit_max);\r
328 ioption("htb-burst",burst);\r
329 ioption("htb-burst-main",burst_main);\r
330 ioption("htb-burst-group",burst_group);\r
331 ioption("htb-nesting-limit",max_nesting);\r
332 ioption("htb-r2q",htb_r2q);\r
333 ioption("magic-include-upload",include_upload);\r
334 ioption("magic-treshold",magic_treshold); \r
335 option("filter-type", cnf); \r
336/* not yet implemented:\r
337 ioption("magic-fixed-packets",fixed_packets);\r
338 ioption("magic-relative-packets",packet_limit);\r
339*/\r
340 }\r
341 fail\r
342 { \r
343 perror(config_filename);\r
344 puts("Warning - using built-in defaults instead ...");\r
345 }\r
346 done; /* ugly macro end */\r
347 printf("\n");\r
348 \r
349 /* leaf discipline for keywords */\r
350 for_each(keyword,keywords)\r
351 {\r
352 if(!strcmpi(keyword->leaf_discipline, ""))\r
353 {\r
354 keyword->leaf_discipline = qos_leaf;\r
355 }\r
356 }\r
357\r
358 if(strcmpi(cnf, "mark"))\r
359 {\r
360 filter_type = 2;\r
361 mark = "CLASSIFY";\r
362 mark_iptables = "CLASSIFY --set-class 1:";\r
363 }\r
364 else\r
365 {\r
366 filter_type = 1;\r
367 mark = "MARK";\r
368 mark_iptables = "MARK --set-mark ";\r
369 }\r
370}\r
371\r
372 \r
373/* ========== This function executes, logs OR ALSO prints command ========== */\r
374\r
375void safe_run(char *cmd)\r
376{\r
377 if(dry_run)\r
378 {\r
379 printf("\n=>%s\n",cmd);\r
380 }\r
381 else\r
382 {\r
383 int skip_cmd = FALSE;\r
384 if(mix_new_hosts)\r
385 {\r
386 skip_cmd = FALSE;\r
387 for_each(textline, previous_commands)\r
388 {\r
389 if(eq(textline->str, cmd))\r
390 {\r
391 skip_cmd = TRUE;\r
392 break;\r
393 }\r
394 }\r
395 }\r
396 \r
397 if(!skip_cmd)\r
398 {\r
399 if(mix_new_hosts)\r
400 {\r
401 printf("Executing command: %s\n", cmd);\r
402 }\r
403 else\r
404 system(cmd);\r
405 }\r
406 }\r
407 if(log_file && !mix_new_hosts)//!!debug\r
408 {\r
409 fprintf(log_file,"%s\n",cmd);\r
410 }\r
411}\r
412\r
413void iptables_save_line(char *line, int ipv6)\r
414{\r
415 const char *cmd = NULL;\r
416 \r
417 if(ipv6)\r
418 {\r
419 if(mix_new_hosts)\r
420 for_each(textline, previous_ip6tables)\r
421 if(eq(textline->str, line))\r
422 {\r
423 printf("Match: %s\n",line);\r
424 return;\r
425 }\r
426\r
427 if(!mix_new_hosts) //!!debug\r
428 fprintf(ip6tables_file,"%s\n",line);\r
429 cmd = ip6tables;\r
430 }\r
431 else\r
432 {\r
433 if(mix_new_hosts)\r
434 for_each(textline, previous_iptables)\r
435 if(eq(textline->str, line))\r
436 {\r
437 printf("Match: %s\n",line);\r
438 return;\r
439 } \r
440\r
441 if(!mix_new_hosts) //!!debug\r
442 fprintf(iptables_file,"%s\n",line);\r
443 cmd = iptables;\r
444 }\r
445 if(mix_new_hosts)\r
446 {\r
447 \r
448 printf("Executing command: %s %s\n",cmd, line);\r
449 } \r
450}\r
451\r
452#define IPv4 FALSE\r
453#define IPv6 TRUE\r
454\r
455\r
456void run_iptables_restore(void)\r
457{\r
458 char *restor;\r
459 string(restor, STRLEN);\r
460\r
461 /*-----------------------------------------------------------------*/\r
462 printf("Running %s <%s ...\n", iptablesrestore, iptablesfile);\r
463 /*-----------------------------------------------------------------*/\r
464\r
465 iptables_save_line("COMMIT", IPv4);\r
466 fclose(iptables_file);\r
467 if(dry_run) \r
468 {\r
469 parse(iptablesfile)\r
470 {\r
471 printf("%s\n",_);\r
472 }\r
473 done; /* ugly macro end */\r
474 }\r
475\r
476 sprintf(restor,"%s <%s",iptablesrestore, iptablesfile);\r
477 safe_run(restor);\r
478\r
479 if(ip6prefix)\r
480 {\r
481 /*-----------------------------------------------------------------*/\r
482 printf("Running %s <%s ...\n", ip6tablesrestore, ip6tablesfile);\r
483 /*-----------------------------------------------------------------*/\r
484 iptables_save_line("COMMIT", IPv6);\r
485 fclose(ip6tables_file);\r
486 if(dry_run) \r
487 {\r
488 parse(ip6tablesfile)\r
489 {\r
490 printf("%s\n",_);\r
491 }\r
492 done; /* ugly macro end */\r
493 }\r
494 sprintf(restor,"%s <%s",ip6tablesrestore, ip6tablesfile);\r
495 safe_run(restor);\r
496 }\r
497 free(restor);\r
498}\r
499\r
500/**/\r
501\r
502char *parse_datafile_line(char *str);\r
503time_t get_mtime(const char *path);\r
504\r
505/*-----------------------------------------------------------------*/\r
506/* Are you looking for int main(int argc, char **argv) ? :-)) */\r
507/*-----------------------------------------------------------------*/\r
508\r
509program\r
510{\r
511 int i=0; /* just plain old Fortran style integer :-) */\r
512 FILE *f=NULL; /* everything is just stream of bytes... */\r
513 char *str, *ptr, *d; /* LET A$=B$ :-) */\r
514 char *substring, *limit_pkts;\r
515\r
516 int parent = 1;\r
517 int just_networks = FALSE; \r
518 int just_flush = FALSE; /* deactivates all previous actions */\r
519 int nodelay = FALSE;\r
520 int just_preview = FALSE; /* preview - generate just stats */\r
521 int start_shaping = FALSE; /* apply FUP - requires classmap file */\r
522 int stop_shaping = FALSE; /* lift FUP - requires classmap file */\r
523 int reduce_ceil = 0; /* allow only rate+(ceil-rate)/2, /4, etc. */\r
524 int just_logs = FALSE; /* just parse logs */\r
525 int run = FALSE;\r
526 int total = 0;\r
527 \r
528 char *althosts=NULL;\r
529 \r
530 printf("\n\\r
531Prometheus QoS - \"fair-per-IP\" Quality of Service setup utility.\n\\r
532Version %s - Copyright (C)2005-2019 Michael Polak, Arachne Labs\n\\r
533iptables-restore & burst tunning & classify modification by Ludva\n\\r
534Credit: CZFree.Net, Martin Devera, Netdave, Aquarius, Gandalf\n\n",version);\r
535\r
536 /*----- Boring... we have to check command line options first: ----*/ \r
537 arguments\r
538 {\r
539 argument("-c") { nextargument(config); }\r
540 argument("-h") { nextargument(althosts);}\r
541 argument("-d") { run = TRUE; dry_run = TRUE; }\r
542 argument("-f") { run = TRUE; just_flush = TRUE; }\r
543 argument("-9") { run = TRUE; just_flush = 9; }\r
544 argument("-p") { run = TRUE; just_preview = TRUE; }\r
545 argument("-q") { run = TRUE; just_preview = TRUE; stop_shaping = TRUE; }\r
546 argument("-2") { run = TRUE; just_preview = TRUE; reduce_ceil = 2; }\r
547 argument("-4") { run = TRUE; just_preview = TRUE; reduce_ceil = 4; }\r
548 argument("-s") { run = TRUE; just_preview = TRUE; start_shaping = TRUE; }\r
549 argument("-x") { run = TRUE; mix_new_hosts = TRUE; log_file_mode = "a"; }\r
550 argument("-r") { run = TRUE; }\r
551 argument("-n") { run = TRUE; nodelay = TRUE; }\r
552 argument("-a") { run = TRUE; just_networks = TRUE; }\r
553 argument("-l") { just_logs = TRUE; }\r
554 argument("-m") { just_logs = TRUE; }\r
555 argument("-y") { just_logs = TRUE; }\r
556 argument("-?") { help(); exit(0); }\r
557 argument("--help") { help(); exit(0); }\r
558 argument("-v") { exit(0); } \r
559 argument("--version") { exit(0); } \r
560 }\r
561 \r
562 if(dry_run)\r
563 {\r
564 puts("*** THIS IS JUST DRY RUN ! ***\n");\r
565 }\r
566\r
567 date(d); /* this is typical cll1.h macro - prints current date */\r
568\r
569 /*-----------------------------------------------------------------*/\r
570 printf("Parsing configuration file %s ...\n", config);\r
571 /*-----------------------------------------------------------------*/\r
572 get_config(config);\r
573 /*-----------------------------------------------------------------*/\r
574 printf("Parsing upstream interfaces list %s ...\n", upstreamfile);\r
575 /*-----------------------------------------------------------------*/\r
576 parse(upstreamfile)\r
577 {\r
578 ptr = parse_datafile_line(_);\r
579 if(ptr)\r
580 {\r
581 create(interface, Interface);\r
582 interface->name = _;\r
583 interface->speed = (long long)atol(ptr);\r
584 /* is supplied value meaningful ?*/\r
585 if(interface->speed <= 0)\r
586 {\r
587 printf("Illegal value of %s interface bandwidth.\n", interface->name);\r
588 reject_config_and_exit(upstreamfile);\r
589 }\r
590 interface->is_upstream = TRUE;\r
591 interface->chain = "FORWARD";\r
592 interface->idxprefix = "forw";\r
593 push(interface, interfaces);\r
594 printf("Upstream interface %s: medium %s capacity %ld kbps\n", interface->name, medium, interface->speed);\r
595 }\r
596 }\r
597 done; /* ugly macro end */\r
598\r
599 /*-----------------------------------------------------------------*/\r
600 printf("Parsing downstream interfaces list %s ...\n", downstreamfile);\r
601 /*-----------------------------------------------------------------*/\r
602 parse(downstreamfile)\r
603 {\r
604 ptr = parse_datafile_line(_);\r
605 if(ptr)\r
606 {\r
607 create(interface, Interface);\r
608 interface->name = _;\r
609 interface->speed = (long long)atol(ptr);\r
610 /* is supplied value meaningful ?*/\r
611 if(interface->speed <= 0)\r
612 {\r
613 printf("Illegal value of %s interface bandwidth.\n", interface->name);\r
614 reject_config_and_exit(downstreamfile);\r
615 }\r
616 interface->is_upstream = FALSE;\r
617 interface->chain = "POSTROUTING";\r
618 interface->idxprefix = "post";\r
619 push(interface, interfaces);\r
620 printf("Downstream interface %s: medium %s capacity %ld kbps\n", interface->name, medium, interface->speed);\r
621 }\r
622 }\r
623 done; /* ugly macro end */\r
624 \r
625 if(just_logs)\r
626 {\r
627 parse_ip_log(argc,argv);\r
628 exit(0);\r
629 }\r
630 else if(not run)\r
631 {\r
632 help();\r
633 exit(0);\r
634 }\r
635\r
636 if(althosts)\r
637 {\r
638 hosts = althosts;\r
639 }\r
640\r
641 if(!mix_new_hosts && just_flush<9)\r
642 {\r
643 /*-----------------------------------------------------------------*/\r
644 puts("Parsing iptables verbose output ...");\r
645 /*-----------------------------------------------------------------*/\r
646 get_traffic_statistics(iptables, FALSE);\r
647 if(ip6prefix)\r
648 {\r
649 /*-----------------------------------------------------------------*/\r
650 puts("Parsing ip6tables verbose output ...");\r
651 /*-----------------------------------------------------------------*/ \r
652 get_traffic_statistics(ip6tables, TRUE);\r
653 }\r
654 }\r
655\r
656 /*-----------------------------------------------------------------*/\r
657 /* cll1.h - let's allocate brand new character buffer... */\r
658 /*-----------------------------------------------------------------*/\r
659 string(str, STRLEN); \r
660 string(limit_pkts, STRLEN);\r
661\r
662 /*-----------------------------------------------------------------*/\r
663 printf("Parsing qos free interfaces file %s ...\n", qosfreefile);\r
664 /*-----------------------------------------------------------------*/\r
665 load(qosfreeinterface, qosfreeinterfaces,\r
666 qosfreefile, QosFreeInterface, name);\r
667\r
668 /*-----------------------------------------------------------------*/\r
669 printf("Parsing macro definition file %s ...\n", macrosfile);\r
670 /*-----------------------------------------------------------------*/\r
671 parse(macrosfile)\r
672 {\r
673 ptr = parse_datafile_line(_);\r
674 if(ptr)\r
675 {\r
676 create(macro, Macro);\r
677 macro->rewrite_from = _;\r
678 macro->rewrite_to = ptr;\r
679 push(macro, macros);\r
680 printf("%s -> %s\n", macro->rewrite_from, macro->rewrite_to);\r
681 }\r
682 }\r
683 done; /* ugly macro end */\r
684\r
685 /*-----------------------------------------------------------------*/\r
686 printf("Parsing class defintion file %s ...\n", hosts);\r
687 /*-----------------------------------------------------------------*/\r
688 parse_hosts(hosts);\r
689/*\r
690 //this was pretty dumb idea anyway...\r
691 if(just_networks)\r
692 {\r
693 analyse_topology("/usr/sbin/traceroute -n -m 10 -w 2 %s.%d");\r
694 exit(-1); \r
695 }\r
696*/\r
697\r
698 if(mix_new_hosts)\r
699 {\r
700 char *eoln;\r
701 /*-----------------------------------------------------------------*/\r
702 printf("Loading log of previously executed commands %s ...\n", cmdlog);\r
703 /*-----------------------------------------------------------------*/\r
704 load(textline, previous_commands, cmdlog, Textfile, str);\r
705 /*-----------------------------------------------------------------*/\r
706 printf("Loading log of iptables-restore commands %s ...\n", iptablesfile);\r
707 /*-----------------------------------------------------------------*/\r
708 load(textline, previous_iptables, iptablesfile, Textfile, str);\r
709 /*-----------------------------------------------------------------*/\r
710 printf("Loading log of ip6tables-restore commands %s ...\n", ip6tablesfile);\r
711 /*-----------------------------------------------------------------*/\r
712 load(textline, previous_ip6tables, ip6tablesfile, Textfile, str);\r
713 /*-----------------------------------------------------------------*/\r
714 printf("Loading map of IP addresses to tc classes %s ...\n", classmap);\r
715 /*-----------------------------------------------------------------*/\r
716 load(textline, previous_classmap, classmap, Textfile, str);\r
717 }\r
718\r
719 /*-----------------------------------------------------------------*/\r
720 puts("Resolving shared connections ...");\r
721 /*-----------------------------------------------------------------*/\r
722 for_each(ip, ips) if(ip->sharing)\r
723 {\r
724 for_each(sharedip, ips) if(eq(sharedip->name, ip->sharing))\r
725 {\r
726 sharedip->traffic += ip->traffic;\r
727 sharedip->traffic_down += ip->direct;\r
728 sharedip->traffic_up += ip->upload;\r
729 ip->traffic = 0;\r
730 ip->mark = sharedip->mark; \r
731 ip->lmsid = sharedip->lmsid;\r
732 ip->pps_limit = sharedip->pps_limit; /* no other way to do this */\r
733\r
734 /* Ugly hack: append IPv4 addresses of sharedip to IPv6 uplinks */\r
735 ptr = strchr(ip->addr, '+');\r
736 if(ptr && ptr-ip->addr > 1 && !sharedip->v6)\r
737 {\r
738 *(--ptr) = 0;\r
739 concatenate(ip->addr, sharedip->addr, ptr);\r
740 ip->name = ip->addr = ptr;\r
741 ptr = strchr(ip->addr, '.');\r
742 while(ptr && *ptr)\r
743 {\r
744 *ptr = ':';\r
745 ptr = strchr(ptr, '.');\r
746 }\r
747 ip->mask += 64;\r
748 }\r
749\r
750 break;\r
751 }\r
752 if(not sharedip)\r
753 {\r
754 printf("Unresolved shared connection: %s %s sharing-%s\n",\r
755 ip->addr, ip->name, ip->sharing);\r
756 }\r
757 }\r
758\r
759 if(!mix_new_hosts && enable_credit && just_flush<9)\r
760 {\r
761 /*-----------------------------------------------------------------*/\r
762 printf("Parsing credit file %s ...\n", credit);\r
763 /*-----------------------------------------------------------------*/\r
764 parse(credit)\r
765 {\r
766 ptr = parse_datafile_line(_);\r
767 if(ptr)\r
768 {\r
769 if_exists(ip,ips,eq(ip->addr,_))\r
770 {\r
771 sscanf(ptr,"%Lu",&(ip->credit));\r
772 }\r
773 }\r
774 }\r
775 done; /* ugly macro end */\r
776 }\r
777\r
778\r
779 if(!just_preview)\r
780 {\r
781 if(!mix_new_hosts)\r
782 {\r
783 /*-----------------------------------------------------------------*/\r
784 puts("Initializing iptables and tc classes ...");\r
785 /*-----------------------------------------------------------------*/\r
786 \r
787 iptables_file = fopen(iptablesfile, "w");\r
788 if(iptables_file == NULL)\r
789 {\r
790 perror(iptablesfile);\r
791 exit(-1);\r
792 }\r
793 iptables_save_line(iptablespreamble, IPv4);\r
794\r
795 if(ip6prefix)\r
796 {\r
797 ip6tables_file = fopen(ip6tablesfile, "w");\r
798 if(ip6tables_file == NULL)\r
799 {\r
800 perror(ip6tablesfile);\r
801 exit(-1);\r
802 }\r
803 iptables_save_line(iptablespreamble, IPv6);\r
804 iptables_save_line(ip6preamble, IPv6);\r
805 }\r
806\r
807 run_iptables_restore();\r
808 }\r
809 \r
810 log_file = fopen(cmdlog, log_file_mode); /* overwrite command and iptables log files, except append in -x mode */\r
811 if(log_file == NULL) \r
812 {\r
813 perror(cmdlog);\r
814 exit(-1);\r
815 } \r
816 \r
817 for_each(interface, interfaces)\r
818 {\r
819 sprintf(str,"%s qdisc del dev %s root 2>/dev/null", tc, interface->name);\r
820 safe_run(str);\r
821 }\r
822\r
823 if(!mix_new_hosts)\r
824 { \r
825 iptables_file = fopen(iptablesfile, log_file_mode); /* overwrite command and iptables log files, except append in -x mode */\r
826 iptables_save_line(iptablespreamble, IPv4);\r
827 if(ip6prefix)\r
828 {\r
829 ip6tables_file = fopen(ip6tablesfile, log_file_mode); /* overwrite command and iptables log files, except append in -x mode */\r
830 iptables_save_line(iptablespreamble, IPv6);\r
831 iptables_save_line(ip6preamble, IPv6);\r
832 }\r
833 }\r
834\r
835 if(qos_free_zone && *qos_free_zone!='0') /* this is currently supported only for IPv4 */\r
836 {\r
837 for_each(interface, interfaces)\r
838 {\r
839 sprintf(str,"-A %s -%c %s -o %s -j ACCEPT", interface->chain, (interface->is_upstream?'d':'s'), qos_free_zone, interface->name);\r
840 iptables_save_line(str, IPv4);\r
841 }\r
842 }\r
843 \r
844 for_each(qosfreeinterface, qosfreeinterfaces)\r
845 {\r
846 sprintf(str,"-A FORWARD -i %s -j ACCEPT", qosfreeinterface->name);\r
847 iptables_save_line(str, IPv4); \r
848 iptables_save_line(str, IPv6);\r
849 sprintf(str,"-A POSTROUTING -o %s -j ACCEPT", qosfreeinterface->name);\r
850 iptables_save_line(str, IPv4);\r
851 iptables_save_line(str, IPv6);\r
852 }\r
853 \r
854 if(ip_count > idxtable_treshold1 && !just_flush)\r
855 {\r
856 int idxcount=0, bitmask=32-idxtable_bitmask1;\r
857 char *subnet, *buf;\r
858 /*-----------------------------------------------------------------*/\r
859 printf("Detected %d addresses - indexing iptables rules to improve performance...\n",ip_count);\r
860 /*-----------------------------------------------------------------*/\r
861\r
862 iptables_save_line(":post_common - [0:0]", IPv4);\r
863 iptables_save_line(":forw_common - [0:0]", IPv4);\r
864 if(ip6prefix)\r
865 {\r
866 iptables_save_line(":post_common - [0:0]", IPv6);\r
867 iptables_save_line(":forw_common - [0:0]", IPv6);\r
868 }\r
869\r
870 for_each(ip,ips) if(ip->addr && *(ip->addr) && !eq(ip->addr,"0.0.0.0/0")) \r
871 {\r
872 if(ip->v6)\r
873 {\r
874 buf=index6_id(ip->addr,bitmask+32);\r
875 }\r
876 else\r
877 {\r
878 buf=index_id(ip->addr, bitmask);\r
879 }\r
880 \r
881 if_exists(idx,idxs,eq(idx->id,buf))\r
882 {\r
883 idx->children++;\r
884 }\r
885 else\r
886 {\r
887 create(idx,Index);\r
888 idx->addr = ip->addr;\r
889 idx->id = buf;\r
890 idx->bitmask = bitmask+32*ip->v6;\r
891 idx->parent = NULL;\r
892 idx->children = 0;\r
893 idx->ipv6 = ip->v6;\r
894 idxcount++;\r
895 push(idx,idxs);\r
896 }\r
897 }\r
898\r
899 /* brutal perfomance optimalization */\r
900 while(idxcount > idxtable_treshold2 && bitmask > 2*idxtable_bitmask2)\r
901 {\r
902 bitmask -= idxtable_bitmask2;\r
903 idxcount = 0;\r
904\r
905 for_each(idx,idxs) if(idx->parent == NULL)\r
906 {\r
907 if(idx->ipv6)\r
908 {\r
909 buf = index6_id(idx->addr, bitmask+32);\r
910 }\r
911 else\r
912 {\r
913 buf = index_id(idx->addr, bitmask);\r
914 }\r
915 if_exists(metaindex,idxs,eq(metaindex->id,buf))\r
916 {\r
917 metaindex->children++;\r
918 }\r
919 else\r
920 {\r
921 create(metaindex,Index);\r
922 metaindex->addr = idx->addr;\r
923 metaindex->id = buf;\r
924 metaindex->bitmask = bitmask+32*idx->ipv6;\r
925 metaindex->parent = NULL;\r
926 metaindex->children = 0;\r
927 metaindex->ipv6 = idx->ipv6;\r
928 idxcount++;\r
929 push(metaindex,idxs);\r
930 }\r
931 idx->parent=metaindex;\r
932 }\r
933 }\r
934\r
935 /* this should slightly optimize throughput ... */\r
936 sort(idx,idxs,desc_order_by,children);\r
937 sort(idx,idxs,order_by,bitmask);\r
938\r
939 i=0;\r
940 for_each(idx, idxs)\r
941 {\r
942 if(idx->ipv6)\r
943 {\r
944 subnet=subnet6_id(idx->addr, idx->bitmask);\r
945 }\r
946 else\r
947 {\r
948 subnet=subnet_id(idx->addr, idx->bitmask);\r
949 }\r
950 printf("%d: %s/%d\n", ++i, subnet, idx->bitmask);\r
951 \r
952 sprintf(str,":post_%s - [0:0]", idx->id);\r
953 iptables_save_line(str, idx->ipv6);\r
954\r
955 sprintf(str,":forw_%s - [0:0]", idx->id);\r
956 iptables_save_line(str, idx->ipv6);\r
957\r
958 for_each(interface, interfaces)\r
959 {\r
960 if(idx->parent)\r
961 {\r
962 string(buf, strlen(idx->parent->id)+6);\r
963 sprintf(buf, "%s_%s", interface->idxprefix, idx->parent->id);\r
964 }\r
965 else\r
966 {\r
967 buf = interface->chain;\r
968 }\r
969\r
970 sprintf(str, "-A %s -%c %s/%d -o %s -j %s_%s", \r
971 buf, (interface->is_upstream?'s':'d'), subnet, idx->bitmask, interface->name, interface->idxprefix, idx->id);\r
972 iptables_save_line(str, idx->ipv6);\r
973\r
974 sprintf(str, "-A %s -%c %s/%d -o %s -j %s_common",\r
975 buf, (interface->is_upstream?'s':'d'), subnet, idx->bitmask, interface->name, interface->idxprefix);\r
976 iptables_save_line(str, idx->ipv6);\r
977 }\r
978 }\r
979 printf("Total indexed iptables chains created: %d\n", i);\r
980\r
981 for_each(interface, interfaces)\r
982 {\r
983 sprintf(str,"-A %s -o %s -j %s_common", interface->chain, interface->name, interface->idxprefix);\r
984 iptables_save_line(str, IPv4);\r
985 if(ip6prefix)\r
986 {\r
987 sprintf(str,"-A %s -o %s -j %s_common", interface->chain, interface->name, interface->idxprefix);\r
988 iptables_save_line(str, IPv6);\r
989 }\r
990 }\r
991 }\r
992 }\r
993\r
994 if(just_flush)\r
995 {\r
996 fclose(iptables_file);\r
997 if(log_file)\r
998 { \r
999 fclose(log_file);\r
1000 }\r
1001 puts("Just flushed iptables and tc classes - now exiting ...");\r
1002 exit(0);\r
1003 }\r
1004\r
1005 if(!mix_new_hosts && !just_preview)\r
1006 {\r
1007 if(!dry_run && !nodelay && qos_free_delay)\r
1008 {\r
1009 printf("Flushed iptables and tc classes - now sleeping for %d seconds...\n", qos_free_delay);\r
1010 sleep(qos_free_delay);\r
1011 }\r
1012\r
1013 for_each(interface, interfaces)\r
1014 {\r
1015 sprintf(str, "%s qdisc add dev %s root handle 1: htb r2q %d default 1",\r
1016 tc, interface->name, htb_r2q);\r
1017 safe_run(str);\r
1018\r
1019 sprintf(str, "%s class add dev %s parent 1: classid 1:2 htb rate %s ceil %s burst %dk prio %d",\r
1020 tc, interface->name, medium, medium, burst_main, highest_priority);\r
1021 safe_run(str);\r
1022\r
1023 sprintf(str, "%s class add dev %s parent 1:2 classid 1:1 htb rate %Ldkbit ceil %Ldkbit burst %dk prio %d",\r
1024 tc, interface->name, interface->speed, interface->speed, burst_main, highest_priority);\r
1025 safe_run(str);\r
1026 }\r
1027 }\r
1028\r
1029 if(mix_new_hosts)\r
1030 {\r
1031 /*-----------------------------------------------------------------*/\r
1032 puts("Locating heavy downloaders and generating root classes ...");\r
1033 /*-----------------------------------------------------------------*/\r
1034 sort(ip,ips,desc_order_by,traffic);\r
1035 }\r
1036\r
1037 /*-----------------------------------------------------------------*/\r
1038 for_each(interface, interfaces)\r
1039 {\r
1040 long long int rate = interface->speed;\r
1041 long long int max = interface->speed;\r
1042 int group_count = 0;\r
1043 //obsolete: FILE *credit_file = NULL;\r
1044 \r
1045 //obsolete: if(!just_preview && !dry_run && enable_credit)\r
1046 //obsolete: {\r
1047 //obsolete: credit_file = fopen(credit,"w");\r
1048 //obsolete: }\r
1049\r
1050 for_each(group,groups)\r
1051 {\r
1052 if(!just_preview)\r
1053 {\r
1054 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", \r
1055 tc, interface->name, parent, group->id, rate, max, burst_group, highest_priority+1, group->desired);\r
1056 safe_run(str);\r
1057 }\r
1058\r
1059 if(group_count++ < max_nesting)\r
1060 {\r
1061 parent = group->id;\r
1062 }\r
1063\r
1064 rate -= digital_divide*group->min;\r
1065 if(rate < group->min)\r
1066 {\r
1067 rate = group->min;\r
1068 }\r
1069 \r
1070 /*shaping of aggresive downloaders, with credit file support */\r
1071 /* obsolete\r
1072 if(use_credit)\r
1073 {\r
1074 int group_rate = group->min, priority_sequence = lowest_priority;\r
1075 \r
1076 for_each(ip, ips) if(ip->min == group->min && ip->max > ip->min)\r
1077 {\r
1078 ip->realquota=ip->credit+(ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20));\r
1079 if( ip->keyword->data_limit \r
1080 and not ip->fixedprio \r
1081 and ip->traffic > ip->realquota )\r
1082 {\r
1083 if(group_rate < ip->max)\r
1084 {\r
1085 ip->max = group_rate;\r
1086 }\r
1087 group_rate+=magic_treshold;\r
1088 ip->prio=lowest_priority;\r
1089 if(ip->prio<highest_priority+2)\r
1090 {\r
1091 ip->prio=highest_priority+2;\r
1092 }\r
1093 }\r
1094 else\r
1095 {\r
1096 if( ip->keyword->data_prio \r
1097 && !ip->fixedprio \r
1098 && ( ip->traffic > ip->credit + (ip->min*ip->keyword->data_prio+(ip->keyword->fixed_prio<<20))) )\r
1099 {\r
1100 ip->prio=priority_sequence--;\r
1101 if(ip->prio<highest_priority+1)\r
1102 {\r
1103 ip->prio=highest_priority+1;\r
1104 }\r
1105 }\r
1106 \r
1107 if(credit_file)\r
1108 {\r
1109 unsigned long long lcredit=0;\r
1110 \r
1111 if((ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))>ip->traffic)\r
1112 {\r
1113 lcredit=(ip->min*ip->keyword->data_limit+(ip->keyword->fixed_limit<<20))-ip->traffic;\r
1114 }\r
1115 fprintf(credit_file,"%s %Lu\n",ip->addr,lcredit);\r
1116 }\r
1117 }\r
1118 } \r
1119 } obsolete */\r
1120 }\r
1121 /* obsolete \r
1122 if(credit_file)\r
1123 {\r
1124 fclose(credit_file);\r
1125 } obsolete */\r
1126 }\r
1127\r
1128 if(just_preview)\r
1129 {\r
1130 if(start_shaping || stop_shaping || reduce_ceil)\r
1131 {\r
1132 time_t how_much_seconds = time(NULL) - get_mtime(classmap); /* sice start of daily aggregation session */\r
1133 printf("Reading %s (%ld seconds old) and applying Fair Use Policy and Aggregation rules... \n", classmap, how_much_seconds);\r
1134 \r
1135 parse(classmap)\r
1136 {\r
1137 ptr=strchr(_,' ');\r
1138 if(ptr)\r
1139 {\r
1140 *ptr=0;\r
1141 ptr++;\r
1142 if_exists(ip,ips,eq(ip->addr,_))\r
1143 {\r
1144 int unshape_this_ip = 0;\r
1145 long avg_mbps_down = ip->traffic_down * 8 / how_much_seconds; \r
1146 long avg_mbps_up = ip->traffic_up * 8 / how_much_seconds;\r
1147 int agreg = 1, print_stats = 1;\r
1148 \r
1149 if(ip->keyword->download_aggregation)\r
1150 {\r
1151 int min_mbps = (ip->min/ip->keyword->download_aggregation)>>10;\r
1152 if(min_mbps < 1)\r
1153 {\r
1154 min_mbps = 1;\r
1155 }\r
1156 \r
1157 if(min_mbps <= avg_mbps_down)\r
1158 {\r
1159 unshape_this_ip = 0;\r
1160 agreg = (int)((float)(avg_mbps_down+1)/min_mbps+.5);\r
1161 ip->max /= agreg;\r
1162 ip->pps_limit /= agreg;\r
1163 printf("Download aggregation 1:%d for %s (min: %lu Mbps avg: %ld Mbps)\n", agreg, ip->name, min_mbps, avg_mbps_down);\r
1164 }\r
1165 else\r
1166 {\r
1167 unshape_this_ip = 1;\r
1168 }\r
1169 }\r
1170 else if(ip->keyword->upload_aggregation)\r
1171 {\r
1172 int min_mbps = (ip->min/ip->keyword->upload_aggregation)>>10;\r
1173 if(min_mbps < 1)\r
1174 {\r
1175 min_mbps = 1;\r
1176 }\r
1177\r
1178 if(min_mbps <= avg_mbps_up)\r
1179 {\r
1180 unshape_this_ip = 0;\r
1181 agreg = (int)((float)(avg_mbps_up+1)/min_mbps+.5);\r
1182 ip->max /= agreg;\r
1183 printf("Upload aggregation 1:%d for %s: (min: %lu Mbps avg: %ld Mbps)\n", agreg, ip->name, min_mbps, avg_mbps_up);\r
1184 }\r
1185 else\r
1186 {\r
1187 unshape_this_ip = 1;\r
1188 }\r
1189 }\r
1190 if(stop_shaping)\r
1191 {\r
1192 unshape_this_ip = 1;\r
1193 }\r
1194 ip->aggregated = agreg; \r
1195 ip->mark = atoi(ptr);\r
1196 if(ip->max < ip->desired || unshape_this_ip || reduce_ceil) /* apply or disable FUP limit immediately.... */\r
1197 {\r
1198 if(unshape_this_ip)\r
1199 {\r
1200 ip->max = ip->desired;\r
1201 if(stop_shaping) /* all limits removed, but not printed with -s (start_shaping) switch */\r
1202 {\r
1203 printf("Removing limit for %s (%s) ", ip->name, ip->addr);\r
1204 }\r
1205 else\r
1206 {\r
1207 print_stats = 0;\r
1208 }\r
1209 }\r
1210 else\r
1211 {\r
1212 printf("Updating %s (%s) ", ip->name, ip->addr);\r
1213 if(reduce_ceil)\r
1214 {\r
1215 ip->max = ip->min + (ip->desired-ip->min)/reduce_ceil;\r
1216 }\r
1217 else if(ip->max < ip->min)\r
1218 {\r
1219 ip->max = ip->min;\r
1220 } \r
1221 }\r
1222 for_each(interface, interfaces)\r
1223 {\r
1224 if(!interface->is_upstream)\r
1225 {\r
1226 if(print_stats)\r
1227 {\r
1228 printf("[down %s: %dk-%dk wants %d]", interface->name, ip->min, ip->max, ip->desired);\r
1229 }\r
1230 sprintf(str, "%s class change dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d", \r
1231 tc, interface->name, ip->group, ip->mark, ip->min, ip->max, burst, ip->prio);\r
1232 safe_run(str);\r
1233 }\r
1234 else\r
1235 {\r
1236 if(print_stats)\r
1237 {\r
1238 printf("[up %s: %dk-%dk wants %dk]", interface->name, (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed), \r
1239 (int)((ip->desired/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed),\r
1240 (int)((ip->desired/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed));\r
1241 }\r
1242 sprintf(str,"%s class change dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d",\r
1243 tc, interface->name, ip->group, ip->mark,\r
1244 (int)((ip->min/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed),\r
1245 (int)((ip->max/ip->keyword->asymetry_ratio)-ip->keyword->asymetry_fixed), burst, ip->prio);\r
1246 safe_run(str);\r
1247 }\r
1248 }\r
1249 if(print_stats)\r
1250 {\r
1251 printf("\n");\r
1252 }\r
1253 }\r
1254 }\r
1255 }\r
1256 }\r
1257 fail\r
1258 { \r
1259 perror(classmap);\r
1260 puts("Warning - classmap file not fund, just generating preview ...");\r
1261 start_shaping=FALSE;\r
1262 stop_shaping=FALSE;\r
1263 }\r
1264 done; /* ugly macro end */\r
1265 }\r
1266 html=preview;\r
1267 json_traffic=json_preview;\r
1268 }\r
1269\r
1270 if(!mix_new_hosts && !dry_run && !just_flush)\r
1271 {\r
1272 /*-----------------------------------------------------------------*/\r
1273 printf("Writing json traffic overview %s ... ", json_traffic);\r
1274 /*-----------------------------------------------------------------*/\r
1275 write_json_traffic(json_traffic);\r
1276\r
1277 /*-----------------------------------------------------------------*/\r
1278 printf("Writing statistics into HTML page %s ...\n", html);\r
1279 /*-----------------------------------------------------------------*/\r
1280 write_htmlandlogs(html, d, total, just_preview);\r
1281 }\r
1282\r
1283 if(just_preview)\r
1284 {\r
1285 char swchar='p';\r
1286 if(start_shaping)\r
1287 {\r
1288 swchar='s';\r
1289 }\r
1290 else if(reduce_ceil)\r
1291 {\r
1292 swchar='0'+reduce_ceil; /* -2, -4 */\r
1293 }\r
1294 else if(stop_shaping)\r
1295 {\r
1296 swchar='q';\r
1297 }\r
1298\r
1299 printf("Statistics preview generated (-%c switch) - now exiting ...\n", swchar);\r
1300 exit(0);\r
1301 }\r
1302 \r
1303 i=0;\r
1304#ifdef DEBUG\r
1305 printf("%-22s %-15s mark\n","name","ip");\r
1306#endif\r
1307\r
1308 printf("Writing %s", classmap); \r
1309 f = fopen(classmap, "w"); \r
1310 if(f < 0)\r
1311 {\r
1312 perror(classmap);\r
1313 }\r
1314\r
1315 /*-----------------------------------------------------------------*/\r
1316 printf(" + generating iptables and tc classes ... ");\r
1317 /*-----------------------------------------------------------------*/\r
1318\r
1319 for_each(ip, ips) if(ip->mark > 0)\r
1320 {\r
1321 for_each(interface, interfaces)\r
1322 {\r
1323 char *chain;\r
1324 if(idxs)\r
1325 {\r
1326 char *buf;\r
1327 duplicate(ip->addr,buf);\r
1328 if(ip->v6)\r
1329 {\r
1330 buf=index6_id(ip->addr,64-idxtable_bitmask1);\r
1331 }\r
1332 else\r
1333 {\r
1334 buf=index_id(ip->addr,32-idxtable_bitmask1);\r
1335 }\r
1336 \r
1337 string(chain, 6+strlen(buf));\r
1338 sprintf(chain, "%s_", interface->idxprefix);\r
1339 strcat(chain, buf);\r
1340\r
1341 free(buf);\r
1342 }\r
1343 else\r
1344 {\r
1345 chain = interface->chain;\r
1346 }\r
1347\r
1348 /* packet limits - this will be optional in future */\r
1349 if(ip->pps_limit)\r
1350 {\r
1351 sprintf(limit_pkts, "-m limit --limit %d/s --limit-burst %d ", \r
1352 ip->pps_limit, ip->pps_limit);\r
1353 }\r
1354 else\r
1355 {\r
1356 *limit_pkts = 0;\r
1357 } \r
1358\r
1359 #ifdef DEBUG\r
1360 printf("%-22s %-16s %04d %d/s\n", ip->name, ip->addr, ip->mark, ip->pps_limit); \r
1361 #endif\r
1362\r
1363 /* ------------------------------------------------ iptables classify */\r
1364 sprintf(str, "-A %s -%c %s/%d -o %s -j %s%d",\r
1365 chain, (interface->is_upstream?'s':'d'), ip->addr, ip->mask,\r
1366 interface->name, mark_iptables, ip->mark);\r
1367 iptables_save_line(str, ip->v6);\r
1368\r
1369 sprintf(str, "-A %s -%c %s/%d -o %s %s-j ACCEPT",\r
1370 chain, (interface->is_upstream?'s':'d'),ip->addr, ip->mask,\r
1371 interface->name, limit_pkts);\r
1372 iptables_save_line(str, ip->v6);\r
1373\r
1374 if(*limit_pkts) /* non-empty string?*/\r
1375 {\r
1376 /* classify overlimit packets to separate overlimit class */\r
1377 sprintf(str, "-A %s -%c %s/%d -o %s -j %s%d",\r
1378 chain, (interface->is_upstream?'s':'d'), ip->addr, ip->mask,\r
1379 interface->name, mark_iptables, OVERLIMIT_CLASS);\r
1380 iptables_save_line(str, ip->v6);\r
1381\r
1382 sprintf(str, "-A %s -%c %s/%d -o %s -j ACCEPT",\r
1383 chain, (interface->is_upstream?'s':'d'), ip->addr, ip->mask,\r
1384 interface->name);\r
1385 iptables_save_line(str, ip->v6);\r
1386 }\r
1387\r
1388 if(ip->min)\r
1389 {\r
1390 //TODO - min and max should not exceed interface->speed\r
1391 \r
1392 /* -------------------------------------------------------- tc class */\r
1393 #ifdef DEBUG\r
1394 printf("[down: %dk-%dk]", ip->min, ip->max);\r
1395 #endif\r
1396\r
1397 sprintf(str, "%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d", \r
1398 tc, interface->name, ip->group, ip->mark, ip->min, ip->max, burst, ip->prio);\r
1399 safe_run(str);\r
1400\r
1401 if(strcmpi(ip->keyword->leaf_discipline, "none"))\r
1402 {\r
1403 sprintf(str, "%s qdisc add dev %s parent 1:%d handle %d %s", \r
1404 tc, interface->name, ip->mark, ip->mark, ip->keyword->leaf_discipline); /*qos_leaf*/\r
1405 safe_run(str);\r
1406 }\r
1407\r
1408 if(filter_type == 1)\r
1409 {\r
1410 sprintf(str, "%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d",\r
1411 tc, interface->name, ip->mark, ip->mark);\r
1412 safe_run(str);\r
1413 }\r
1414 }\r
1415 else\r
1416 {\r
1417 #ifdef DEBUG\r
1418 printf("(sharing %s)\n", ip->sharing);\r
1419 #endif\r
1420 }\r
1421 i++;\r
1422 }\r
1423 /* classmap */\r
1424 if(ip->min && f > 0)\r
1425 {\r
1426 fprintf(f, "%s %d\n", ip->addr, ip->mark);\r
1427 }\r
1428 }\r
1429 if(f > 0)\r
1430 {\r
1431 puts("done.");\r
1432 fclose(f);\r
1433 }\r
1434\r
1435 for_each(interface, interfaces)\r
1436 {\r
1437 char *chain;\r
1438 if(idxs)\r
1439 {\r
1440 string(chain, STRLEN);\r
1441 sprintf(chain, "%s_common", interface->idxprefix);\r
1442 }\r
1443 else\r
1444 {\r
1445 chain = interface->chain;\r
1446 }\r
1447\r
1448 if(free_min)\r
1449 {\r
1450 final_chain = "ACCEPT";\r
1451\r
1452 sprintf(str, "-A %s -o %s -j %s%d",\r
1453 chain, interface->name, mark_iptables, FREE_CLASS);\r
1454 iptables_save_line(str, IPv4); /* only for IPv4 */\r
1455 }\r
1456\r
1457 sprintf(str,"-A %s -o %s -j %s", chain, interface->name, final_chain);\r
1458 iptables_save_line(str, IPv4);\r
1459 if(ip6prefix)\r
1460 {\r
1461 sprintf(str,"-A %s -o %s -j %s", chain, interface->name, final_chain);\r
1462 iptables_save_line(str, IPv6);\r
1463 }\r
1464\r
1465 if(free_min) /* allocate free bandwith if it is not zero... */ \r
1466 {\r
1467 /*-----------------------------------------------------------------*/\r
1468 puts("Generating free bandwith class ...");\r
1469 /*-----------------------------------------------------------------*/\r
1470 sprintf(str, "%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d",\r
1471 tc, interface->name, parent, FREE_CLASS, free_min, free_max,burst, lowest_priority);\r
1472 safe_run(str);\r
1473 /* tc SFQ */\r
1474 if(strcmpi(qos_leaf, "none"))\r
1475 {\r
1476 sprintf(str,"%s qdisc add dev %s parent 1:%d handle %d %s", tc, interface->name, FREE_CLASS, FREE_CLASS, qos_leaf);\r
1477 safe_run(str);\r
1478 } \r
1479 /* tc handle 1 fw flowid */\r
1480 sprintf(str,"%s filter add dev %s parent 1:0 protocol ip handle %d fw flowid 1:%d", tc, interface->name, FREE_CLASS, FREE_CLASS);\r
1481 safe_run(str);\r
1482 }\r
1483 /*-----------------------------------------------------------------*/\r
1484 puts("Generating bandwith class for overlimit packets...");\r
1485 /*-----------------------------------------------------------------*/\r
1486 sprintf(str, "%s class add dev %s parent 1:%d classid 1:%d htb rate %dkbit ceil %dkbit burst %dk prio %d",\r
1487 tc, interface->name, parent, OVERLIMIT_CLASS, overlimit_min, overlimit_max, burst, lowest_priority);\r
1488 safe_run(str);\r
1489 } \r
1490 printf("Total IP count: %d\n", i);\r
1491 if(!mix_new_hosts)\r
1492 {\r
1493 run_iptables_restore();\r
1494 }\r
1495 if(log_file)\r
1496 {\r
1497 fclose(log_file);\r
1498 }\r
1499 return 0;\r
1500 /* that's all folks, thank you for reading it all the way up to this point ;-) */\r
1501 /* bad luck C<<1 is not yet finished, I promise no sprintf() next time... */\r
1502}\r
This page took 0.230148 seconds and 4 git commands to generate.