1 /*
2 * ProFTPD: mod_qos -- a module for managing QoS socket options
3 *
4 * Copyright (c) 2010 Philip Prindeville
5 * Copyright (c) 2010-2014 The ProFTPD Project
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20 *
21 * As a special exemption, Philip Prindeville and other respective copyright
22 * holders give permission to link this program with OpenSSL, and distribute the
23 * resulting executable, without including the source code for OpenSSL in the
24 * source distribution.
25 *
26 * This is mod_qos, contrib software for proftpd 1.3.x and above.
27 */
28
29 #include "conf.h"
30
31 #define MOD_QOS_VERSION "mod_qos/0.1"
32
33 /* Make sure the version of proftpd is as necessary. */
34 #if PROFTPD_VERSION_NUMBER < 0x0001030401
35 # error "ProFTPD 1.3.4rc1 or later required"
36 #endif
37
38 module qos_module;
39
40 /* The level argument in a setsockopt(2) call for the TCP level is platform
41 * dependent. Linux wants SOL_IP, *BSD wants IPPROTO_IP.
42 */
43 #ifdef SOL_IP
44 static int ip_level = SOL_IP;
45 #else
46 static int ip_level = IPPROTO_IP;
47 #endif /* !SOL_IP */
48
49 /* These particular bits have yet to be widely deployed, thus the autodetection
50 * fun here.
51 *
52 * The semantics of the categories thus defined (CS0-CS7, AF11-AF43) are
53 * determined by the network configuration of each site; there are no
54 * global conventions/semantics to attach to these categories.
55 */
56
57 #ifdef IPTOS_CLASS_CS0
58 # define QOS_CLASS_CS0 IPTOS_CLASS_CS0
59 #else
60 # define QOS_CLASS_CS0 0x00
61 #endif
62
63 #ifdef IPTOS_CLASS_CS1
64 # define QOS_CLASS_CS1 IPTOS_CLASS_CS1
65 #else
66 # define QOS_CLASS_CS1 0x20
67 #endif
68
69 #ifdef IPTOS_CLASS_CS2
70 # define QOS_CLASS_CS2 IPTOS_CLASS_CS2
71 #else
72 # define QOS_CLASS_CS2 0x40
73 #endif
74
75 #ifdef IPTOS_CLASS_CS3
76 # define QOS_CLASS_CS3 IPTOS_CLASS_CS3
77 #else
78 # define QOS_CLASS_CS3 0x60
79 #endif
80
81 #ifdef IPTOS_CLASS_CS4
82 # define QOS_CLASS_CS4 IPTOS_CLASS_CS4
83 #else
84 # define QOS_CLASS_CS4 0x80
85 #endif
86
87 #ifdef IPTOS_CLASS_CS5
88 # define QOS_CLASS_CS5 IPTOS_CLASS_CS5
89 #else
90 # define QOS_CLASS_CS5 0xa0
91 #endif
92
93 #ifdef IPTOS_CLASS_CS6
94 # define QOS_CLASS_CS6 IPTOS_CLASS_CS6
95 #else
96 # define QOS_CLASS_CS6 0xc0
97 #endif
98
99 #ifdef IPTOS_CLASS_CS7
100 # define QOS_CLASS_CS7 IPTOS_CLASS_CS7
101 #else
102 # define QOS_CLASS_CS7 0xe0
103 #endif
104
105 /* See RFC2474 for a discussion of Differentiated Services field */
106
107 #ifdef IPTOS_DSCP_AF11
108 # define QOS_DSCP_AF11 IPTOS_DSCP_AF11
109 #else
110 # define QOS_DSCP_AF11 0x28
111 #endif
112
113 #ifdef IPTOS_DSCP_AF12
114 # define QOS_DSCP_AF12 IPTOS_DSCP_AF12
115 #else
116 # define QOS_DSCP_AF12 0x30
117 #endif
118
119 #ifdef IPTOS_DSCP_AF13
120 # define QOS_DSCP_AF13 IPTOS_DSCP_AF13
121 #else
122 # define QOS_DSCP_AF13 0x38
123 #endif
124
125 #ifdef IPTOS_DSCP_AF21
126 # define QOS_DSCP_AF21 IPTOS_DSCP_AF21
127 #else
128 # define QOS_DSCP_AF21 0x48
129 #endif
130
131 #ifdef IPTOS_DSCP_AF22
132 # define QOS_DSCP_AF22 IPTOS_DSCP_AF22
133 #else
134 # define QOS_DSCP_AF22 0x50
135 #endif
136
137 #ifdef IPTOS_DSCP_AF23
138 # define QOS_DSCP_AF23 IPTOS_DSCP_AF23
139 #else
140 # define QOS_DSCP_AF23 0x58
141 #endif
142
143 #ifdef IPTOS_DSCP_AF31
144 # define QOS_DSCP_AF31 IPTOS_DSCP_AF31
145 #else
146 # define QOS_DSCP_AF31 0x68
147 #endif
148
149 #ifdef IPTOS_DSCP_AF32
150 # define QOS_DSCP_AF32 IPTOS_DSCP_AF32
151 #else
152 # define QOS_DSCP_AF32 0x70
153 #endif
154
155 #ifdef IPTOS_DSCP_AF33
156 # define QOS_DSCP_AF33 IPTOS_DSCP_AF33
157 #else
158 # define QOS_DSCP_AF33 0x78
159 #endif
160
161 #ifdef IPTOS_DSCP_AF41
162 # define QOS_DSCP_AF41 IPTOS_DSCP_AF41
163 #else
164 # define QOS_DSCP_AF41 0x88
165 #endif
166
167 #ifdef IPTOS_DSCP_AF42
168 # define QOS_DSCP_AF42 IPTOS_DSCP_AF42
169 #else
170 # define QOS_DSCP_AF42 0x90
171 #endif
172
173 #ifdef IPTOS_DSCP_AF43
174 # define QOS_DSCP_AF43 IPTOS_DSCP_AF43
175 #else
176 # define QOS_DSCP_AF43 0x98
177 #endif
178
179 #ifdef IPTOS_DSCP_EF
180 # define QOS_DSCP_EF IPTOS_DSCP_EF
181 #else
182 # define QOS_DSCP_EF 0xb8
183 #endif
184
185 struct qos_rec {
186 const char *name;
187 int value;
188 };
189
190 static struct qos_rec qos_vals[] = {
191 { "cs0", QOS_CLASS_CS0 },
192 { "cs1", QOS_CLASS_CS1 },
193 { "cs2", QOS_CLASS_CS2 },
194 { "cs3", QOS_CLASS_CS3 },
195 { "cs4", QOS_CLASS_CS4 },
196 { "cs5", QOS_CLASS_CS5 },
197 { "cs6", QOS_CLASS_CS6 },
198 { "cs7", QOS_CLASS_CS7 },
199
200 { "af11", QOS_DSCP_AF11 },
201 { "af12", QOS_DSCP_AF12 },
202 { "af13", QOS_DSCP_AF13 },
203 { "af21", QOS_DSCP_AF21 },
204 { "af22", QOS_DSCP_AF22 },
205 { "af23", QOS_DSCP_AF23 },
206 { "af31", QOS_DSCP_AF31 },
207 { "af32", QOS_DSCP_AF32 },
208 { "af33", QOS_DSCP_AF33 },
209 { "af41", QOS_DSCP_AF41 },
210 { "af42", QOS_DSCP_AF42 },
211 { "af43", QOS_DSCP_AF43 },
212
213 { "ef", QOS_DSCP_EF },
214
215 /* Some more human-readable strings */
216 #ifdef IPTOS_LOWDELAY
217 { "lowdelay", IPTOS_LOWDELAY },
218 #endif
219
220 #ifdef IPTOS_THROUGHPUT
221 { "throughput",IPTOS_THROUGHPUT },
222 #endif
223
224 #ifdef IPTOS_RELIABILITY
225 { "reliability",IPTOS_RELIABILITY },
226 #endif
227
228 #ifdef IPTOS_LOWCOST
229 { "lowcost", IPTOS_LOWCOST },
230 #endif
231
232 #ifdef IPTOS_MINCOST
233 { "mincost", IPTOS_MINCOST },
234 #endif
235
236 { NULL, -1 }
237 };
238
239 /* Prototypes. */
240 static int qos_sess_init(void);
241
qos_get_int(const char * str)242 static int qos_get_int(const char *str) {
243 register unsigned int i;
244
245 for (i = 0; qos_vals[i].name; i++) {
246 if (strcasecmp(qos_vals[i].name, str) == 0) {
247 return qos_vals[i].value;
248 }
249 }
250
251 return -1;
252 }
253
254 /* Configuration handlers
255 */
256
257 /* usage: QoSOptions */
set_qosoptions(cmd_rec * cmd)258 MODRET set_qosoptions(cmd_rec *cmd) {
259 register unsigned int i;
260 config_rec *c;
261 int ctrlqos = 0, dataqos = 0;
262
263 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL);
264
265 /* Make sure we have the right number of parameters. */
266 if ((cmd->argc-1) % 2 != 0) {
267 CONF_ERROR(cmd, "bad number of parameters");
268 }
269
270 for (i = 1; i < cmd->argc; i++) {
271 if (strcasecmp(cmd->argv[i], "dataqos") == 0) {
272 dataqos = qos_get_int(cmd->argv[++i]);
273 if (dataqos == -1) {
274 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown dataqos parameter '",
275 cmd->argv[i-1], "'", NULL));
276 }
277
278 } else if (strcasecmp(cmd->argv[i], "ctrlqos") == 0) {
279 ctrlqos = qos_get_int(cmd->argv[++i]);
280 if (ctrlqos == -1) {
281 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown ctrlqos parameter '",
282 cmd->argv[i-1], "'", NULL));
283 }
284
285 } else {
286 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown QoS option: '",
287 cmd->argv[i], "'", NULL));
288 }
289 }
290
291 c = add_config_param(cmd->argv[0], 2, NULL, NULL);
292 c->argv[0] = pcalloc(c->pool, sizeof(int));
293 *((int *) c->argv[0]) = ctrlqos;
294 c->argv[1] = pcalloc(c->pool, sizeof(int));
295 *((int *) c->argv[1]) = dataqos;
296
297 return PR_HANDLED(cmd);
298 }
299
300 /* Event handlers
301 */
302
303 #ifdef IP_TOS
qos_ctrl_listen_ev(const void * event_data,void * user_data)304 static void qos_ctrl_listen_ev(const void *event_data, void *user_data) {
305 const struct socket_ctx *sc;
306
307 sc = event_data;
308
309 /* Only set TOS flags on IPv4 sockets; IPv6 sockets don't seem to support
310 * them.
311 */
312
313 if (pr_netaddr_get_family(sc->addr) == AF_INET) {
314 config_rec *c;
315 c = find_config(sc->server->conf, CONF_PARAM, "QoSOptions", FALSE);
316
317 if (c) {
318 int ctrlqos;
319
320 ctrlqos = *((int *) c->argv[0]);
321 if (ctrlqos != 0) {
322 int res;
323
324 res = setsockopt(sc->sockfd, ip_level, IP_TOS, (void *) &ctrlqos,
325 sizeof(ctrlqos));
326 if (res < 0) {
327 pr_log_pri(PR_LOG_NOTICE, MOD_QOS_VERSION
328 ": error setting control socket IP_TOS: %s", strerror(errno));
329 }
330 }
331 }
332 }
333 }
334
qos_data_listen_ev(const void * event_data,void * user_data)335 static void qos_data_listen_ev(const void *event_data, void *user_data) {
336 const struct socket_ctx *sc;
337
338 sc = event_data;
339
340 /* Only set TOS flags on IPv4 sockets; IPv6 sockets don't seem to support
341 * them.
342 */
343 if (pr_netaddr_get_family(sc->addr) == AF_INET) {
344 config_rec *c;
345
346 c = find_config(sc->server->conf, CONF_PARAM, "QoSOptions", FALSE);
347 if (c) {
348 int dataqos, res;
349
350 dataqos = *((int *) c->argv[1]);
351
352 res = setsockopt(sc->sockfd, ip_level, IP_TOS, (void *) &dataqos,
353 sizeof(dataqos));
354 if (res < 0) {
355 pr_log_pri(PR_LOG_NOTICE, MOD_QOS_VERSION
356 ": error setting data socket IP_TOS: %s", strerror(errno));
357 }
358 }
359 }
360 }
361
qos_data_connect_ev(const void * event_data,void * user_data)362 static void qos_data_connect_ev(const void *event_data, void *user_data) {
363 const struct socket_ctx *sc;
364
365 sc = event_data;
366
367 /* Only set TOS flags on IPv4 sockets; IPv6 sockets don't seem to support
368 * them.
369 */
370 if (pr_netaddr_get_family(sc->addr) == AF_INET) {
371 config_rec *c;
372 c = find_config(sc->server->conf, CONF_PARAM, "QoSOptions", FALSE);
373 if (c) {
374 int dataqos, res;
375
376 dataqos = *((int *) c->argv[1]);
377
378 res = setsockopt(sc->sockfd, ip_level, IP_TOS, (void *) &dataqos,
379 sizeof(dataqos));
380 if (res < 0) {
381 pr_log_pri(PR_LOG_NOTICE, MOD_QOS_VERSION
382 ": error setting data socket IP_TOS: %s", strerror(errno));
383 }
384 }
385 }
386 }
387 #endif /* IP_TOS */
388
389 #ifdef PR_SHARED_MODULE
qos_mod_unload_ev(const void * event_data,void * user_data)390 static void qos_mod_unload_ev(const void *event_data, void *user_data) {
391 if (strcmp("mod_qos.c", (const char *) event_data) == 0) {
392 pr_event_unregister(&qos_module, NULL, NULL);
393 }
394 }
395 #endif /* PR_SHARED_MODULE */
396
qos_sess_reinit_ev(const void * event_data,void * user_data)397 static void qos_sess_reinit_ev(const void *event_data, void *user_data) {
398 int res;
399
400 /* A HOST command changed the main_server pointer, reinitialize ourselves. */
401
402 pr_event_unregister(&qos_module, "core.data-connect", qos_data_connect_ev);
403 pr_event_unregister(&qos_module, "core.data-listen", qos_data_listen_ev);
404 pr_event_unregister(&qos_module, "core.session-reinit", qos_sess_reinit_ev);
405
406 res = qos_sess_init();
407 if (res < 0) {
408 pr_session_disconnect(&qos_module,
409 PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
410 }
411 }
412
413 /* Initialization routines
414 */
415
qos_init(void)416 static int qos_init(void) {
417 #ifdef IP_TOS
418 pr_event_register(&qos_module, "core.ctrl-listen", qos_ctrl_listen_ev, NULL);
419 #endif
420
421 #ifdef PR_SHARED_MODULE
422 pr_event_register(&qos_module, "core.module-unload", qos_mod_unload_ev, NULL);
423 #endif
424 return 0;
425 }
426
qos_sess_init(void)427 static int qos_sess_init(void) {
428 #ifdef IP_TOS
429 config_rec *c;
430
431 c = find_config(main_server->conf, CONF_PARAM, "QoSOptions", FALSE);
432 if (c) {
433 int dataqos;
434
435 dataqos = *((int *) c->argv[1]);
436 if (dataqos != 0) {
437 pr_event_register(&qos_module, "core.data-connect", qos_data_connect_ev,
438 NULL);
439 pr_event_register(&qos_module, "core.data-listen", qos_data_listen_ev,
440 NULL);
441 }
442 }
443 #endif
444
445 pr_event_register(&qos_module, "core.session-reinit", qos_sess_reinit_ev,
446 NULL);
447
448 return 0;
449 }
450
451 /* Module API tables
452 */
453
454 static conftable qos_conftab[] = {
455 { "QoSOptions", set_qosoptions, NULL },
456 { NULL }
457 };
458
459 module qos_module = {
460 NULL, NULL,
461
462 /* Module API version 2.0 */
463 0x20,
464
465 /* Module name */
466 "qos",
467
468 /* Module configuration handler table */
469 qos_conftab,
470
471 /* Module command handler table */
472 NULL,
473
474 /* Module authentication handler table */
475 NULL,
476
477 /* Module initialization function */
478 qos_init,
479
480 /* Session initialization function */
481 qos_sess_init,
482
483 /* Module version */
484 MOD_QOS_VERSION
485 };
486