List of commits:
Subject Hash Author Date (UTC)
Added support for separate FORCE_ADDR for IPv4 and IPv6, MSS, TTL etc. e32e97cafe360f192283168d76c327559282e24d Catalin(ux) M. BOIE 2011-06-21 16:55:12
Added TODO to %doc rpm section. 086dbb2e078955dddf83cbfd65998334d6db81e4 Catalin(ux) M. BOIE 2010-12-19 19:46:20
Added MSS. 9a5c3676567cb92e6c0b4106c2b97cd2da2273a5 Catalin(ux) M. BOIE 2010-12-14 17:00:37
Added TOS test script. a4b1b05b27b3eaa06a4a71f656e1b80731d1b8ff Catalin(ux) M. BOIE 2010-12-14 16:55:03
Ignore Changelog-last file. 1bfa5decd11f4fd95e4b002d88e2a2e04c747855 Catalin(ux) M. BOIE 2010-12-14 16:54:36
Added KA. ec9b4556e51ca9a807400910f8ca6d698f9ad670 Catalin(ux) M. BOIE 2010-12-14 16:53:58
Bump up the version to 0.5. f7ba7f0feb49ac4942d85903d31e085f1ffa6149 Catalin(ux) M. BOIE 2010-11-07 23:16:25
Duilder updates. 4d3691f340591f68f9b0ae704da24b4352957f05 Catalin(ux) M. BOIE 2010-11-07 23:15:29
Added support to force TOS by using env var FORCE_NET_TOS. 319cfbee315a0c6889791b122fdb8069aa2ffdf1 Catalin(ux) M. BOIE 2010-11-07 23:14:23
No need for duilder_release. I will use a global one. 15a59294aa3922e58bff8aaa9de7cc06dc44fe1e Catalin(ux) M. BOIE 2010-10-27 20:43:15
TODO in. 9ea8407b8a2767debcf7597bbfc1bddfe44c78eb Catalin(ux) M. BOIE 2010-10-27 20:21:13
Typo. 2282d895e91cf2b02a7b67d933af2c68db5798b6 Catalin(ux) M. BOIE 2010-10-27 19:54:40
Aded license information to README file. fe8071fd2d9f912136fb7fa22f289ddc63cf0f14 Catalin(ux) M. BOIE 2010-10-27 19:46:48
Improved description. 1f17958175bf3f2a656a365fdafca21118737cf1 Catalin(ux) M. BOIE 2010-10-27 19:43:33
Silence compiler useless warning. 117e169b70197ac7e62907f855eb475a0a43fe07 Catalin(ux) M. BOIE 2010-10-27 19:24:45
Bump up the version to 0.4. 59371ee292d38d489719f105a9ff16c923be09d0 Catalin(ux) M. BOIE 2010-10-27 19:16:53
Corrected documentation. cf9d535fcb9d433b8a66c3c5283d9d9d64ae7240 Catalin(ux) M. BOIE 2010-10-27 19:16:29
Nothing to install in /usr/bin! d6eecf1bb4cfcf3d4bc08d8e87cac05f567fd51c Catalin(ux) M. BOIE 2010-10-27 19:13:54
Bump version to 0.3. 3531182e79370861cf73083948494eb4a8ced776 Catalin(ux) M. BOIE 2010-10-27 19:13:01
Store latest version/revision into LATEST_VER/REV for easy web update. 2d3036b2a52f2825cb7be39dc3039c0c6637b4f0 Catalin(ux) M. BOIE 2010-10-27 13:03:22
Commit e32e97cafe360f192283168d76c327559282e24d - Added support for separate FORCE_ADDR for IPv4 and IPv6, MSS, TTL etc.
Author: Catalin(ux) M. BOIE
Author date (UTC): 2011-06-21 16:55
Committer name: Catalin(ux) M. BOIE
Committer date (UTC): 2011-06-21 16:55
Parent(s): 086dbb2e078955dddf83cbfd65998334d6db81e4
Signing key:
Tree: f2160fdd3b999e67c49de78676fd41518cda4b88
File Lines added Lines deleted
.gitignore 2 0
Makefile.in 5 2
README 51 2
TODO 8 1
force_bind.c 623 87
force_bind.spec.in 2 2
send_udp.c 55 0
test1.sh 3 3
test2.sh 10 0
test_all.sh 15 0
test_bw1.sh 11 0
test_bw2.sh 11 0
test_bw3.sh 12 0
test_ka1.sh 2 2
test_tos1.sh 2 2
File .gitignore changed (mode: 100644) (index 2bd0319..0cd2559)
... ... Changelog*
3 3 *.so* *.so*
4 4 *.spec *.spec
5 5 test_bind test_bind
6 *.strace
7 send_udp
File Makefile.in changed (mode: 100644) (index b035b10..ce93d8a)
... ... export CFLAGS += -ggdb3 -Wall -Wextra -Wno-long-long -pipe
5 5 export CFLAGSSO = $(CFLAGS) -ldl -lc -shared -rdynamic -fpic export CFLAGSSO = $(CFLAGS) -ldl -lc -shared -rdynamic -fpic
6 6
7 7 .PHONY: all .PHONY: all
8 all: force_bind.so test_bind
8 all: force_bind.so
9 9
10 10 force_bind.so: force_bind.c force_bind.so: force_bind.c
11 11 $(CC) $(CFLAGSSO) -Wl,-soname,force_bind.so -o $@ force_bind.c $(CC) $(CFLAGSSO) -Wl,-soname,force_bind.so -o $@ force_bind.c
 
... ... force_bind.so: force_bind.c
13 13 test_bind: test_bind.c test_bind: test_bind.c
14 14 $(CC) $(CFLAGS) $< -o $@ $(CC) $(CFLAGS) $< -o $@
15 15
16 send_udp: send_udp.c
17 $(CC) $(CFLAGS) $< -o $@
18
16 19 .PHONY: clean .PHONY: clean
17 20 clean: clean:
18 @rm -f force_bind.so.* test_bind *.a *.o *.so* $(PRJ)-*.rpm $(PRJ)-*-*-*.tgz $(PRJ)-*.tar.gz
21 @rm -f force_bind.so.* test_bind *.a *.o *.so* $(PRJ)-*.rpm $(PRJ)-*-*-*.tgz $(PRJ)-*.tar.gz *.strace
19 22
20 23 install: all install: all
21 24 @mkdir -p $(I_USR_LIB) @mkdir -p $(I_USR_LIB)
File README changed (mode: 100644) (index 70d1482..08bf90a)
... ... How it works: force_bind is a shared object that is loaded with LD_PRELOAD and h
16 16 Forcing an IP/port to bind to is done with environments variables. Forcing an IP/port to bind to is done with environments variables.
17 17
18 18 Examples: Examples:
19 1. FORCE_BIND_ADDRESS=127.0.0.1 FORCE_BIND_PORT=33 LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so your_program # force binding to 127.0.0.1/33.
20 2. FORCE_BIND_ADDRESS=127.0.0.2 LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so your_program # force binding to 127.0.0.2
19 1. Force bind to 127.0.0.1, port 33, verbose operations:
20 export FORCE_NET_VERBOSE=1
21 export FORCE_BIND_ADDRESS_V4=127.0.0.1
22 export FORCE_BIND_PORT_V4=33
23 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
24 your_program_here
25
26 2. Force binding to 127.0.0.2, port unchanged
27 export FORCE_BIND_ADDRESS_V4=127.0.0.2
28 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
29 your_program_here
30
31 3. Force binding to ::1 (IPv6), port unchanged
32 export FORCE_BIND_ADDRESS_V6=::1
33 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
34 your_program_here
35
36 4. Changing TOS on all sockets to 30
37 export FORCE_NET_TOS=30
38 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
39 your_program_here
40
41 5. Force Keep alive to 60 seconds:
42 export FORCE_NET_KA=60
43 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
44 your_program_here
45
46 6. Force MSS to 1400
47 export FORCE_NET_MSS=1400
48 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
49 your_program_here
50
51 7. Force bandwidth to 1000 bytes/s for _all_ connections, cumulated
52 export FORCE_NET_BW=1000
53 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
54 your_program_here
55
56 8. Force bandwidth to 20000 bytes/s per socket
57 export FORCE_NET_BW_PER_SOCKET=20000
58 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
59 your_program_here
60
61 9. Force REUSEADDR
62 export FORCE_NET_REUSEADDR=1
63 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
64 your_program_here
65
66 10. Force NODELAY
67 export FORCE_NET_NODELAY=1
68 export LD_PRELOAD=${LD_PRELOAD}:/usr/lib/force_bind.so
69 your_program_here
21 70
22 71 Installation: Installation:
23 72 - ./configure - ./configure
File TODO changed (mode: 100644) (index a0af61e..644a84a)
1 [ ] We we have more bind calls, we have a problem. Probably we have to use a table like this:
1 [ ] If we have more bind calls, we have a problem. Probably we have to use a table like this:
2 2 ipv4 0.0.0.0/80 127.0.0.2/8080 ipv4 0.0.0.0/80 127.0.0.2/8080
3 3 ipv6 ::/80 ::1/8080 ipv6 ::/80 ::1/8080
4 4 unix /tmp/sock /tmp/sock1 unix /tmp/sock /tmp/sock1
5 [ ] Test UDP sendto and sendmsg!
6 [ ] Overwrite destination
7 [ ] Probably we have to hijack also dup/dup2/etc.
8 [ ] Bandwidth per total or per socket. Better, per ip/protocol
9 [ ] IPv6 flowlabel.
10 [ ] Corrupt data
11 [ ]
File force_bind.c changed (mode: 100644) (index 7933eb5..8967a67)
22 22 #include <sys/stat.h> #include <sys/stat.h>
23 23 #include <sys/types.h> #include <sys/types.h>
24 24 #include <sys/stat.h> #include <sys/stat.h>
25 #include <sys/time.h>
26 #include <time.h>
25 27 #include <errno.h> #include <errno.h>
26 28 #include <dirent.h> #include <dirent.h>
27 29 #include <asm/unistd.h> #include <asm/unistd.h>
 
32 34 #include <netinet/tcp.h> #include <netinet/tcp.h>
33 35
34 36
35 static int (*old_bind)(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = NULL;
36 static int (*old_setsockopt)(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
37 static int (*old_socket)(int domain, int type, int protocol);
38 static char *force_address = NULL;
39 static int force_port = -1;
40 static unsigned int set_tos = 0, tos;
41 static unsigned int set_keepalive = 0, keepalive;
42 static unsigned int force_mss = 0, mss;
37 #define FB_FLAGS_NETSOCK 1
43 38
39 struct private
40 {
41 int domain;
42 int type;
43 unsigned int flags;
44 44
45 /* Functions */
45 /* bandwidth */
46 unsigned long long limit;
47 unsigned long long rest;
48 struct timeval last;
49 };
46 50
47 static void set_ka(int sock)
51 struct node
48 52 { {
49 int flag, ret;
53 int fd;
54 struct private priv;
55 struct node *next;
56 };
50 57
51 flag = (keepalive > 0) ? 1 : 0;
52 ret = old_setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag));
53 syslog(LOG_INFO, "force_bind: changing SO_KEEPALIVE to 1 (ret=%d).\n", ret);
58 struct info
59 {
60 struct node *head, *tail;
61 };
62
63
64 static int (*old_bind)(int sockfd, const struct sockaddr *addr, socklen_t addrlen) = NULL;
65 static int (*old_setsockopt)(int sockfd, int level, int optname, const void *optval, socklen_t optlen);
66 static int (*old_socket)(int domain, int type, int protocol);
67 static int (*old_close)(int fd);
68 static ssize_t (*old_write)(int fd, const void *buf, size_t len);
69 static ssize_t (*old_send)(int sockfd, const void *buf, size_t len, int flags);
70 static ssize_t (*old_sendto)(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);
71 static ssize_t (*old_sendmsg)(int sockfd, const struct msghdr *msg, int flags);
72 static int (*old_accept)(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
73
74 static char *force_address_v4 = NULL;
75 static char *force_address_v6 = NULL;
76 static int force_port_v4 = -1;
77 static int force_port_v6 = -1;
78 static unsigned int force_tos = 0, tos;
79 static unsigned int force_ttl = 0, ttl;
80 static unsigned int force_keepalive = 0, keepalive;
81 static unsigned int force_mss = 0, mss;
82 static unsigned int force_reuseaddr = 0, reuseaddr;
83 static unsigned int force_nodelay = 0, nodelay;
84 static unsigned long long bw_limit_per_socket = 0;
85 static struct private bw_global;
86 static struct info fdinfo;
87 static unsigned int verbose = 0;
88
89
90 /* Helper functions */
91 static int my_syslog(int priority, const char *format, ...)
92 {
93 va_list ap;
94
95 if (verbose == 0)
96 return 0;
97
98 va_start(ap, format);
99 vsyslog(priority, format, ap);
100 va_end(ap);
101
102 return 0;
54 103 } }
55 104
56 static void set_ka_idle(int sock)
105 static struct node *get(const int fd)
57 106 { {
58 int ret;
107 struct node *p;
108
109 p = fdinfo.head;
110 while (p != NULL) {
111 if (p->fd == fd)
112 return p;
59 113
60 ret = old_setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepalive, sizeof(keepalive));
61 syslog(LOG_INFO, "force_bind: changing TCP_KEEPIDLE to %us (ret=%d).\n",
62 keepalive, ret);
114 p = p->next;
115 }
116
117 return NULL;
63 118 } }
64 119
65 static void set_mss(int sock)
120 static void add(const int fd, const struct private *p)
66 121 { {
67 int ret;
122 struct node *q;
123
124 /* do we have a copy? */
125 q = get(fd);
126 if (q == NULL) {
127 /* Try to find a free location */
128 q = fdinfo.head;
129 while (q != NULL) {
130 if (q->fd == -1) {
131 q->fd = fd;
132 break;
133 }
134
135 q = q->next;
136 }
137
138 if (q == NULL) {
139 q = (struct node *) malloc(sizeof(struct node));
140 if (q == NULL) {
141 my_syslog(LOG_INFO, "force_bind: Cannot alloc memory; ignore fd!\n");
142 return;
143 }
144
145 q->next = NULL;
146 q->fd = fd;
147 }
148 }
149 memcpy(&q->priv, p, sizeof(struct private));
150
151 /* Set bandwidth requirements */
152 q->priv.limit = bw_limit_per_socket;
153 if (bw_limit_per_socket > 0) {
154 q->priv.rest = 0;
155 gettimeofday(&q->priv.last, NULL);
156 }
68 157
69 ret = old_setsockopt(sock, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss));
70 syslog(LOG_INFO, "force_bind: changing TCP_MAXSEG to %u (ret=%d).\n",
71 mss, ret);
158 if (fdinfo.tail == NULL) {
159 fdinfo.head = q;
160 } else {
161 fdinfo.tail->next = q;
162 }
163 fdinfo.tail = q;
72 164 } }
73 165
74 void init(void)
166 static void del(const int fd)
167 {
168 struct node *p;
169
170 p = fdinfo.head;
171 while (p != NULL) {
172 if (p->fd == fd) {
173 p->fd = -1;
174 return;
175 }
176
177 p = p->next;
178 }
179 }
180
181
182 /* Functions */
183
184 static void init(void)
75 185 { {
76 186 static unsigned char inited = 0; static unsigned char inited = 0;
77 187 char *x; char *x;
 
... ... void init(void)
81 191
82 192 inited = 1; inited = 1;
83 193
194 fdinfo.head = NULL;
195 fdinfo.tail = NULL;
196
197 x = getenv("FORCE_NET_VERBOSE");
198 if (x != NULL)
199 verbose = strtol(x, NULL, 10);
200
201 x = getenv("FORCE_BIND_ADDRESS_V4");
202 if (x != NULL) {
203 force_address_v4 = x;
204 my_syslog(LOG_INFO, "force_bind: Force bind to address %s.\n",
205 force_address_v4);
206 }
207
208 x = getenv("FORCE_BIND_ADDRESS_V6");
209 if (x != NULL) {
210 force_address_v6 = x;
211 my_syslog(LOG_INFO, "force_bind: Force bind to address %s.\n",
212 force_address_v6);
213 }
214
215 /* obsolete mode */
84 216 x = getenv("FORCE_BIND_ADDRESS"); x = getenv("FORCE_BIND_ADDRESS");
85 217 if (x != NULL) { if (x != NULL) {
86 force_address = x;
87 syslog(LOG_INFO, "force_bind: Force bind to address %s.\n",
88 force_address);
218 force_address_v4 = x;
219 force_address_v6 = x;
220 my_syslog(LOG_INFO, "force_bind: Force bind to address %s."
221 " Obsolete, use FORCE_BIND_ADDRESS_V4/6.\n",
222 force_address_v4);
89 223 } }
90 224
225 x = getenv("FORCE_BIND_PORT_V4");
226 if (x != NULL) {
227 force_port_v4 = strtol(x, NULL, 10);
228 my_syslog(LOG_INFO, "force_bind: Force bind to port %d.\n",
229 force_port_v4);
230 }
231
232 x = getenv("FORCE_BIND_PORT_V6");
233 if (x != NULL) {
234 force_port_v6 = strtol(x, NULL, 10);
235 my_syslog(LOG_INFO, "force_bind: Force bind to port %d.\n",
236 force_port_v6);
237 }
238
239 /* obsolete mode */
91 240 x = getenv("FORCE_BIND_PORT"); x = getenv("FORCE_BIND_PORT");
92 241 if (x != NULL) { if (x != NULL) {
93 force_port = strtol(x, NULL, 10);
94 syslog(LOG_INFO, "force_bind: Force bind to port %d.\n",
95 force_port);
242 force_port_v4 = strtol(x, NULL, 10);
243 force_port_v6 = strtol(x, NULL, 10);
244 my_syslog(LOG_INFO, "force_bind: Force bind to port %d."
245 " Obsolete, use FORCE_BIND_PORT_V4/6.\n",
246 force_port_v4);
96 247 } }
97 248
98 249 /* tos */ /* tos */
99 250 x = getenv("FORCE_NET_TOS"); x = getenv("FORCE_NET_TOS");
100 251 if (x != NULL) { if (x != NULL) {
101 set_tos = 1;
252 force_tos = 1;
102 253 tos = strtoul(x, NULL, 0); tos = strtoul(x, NULL, 0);
103 syslog(LOG_INFO, "force_bind: Force TOS to %hhu.\n",
254 my_syslog(LOG_INFO, "force_bind: Force TOS to %hhu.\n",
104 255 tos); tos);
105 256 } }
106 257
258 /* ttl */
259 x = getenv("FORCE_NET_TTL");
260 if (x != NULL) {
261 force_ttl = 1;
262 ttl = strtoul(x, NULL, 0);
263 my_syslog(LOG_INFO, "force_bind: Force TTL to %hhu.\n",
264 ttl);
265 }
266
107 267 /* keep alive */ /* keep alive */
108 268 x = getenv("FORCE_NET_KA"); x = getenv("FORCE_NET_KA");
109 269 if (x != NULL) { if (x != NULL) {
110 set_keepalive = 1;
270 force_keepalive = 1;
111 271 keepalive = strtoul(x, NULL, 0); keepalive = strtoul(x, NULL, 0);
112 syslog(LOG_INFO, "force_bind: Force KA to %u.\n",
272 my_syslog(LOG_INFO, "force_bind: Force KA to %u.\n",
113 273 keepalive); keepalive);
114 274 } }
115 275
 
... ... void init(void)
118 278 if (x != NULL) { if (x != NULL) {
119 279 force_mss = 1; force_mss = 1;
120 280 mss = strtoul(x, NULL, 0); mss = strtoul(x, NULL, 0);
121 syslog(LOG_INFO, "force_bind: Force MSS to %u.\n",
281 my_syslog(LOG_INFO, "force_bind: Force MSS to %u.\n",
122 282 mss); mss);
123 283 } }
124 284
285 /* REUSEADDR */
286 x = getenv("FORCE_NET_REUSEADDR");
287 if (x != NULL) {
288 force_reuseaddr = 1;
289 reuseaddr = strtoul(x, NULL, 0);
290 my_syslog(LOG_INFO, "force_bind: Force REUSEADDR to %u.\n",
291 reuseaddr);
292 }
293
294 /* NODELAY */
295 x = getenv("FORCE_NET_NODELAY");
296 if (x != NULL) {
297 force_nodelay = 1;
298 nodelay = strtoul(x, NULL, 0);
299 my_syslog(LOG_INFO, "force_bind: Force NODELAY to %u.\n",
300 nodelay);
301 }
302
303 /* bandwidth */
304 x = getenv("FORCE_NET_BW");
305 if (x != NULL) {
306 bw_global.limit = strtoul(x, NULL, 0);
307 gettimeofday(&bw_global.last, NULL);
308 bw_global.rest = 0;
309 my_syslog(LOG_INFO, "force_bind: Force bandwidth to %llub/s.\n",
310 bw_global.limit);
311 } else {
312 bw_global.limit = 0;
313 }
314
315 /* bandwidth per socket */
316 x = getenv("FORCE_NET_BW_PER_SOCKET");
317 if (x != NULL) {
318 if (bw_global.limit > 0) {
319 my_syslog(LOG_INFO, "force_bind: Cannot set limit per socket"
320 " when global one is set.\n");
321 } else {
322 bw_limit_per_socket = strtoul(x, NULL, 0);
323 my_syslog(LOG_INFO, "force_bind: Force bandwidth per socket to %llub/s.\n",
324 bw_limit_per_socket);
325 }
326 }
327
328
125 329 old_bind = dlsym(RTLD_NEXT, "bind"); old_bind = dlsym(RTLD_NEXT, "bind");
126 330 if (old_bind == NULL) { if (old_bind == NULL) {
127 syslog(LOG_ERR, "force_bind: Cannot resolve 'bind'!\n");
331 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'bind'!\n");
128 332 exit(1); exit(1);
129 333 } }
130 334
131 335 old_setsockopt = dlsym(RTLD_NEXT, "setsockopt"); old_setsockopt = dlsym(RTLD_NEXT, "setsockopt");
132 336 if (old_setsockopt == NULL) { if (old_setsockopt == NULL) {
133 syslog(LOG_ERR, "force_bind: Cannot resolve 'setsockopt'!\n");
337 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'setsockopt'!\n");
134 338 exit(1); exit(1);
135 339 } }
136 340
137 341 old_socket = dlsym(RTLD_NEXT, "socket"); old_socket = dlsym(RTLD_NEXT, "socket");
138 342 if (old_socket == NULL) { if (old_socket == NULL) {
139 syslog(LOG_ERR, "force_bind: Cannot resolve 'socket'!\n");
343 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'socket'!\n");
344 exit(1);
345 }
346
347 old_close = dlsym(RTLD_NEXT, "close");
348 if (old_close == NULL) {
349 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'close'!\n");
350 exit(1);
351 }
352
353 old_write = dlsym(RTLD_NEXT, "write");
354 if (old_write == NULL) {
355 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'write'!\n");
140 356 exit(1); exit(1);
141 357 } }
358
359 old_send = dlsym(RTLD_NEXT, "send");
360 if (old_send == NULL) {
361 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'send'!\n");
362 exit(1);
363 }
364
365 old_sendto = dlsym(RTLD_NEXT, "sendto");
366 if (old_sendto == NULL) {
367 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'sendto'!\n");
368 exit(1);
369 }
370
371 old_sendmsg = dlsym(RTLD_NEXT, "sendmsg");
372 if (old_sendmsg == NULL) {
373 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'sendmsg'!\n");
374 exit(1);
375 }
376
377 old_accept = dlsym(RTLD_NEXT, "accept");
378 if (old_accept == NULL) {
379 my_syslog(LOG_ERR, "force_bind: Cannot resolve 'accept'!\n");
380 exit(1);
381 }
382
383 my_syslog(LOG_INFO, "force_bind: Inited.\n");
142 384 } }
143 385
144 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
386 static int set_ka(int sockfd)
387 {
388 int flag, ret;
389
390 if (force_keepalive == 0)
391 return 0;
392
393 flag = (keepalive > 0) ? 1 : 0;
394 ret = old_setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag));
395 my_syslog(LOG_INFO, "force_bind: changing SO_KEEPALIVE to %d (ret=%d) [%d].\n",
396 flag, ret, sockfd);
397
398 return 0;
399 }
400
401 static int set_ka_idle(int sockfd)
402 {
403 int ret;
404
405 if (force_keepalive == 0)
406 return 0;
407
408 ret = old_setsockopt(sockfd, IPPROTO_TCP, TCP_KEEPIDLE, &keepalive, sizeof(keepalive));
409 my_syslog(LOG_INFO, "force_bind: changing TCP_KEEPIDLE to %us (ret=%d) [%d].\n",
410 keepalive, ret, sockfd);
411
412 return 0;
413 }
414
415 static int set_mss(int sockfd)
416 {
417 int ret;
418
419 if (force_mss == 0)
420 return 0;
421
422 ret = old_setsockopt(sockfd, IPPROTO_TCP, TCP_MAXSEG, &mss, sizeof(mss));
423 my_syslog(LOG_INFO, "force_bind: changing MSS to %u (ret=%d) [%d].\n",
424 mss, ret, sockfd);
425
426 return 0;
427 }
428
429 static int set_tos(int sockfd)
430 {
431 int ret;
432
433 if (force_tos == 0)
434 return 0;
435
436 ret = old_setsockopt(sockfd, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
437 my_syslog(LOG_INFO, "force_bind: changing TOS to %hhu (ret=%d) [%d].\n",
438 tos, ret, sockfd);
439
440 return 0;
441 }
442
443 static int set_ttl(int sockfd)
444 {
445 int ret;
446
447 if (force_ttl == 0)
448 return 0;
449
450 ret = old_setsockopt(sockfd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl));
451 my_syslog(LOG_INFO, "force_bind: changing TTL to %hhu (ret=%d) [%d].\n",
452 ttl, ret, sockfd);
453
454 return 0;
455 }
456
457 static int set_reuseaddr(int sockfd)
458 {
459 int ret;
460
461 if (force_reuseaddr == 0)
462 return 0;
463
464 ret = old_setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr));
465 my_syslog(LOG_INFO, "force_bind: changing reuseaddr to %u (ret=%d) [%d].\n",
466 reuseaddr, ret, sockfd);
467
468 return 0;
469 }
470
471 static int set_nodelay(int sockfd)
472 {
473 int ret;
474
475 if (force_nodelay == 0)
476 return 0;
477
478 ret = old_setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay));
479 my_syslog(LOG_INFO, "force_bind: changing nodelay to %u (ret=%d) [%d].\n",
480 nodelay, ret, sockfd);
481
482 return 0;
483 }
484
485 static void change_things(int sockfd, struct sockaddr *sa)
145 486 { {
146 487 int err; int err;
147 struct sockaddr new;
488 struct sockaddr_storage tmp;
489 socklen_t tmp_len;
148 490 struct sockaddr_in *sa4; struct sockaddr_in *sa4;
149 491 struct sockaddr_in6 *sa6; struct sockaddr_in6 *sa6;
150 void *p = NULL;
151 492 unsigned short *pport = NULL; unsigned short *pport = NULL;
493 void *p;
494 struct node *q;
495 char *force_address;
496 int force_port;
152 497
153 498 init(); init();
154 499
155 if ((addr->sa_family != AF_INET) && (addr->sa_family != AF_INET6)) {
156 syslog(LOG_INFO, "force_bind: unsupported family=%u!\n",
157 addr->sa_family);
158 return old_bind(sockfd, addr, addrlen);
159 }
500 /* We do not touch non network sockets */
501 q = get(sockfd);
502 if ((q == NULL) || ((q->priv.flags & FB_FLAGS_NETSOCK) == 0))
503 return;
160 504
161 memcpy(&new, addr, sizeof(struct sockaddr));
505 if (sa == NULL) {
506 tmp_len = sizeof(struct sockaddr_storage);
507 err = getsockname(sockfd, (struct sockaddr *) &tmp, &tmp_len);
508 if (err != 0) {
509 my_syslog(LOG_INFO, "force_bind: Cannot get socket name err=%d (%s) [%d]!\n",
510 err, strerror(errno), sockfd);
511 return;
512 }
513 sa = (struct sockaddr *) &tmp;
514 }
162 515
163 switch (new.sa_family) {
516 switch (sa->sa_family) {
164 517 case AF_INET: case AF_INET:
165 sa4 = (struct sockaddr_in *) &new;
518 sa4 = (struct sockaddr_in *) sa;
166 519 p = &sa4->sin_addr; p = &sa4->sin_addr;
167 520 pport = &sa4->sin_port; pport = &sa4->sin_port;
521 force_address = force_address_v4;
522 force_port = force_port_v4;
168 523 break; break;
169 524
170 525 case AF_INET6: case AF_INET6:
171 sa6 = (struct sockaddr_in6 *) &new;
526 sa6 = (struct sockaddr_in6 *) sa;
172 527 p = &sa6->sin6_addr.s6_addr; p = &sa6->sin6_addr.s6_addr;
173 528 pport = &sa6->sin6_port; pport = &sa6->sin6_port;
529 force_address = force_address_v6;
530 force_port = force_port_v6;
174 531 break; break;
532
533 default:
534 my_syslog(LOG_INFO, "force_bind: unsupported family=%u [%d]!\n",
535 sa->sa_family, sockfd);
536 return;
175 537 } }
176 538
177 539 if (force_address != NULL) { if (force_address != NULL) {
178 err = inet_pton(new.sa_family, force_address, p);
540 err = inet_pton(sa->sa_family, force_address, p);
179 541 if (err != 1) { if (err != 1) {
180 syslog(LOG_INFO, "force_bind: cannot convert [%s] (%d)!\n",
181 force_address, err);
182 return old_bind(sockfd, addr, addrlen);
542 my_syslog(LOG_INFO, "force_bind: cannot convert [%s] (%d) (%s) [%d]!\n",
543 force_address, err, strerror(errno), sockfd);
544 return;
183 545 } }
184 546 } }
185 547
186 548 if (force_port != -1) if (force_port != -1)
187 549 *pport = htons(force_port); *pport = htons(force_port);
550 }
188 551
189 return old_bind(sockfd, &new, addrlen);
552 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
553 {
554 struct sockaddr_storage new;
555
556 memcpy(&new, addr, addrlen);
557
558 change_things(sockfd, (struct sockaddr *) &new);
559 return old_bind(sockfd, (struct sockaddr *) &new, addrlen);
190 560 } }
191 561
192 562 int setsockopt(int sockfd, int level, int optname, const void *optval, int setsockopt(int sockfd, int level, int optname, const void *optval,
 
... ... int setsockopt(int sockfd, int level, int optname, const void *optval,
195 565 init(); init();
196 566
197 567 if (level == SOL_SOCKET) { if (level == SOL_SOCKET) {
198 if ((optname == SO_KEEPALIVE) && (set_keepalive == 1)) {
199 set_ka(sockfd);
200 return 0;
201 }
568 if (optname == SO_KEEPALIVE)
569 return set_ka(sockfd);
570 if (optname == SO_REUSEADDR)
571 return set_reuseaddr(sockfd);
202 572 } }
203 573
204 574 if (level == IPPROTO_IP) { if (level == IPPROTO_IP) {
205 if ((optname == IP_TOS) && (set_tos == 1)) {
206 syslog(LOG_INFO, "force_bind: changing TOS from %hhu to %hhu.\n",
207 *(char *)optval, tos);
208 optval = &tos;
209 optlen = sizeof(tos);
210 }
575 if (optname == IP_TOS)
576 return set_tos(sockfd);
577 if (optname == IP_TTL)
578 return set_ttl(sockfd);
211 579 } }
212 580
213 581 if (level == IPPROTO_TCP) { if (level == IPPROTO_TCP) {
214 if ((optname == TCP_KEEPIDLE) && (set_keepalive == 1)) {
215 set_ka_idle(sockfd);
216 return 0;
217 }
218
219 if ((optname == TCP_MAXSEG) && (force_mss == 1)) {
220 set_mss(sockfd);
221 return 0;
222 }
582 if (optname == TCP_KEEPIDLE)
583 return set_ka_idle(sockfd);
584 if (optname == TCP_MAXSEG)
585 return set_mss(sockfd);
586 if (optname == TCP_NODELAY)
587 return set_nodelay(sockfd);
223 588 } }
224 589
225 590 return old_setsockopt(sockfd, level, optname, optval, optlen); return old_setsockopt(sockfd, level, optname, optval, optlen);
226 591 } }
227 592
593 /*
594 * Helper called when a socket is created: socket, accept
595 */
596 void socket_create_callback(const int sockfd, int domain, int type)
597 {
598 struct private p;
599 socklen_t type_len;
600 int err;
601
602 init();
603
604 if (type == -1) {
605 type_len = sizeof(type);
606 err = getsockopt(sockfd, SOL_SOCKET, SO_TYPE, (void *) &type, &type_len);
607 if (err != 0)
608 my_syslog(LOG_INFO, "force_bind: Cannot get socket type err=%d (%s) [%d].\n",
609 err, strerror(errno), sockfd);
610 }
611
612 set_tos(sockfd);
613 set_ttl(sockfd);
614 set_ka(sockfd);
615 if (type == SOCK_STREAM)
616 set_ka_idle(sockfd);
617 set_mss(sockfd);
618 set_reuseaddr(sockfd);
619 set_nodelay(sockfd);
620
621 p.domain = domain;
622 p.type = type;
623 p.flags = FB_FLAGS_NETSOCK;
624 add(sockfd, &p);
625 }
626
228 627 /* /*
229 628 * 'socket' is hijacked to be able to call setsockopt on it. * 'socket' is hijacked to be able to call setsockopt on it.
230 629 */ */
231 630 int socket(int domain, int type, int protocol) int socket(int domain, int type, int protocol)
232 631 { {
233 int sock;
632 int sockfd;
234 633
235 634 init(); init();
236 635
237 sock = old_socket(domain, type, protocol);
238 if (sock == -1)
636 sockfd = old_socket(domain, type, protocol);
637 if (sockfd == -1)
239 638 return -1; return -1;
240 639
241 if (set_tos == 1)
242 setsockopt(sock, IPPROTO_IP, IP_TOS, &tos, sizeof(tos));
640 socket_create_callback(sockfd, domain, type);
243 641
244 if (set_keepalive == 1) {
245 set_ka(sock);
642 return sockfd;
643 }
246 644
247 if (type == SOCK_STREAM)
248 set_ka_idle(sock);
645 /*
646 * Enforce bandwidth
647 */
648 static void bw(const int sockfd, const ssize_t bytes)
649 {
650 struct timeval now;
651 struct timespec ts, rest;
652 long long allowed;
653 long long diff_ms, sleep_ms;
654 int err;
655 struct node *p;
656 struct private *q;
657
658 if (bytes <= 0)
659 return;
660
661 /* Is a network socket? */
662 p = get(sockfd);
663 if (p == NULL)
664 return;
665
666 q = &p->priv;
667 if ((q->flags & FB_FLAGS_NETSOCK) == 0)
668 return;
669
670 if (q->limit == 0) {
671 if (bw_global.limit == 0)
672 return;
673 q = &bw_global;
249 674 } }
250 675
251 if (force_mss == 1)
252 set_mss(sock);
676 gettimeofday(&now, NULL);
677
678 diff_ms = (now.tv_sec - q->last.tv_sec) * 1000
679 + (now.tv_usec - q->last.tv_usec) / 1000;
680 if (diff_ms < 0)
681 return;
682
683 allowed = q->rest + q->limit * diff_ms / 1000;
684 q->last = now;
685
686 printf("diff_ms=%lld rest=%llu bytes=%u allowed=%llub\n",
687 diff_ms, q->rest, bytes, allowed);
688
689 if (bytes <= allowed) {
690 q->rest = allowed - bytes;
691 printf("\tInside limit, rest=%llu.\n", q->rest);
692 return;
693 }
694
695 q->rest = 0;
696 sleep_ms = (bytes - allowed) * 1000 / q->limit;
697
698 ts.tv_sec = sleep_ms / 1000;
699 ts.tv_nsec = (sleep_ms % 1000) * 1000 * 1000;
700 printf("\tWe will sleep %lus %lunsec.\n", ts.tv_sec, ts.tv_nsec);
701
702 /* We try to sleep even if we are interrupted by signals */
703 while (1) {
704 err = nanosleep(&ts, &rest);
705 if (err == -1) {
706 if (errno == EINTR) {
707 ts = rest;
708 continue;
709 }
710
711 my_syslog(LOG_INFO, "force_bind: nanosleep returned error"
712 " (%d) (%s).\n",
713 err, strerror(errno));
714 }
715
716 break;
717 }
718 }
719
720 int close(int fd)
721 {
722 init();
723
724 del(fd);
725
726 return old_close(fd);
727 }
728
729 ssize_t write(int fd, const void *buf, size_t len)
730 {
731 ssize_t n;
732
733 change_things(fd, NULL);
734 n = old_write(fd, buf, len);
735 bw(fd, n);
736
737 return n;
738 }
739
740 ssize_t send(int sockfd, const void *buf, size_t len, int flags)
741 {
742 ssize_t n;
743
744 change_things(sockfd, NULL);
745 n = old_send(sockfd, buf, len, flags);
746 bw(sockfd, n);
747
748 return n;
749 }
750
751 ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
752 const struct sockaddr *dest_addr, socklen_t addrlen)
753 {
754 ssize_t n;
755
756 change_things(sockfd, NULL);
757 n = old_sendto(sockfd, buf, len, flags, dest_addr, addrlen);
758 bw(sockfd, n);
253 759
254 return sock;
760 return n;
255 761 } }
256 762
763 /*
764 * TODO: Add sendmmsg
765 */
766 ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)
767 {
768 ssize_t n;
769
770 change_things(sockfd, NULL);
771 n = old_sendmsg(sockfd, msg, flags);
772 bw(sockfd, n);
773
774 return n;
775 }
776
777 /*
778 * We have to hijack accept because this program may be a daemon.
779 * TODO: accept4 should also be hijacked.
780 */
781 int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
782 {
783 int new_sock;
784
785 init();
786
787 new_sock = old_accept(sockfd, addr, addrlen);
788
789 socket_create_callback(new_sock, -1, -1);
790
791 return new_sock;
792 }
File force_bind.spec.in changed (mode: 100644) (index 634c577..384112a)
... ... BuildRoot: %{_tmppath}/%{name}-%{version}-buildroot
11 11
12 12
13 13 %description %description
14 It uses LD_PRELOAD to hijack 'bind' system call. Environment variables
15 FORCE_BIND_ADDRESS and FORCE_BIND_PORT can be used to control it.
14 It uses LD_PRELOAD to manipulate socket calls. Environment variables
15 can be used to control in what way you want to bind/change TOS/MSS/KA/bandwidth.
16 16
17 17 %prep %prep
18 18 %setup %setup
File send_udp.c added (mode: 100644) (index 0000000..96ea103)
1 #include <sys/types.h>
2 #include <sys/socket.h>
3 #include <sys/un.h>
4 #include <stdlib.h>
5 #include <stdio.h>
6 #include <string.h>
7 #include <unistd.h>
8 #include <arpa/inet.h>
9
10 int main(int argc, char *argv[])
11 {
12 int sock, err;
13 struct sockaddr_in sa;
14 int port = 123;
15 unsigned char buf[4096];
16 unsigned int bytes = 100000, rest, max;
17
18 sock = socket(AF_INET, SOCK_DGRAM, 0);
19 if (sock == -1) {
20 perror("socket");
21 return 1;
22 }
23
24 if (argc >= 2)
25 port = strtol(argv[1], NULL, 10);
26
27 if (argc >= 3)
28 bytes = strtol(argv[2], NULL, 10);
29
30 memset(&sa, 0, sizeof(struct sockaddr));
31 sa.sin_family = AF_INET;
32 sa.sin_port = htons(port);
33 sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
34
35 memset(buf, 'A', sizeof(buf));
36
37 rest = bytes;
38 while (rest > 0) {
39 max = sizeof(buf);
40 if (rest < max)
41 max = rest;
42 printf("Sending %u bytes...\n", max);
43 err = sendto(sock, buf, max, 0, (struct sockaddr *) &sa, sizeof(sa));
44 if (err == -1) {
45 perror("sendto");
46 break;
47 }
48
49 rest -= err;
50 }
51
52 close(sock);
53
54 return 0;
55 }
File test1.sh changed (mode: 100755) (index 83d21d2..69df55f)
1 1 #!/bin/sh #!/bin/sh
2 2
3 export FORCE_BIND_ADDRESS=127.0.0.2
3 export FORCE_BIND_ADDRESS_V4=127.0.0.2
4 4
5 5 # use -1 to not change port # use -1 to not change port
6 export FORCE_BIND_PORT=900
6 export FORCE_BIND_PORT_V4=900
7 7
8 8 export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so" export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so"
9 9
10 exec "$@"
10 strace -o test1.strace -s100 -f "$@"
File test2.sh added (mode: 100755) (index 0000000..2d9a688)
1 #!/bin/sh
2
3 export FORCE_BIND_ADDRESS_V4=127.0.0.2
4
5 # use -1 to not change port
6 export FORCE_BIND_PORT_V4=900
7
8 export LD_PRELOAD="${LD_PRELOAD}:/usr/lib/force_bind.so"
9
10 exec "$@"
File test_all.sh added (mode: 100755) (index 0000000..711d784)
1 #!/bin/sh
2
3 export FORCE_NET_VERBOSE=1
4 export FORCE_NET_TOS="0x0f"
5 export FORCE_NET_KA=60
6 export FORCE_NET_MSS=1400
7 export FORCE_NET_REUSEADDR=1
8 export FORCE_NET_NODELAY=1
9
10 export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so"
11
12 debug ./test_bind
13
14 strace -o test_all.strace -f -s100 ./test_bind
15 strace -o test_all2.strace -f -s100 telnet www.kernel.org 80
File test_bw1.sh added (mode: 100755) (index 0000000..9a925f2)
1 #!/bin/sh
2
3 # Test bandwidth limiting
4
5 ulimit -c2000000
6
7 export FORCE_NET_BW="1000"
8
9 export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so"
10
11 time strace -f -s200 -o test_bw1.strace ./send_udp 123 3000
File test_bw2.sh added (mode: 100755) (index 0000000..6fda450)
1 #!/bin/sh
2
3 # Test bandwidth limiting
4
5 ulimit -c2000000
6
7 export FORCE_NET_BW_PER_SOCKET="1000"
8
9 export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so"
10
11 time strace -f -s200 -o test_bw2.strace ./send_udp 123 3000
File test_bw3.sh added (mode: 100755) (index 0000000..805a280)
1 #!/bin/sh
2
3 # Test bandwidth limiting when both bw options are provided
4
5 ulimit -c2000000
6
7 export FORCE_NET_BW="1000"
8 export FORCE_NET_BW_PER_SOCKET="1000" # this should fail
9
10 export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so"
11
12 time strace -f -s200 -o test_bw2.strace ./send_udp 123 3000
File test_ka1.sh changed (mode: 100755) (index 3abe306..15e7a1b)
1 1 #!/bin/sh #!/bin/sh
2 2
3 export FORCE_NET_TOS="0xff"
3 export FORCE_NET_KA=60
4 4
5 5 export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so" export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so"
6 6
7 exec ./test_bind "$@"
7 strace -o test_ka1.strace -f -s100 ./test_bind "$@"
File test_tos1.sh changed (mode: 100755) (index 3abe306..e9620dd)
1 1 #!/bin/sh #!/bin/sh
2 2
3 export FORCE_NET_TOS="0xff"
3 export FORCE_NET_TOS="0x0f"
4 4
5 5 export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so" export LD_PRELOAD="${LD_PRELOAD}:./force_bind.so"
6 6
7 exec ./test_bind "$@"
7 strace -o test_tos1.strace -f -s100 ./test_bind "$@"
Hints:
Before first commit, do not forget to setup your git environment:
git config --global user.name "your_name_here"
git config --global user.email "your@email_here"

Clone this repository using HTTP(S):
git clone https://rocketgit.com/user/catalinux/force_bind

Clone this repository using ssh (do not forget to upload a key first):
git clone ssh://rocketgit@ssh.rocketgit.com/user/catalinux/force_bind

Clone this repository using git:
git clone git://git.rocketgit.com/user/catalinux/force_bind

You are allowed to anonymously push to this repository.
This means that your pushed commits will automatically be transformed into a merge request:
... clone the repository ...
... make some changes and some commits ...
git push origin main