├── README.md ├── src ├── Makefile └── tcp_ledbat.c └── util ├── client.c └── server.c /README.md: -------------------------------------------------------------------------------- 1 | # TCP-LEDBAT kernel module 2 | 3 | This is an implementation of the LEDBAT congestion control algorithm over TCP 4 | using the Linux kernel modular congestion control framework. 5 | 6 | The current version is updated to compile under Linux kernel 5.4.x versions. A version 7 | working with kernels up to 3.3.x is tagged with a "3.3" tag. The module has not been tested 8 | with version in between. 9 | 10 | The module currently loads properly but it has *not* been tested thoroughly. 11 | 12 | For more information also visit http://perso.telecom-paristech.fr/~drossi/index.php?n=Software.LEDBAT 13 | 14 | ## How to build 15 | 16 | * clone the this repository 17 | 18 | ```shell 19 | $ git clone https://github.com/silviov/TCP-LEDBAT.git 20 | ``` 21 | 22 | * build the module 23 | 24 | ```shell 25 | $ cd TCP-LEDBAT/src 26 | $ make 27 | ``` 28 | 29 | * load the module 30 | 31 | ```shell 32 | $ sudo make install 33 | ``` 34 | 35 | ## How to use the module 36 | 37 | After the module has been loaded check "ledbat" should appear among the available 38 | congestion control algorithms. You should see something like 39 | 40 | ``` shell 41 | $ cat /proc/sys/net/ipv4/tcp_available_congestion_control 42 | cubic reno ledbat 43 | ``` 44 | 45 | Then you can either use it for all flows by doing 46 | 47 | ```shell 48 | $ sudo sysctl -w net.ipv4.tcp_congestion_control=ledbat 49 | ``` 50 | 51 | or you can do it on specific sockets via the `setsockopt` option. You can see an 52 | example in the `utils/client.c` file. 53 | 54 | ## How to debug 55 | 56 | At the top of the source file for the module you can find some constant 57 | definitions. By setting their values you can select the level of debugging 58 | information printed by the module. 59 | 60 | In order to see the messages, however, remember to set the debug level of the 61 | kernel to DEBUG with the command 62 | 63 | ```shell 64 | $ sudo sysctl kernel.printk=8 65 | ``` 66 | 67 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | obj-m := tcp_ledbat.o 2 | IDIR= /lib/modules/$(shell uname -r)/kernel/net/ipv4/ 3 | KDIR := /lib/modules/$(shell uname -r)/build 4 | PWD := $(shell pwd) 5 | default: 6 | $(MAKE) -C $(KDIR) M=$(PWD) modules 7 | 8 | install: 9 | install -v -m 644 tcp_ledbat.ko $(IDIR) 10 | depmod 11 | modprobe tcp_ledbat 12 | sysctl -w net.ipv4.tcp_allowed_congestion_control="cubic reno ledbat" 13 | 14 | clean: 15 | rm -rf Module.markers modules.order Module.symvers tcp_ledbat.ko tcp_ledbat.mod.c tcp_ledbat.mod.o tcp_ledbat.o 16 | -------------------------------------------------------------------------------- /src/tcp_ledbat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * TCP-LEDBAT 3 | 4 | * Implement the congestion control algorithm described in 5 | * draft-shalunov-ledbat-congestion-00.txt available at 6 | * http://tools.ietf.org/html/draft-shalunov-ledbat-congestion-00 7 | * 8 | * Our implementation is derived from the TCP-LP kernel implementation 9 | * (cfr. tcp_lp.c) 10 | * 11 | * Created by Silvio Valenti on tue 2nd June 2009 12 | */ 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | /* resolution of owd */ 19 | #define LP_RESOL 1000 20 | 21 | #define DEBUG_SLOW_START 0 22 | #define DEBUG_DELAY 0 23 | #define DEBUG_OWD_HZ 0 24 | #define DEBUG_NOISE_FILTER 0 25 | #define DEBUG_BASE_HISTO 0 26 | 27 | /* NOTE: len are the actual length - 1 */ 28 | static int base_histo_len = 10; 29 | static int noise_filter_len = 4; 30 | static int target = 100; 31 | static int gain_num = 1; 32 | static int gain_den = 1; 33 | static int do_ss = 0; 34 | static int ledbat_ssthresh = 0xffff; 35 | 36 | module_param(base_histo_len, int, 0644); 37 | MODULE_PARM_DESC(base_histo_len, "length of the base history vector"); 38 | module_param(noise_filter_len, int, 0644); 39 | MODULE_PARM_DESC(noise_filter_len, "length of the noise_filter vector"); 40 | module_param(target, int, 0644); 41 | MODULE_PARM_DESC(target, "target queuing delay"); 42 | module_param(gain_num, int, 0644); 43 | MODULE_PARM_DESC(gain_num, "multiplicative factor of the gain"); 44 | module_param(gain_den, int, 0644); 45 | MODULE_PARM_DESC(gain_den, "multiplicative factor of the gain"); 46 | 47 | /* Our extensions to play with slow start */ 48 | #define DO_NOT_SLOWSTART 0 49 | #define DO_SLOWSTART 1 50 | #define DO_SLOWSTART_WITH_THRESHOLD 2 51 | 52 | module_param(do_ss, int, 0644); 53 | MODULE_PARM_DESC(do_ss, "do slow start: 0 no, 1 yes, 2 with_ssthresh"); 54 | module_param(ledbat_ssthresh, int, 0644); 55 | MODULE_PARM_DESC(ledbat_ssthresh, "slow start threshold"); 56 | 57 | struct owd_circ_buf { 58 | u32 *buffer; 59 | u8 first; 60 | u8 next; 61 | u8 len; 62 | u8 min; 63 | }; 64 | 65 | /** 66 | * enum tcp_ledbat_state 67 | * @LP_VALID_RHZ: is remote HZ valid? 68 | * @LP_VALID_OWD: is OWD valid? 69 | * @LP_WITHIN_THR: are we within threshold? 70 | * @LP_WITHIN_INF: are we within inference? 71 | * 72 | * TCP-LEDBAT's state flags. 73 | * We create this set of state flags mainly for debugging. 74 | */ 75 | enum tcp_ledbat_state { 76 | LEDBAT_VALID_RHZ = (1 << 0), 77 | LEDBAT_VALID_OWD = (1 << 1), 78 | LEDBAT_INCREASING = (1 << 2), 79 | LEDBAT_CAN_SS = (1 << 3), 80 | }; 81 | 82 | /** 83 | * struct ledbat 84 | */ 85 | struct ledbat { 86 | u32 last_rollover; 87 | u32 remote_hz; 88 | u32 remote_ref_time; 89 | u32 local_ref_time; 90 | u32 snd_cwnd_cnt; /* already in struct tcp_sock but we need 32 bits. */ 91 | u32 last_ack; 92 | struct owd_circ_buf base_history; 93 | struct owd_circ_buf noise_filter; 94 | u32 flag; 95 | }; 96 | 97 | static int ledbat_init_circbuf(struct owd_circ_buf *buffer, u16 len) 98 | { 99 | u32 *b = kmalloc(len * sizeof(u32), GFP_KERNEL); 100 | if (b == NULL) 101 | return 1; 102 | buffer->len = len; 103 | buffer->buffer = b; 104 | buffer->first = 0; 105 | buffer->next = 0; 106 | buffer->min = 0; 107 | return 0; 108 | } 109 | 110 | static void tcp_ledbat_release(struct sock *sk) 111 | { 112 | struct ledbat *ledbat = inet_csk_ca(sk); 113 | kfree(ledbat->noise_filter.buffer); 114 | kfree(ledbat->base_history.buffer); 115 | } 116 | 117 | /** 118 | * tcp_ledbat_init 119 | */ 120 | static void tcp_ledbat_init(struct sock *sk) 121 | { 122 | struct ledbat *ledbat = inet_csk_ca(sk); 123 | 124 | ledbat_init_circbuf(&(ledbat->base_history), base_histo_len + 1); 125 | ledbat_init_circbuf(&(ledbat->noise_filter), noise_filter_len + 1); 126 | 127 | ledbat->last_rollover = 0; 128 | ledbat->flag = 0; 129 | ledbat->remote_hz = 0; 130 | ledbat->remote_ref_time = 0; 131 | ledbat->local_ref_time = 0; 132 | ledbat->snd_cwnd_cnt = 0; 133 | ledbat->last_ack = 0; 134 | 135 | if (do_ss) { 136 | ledbat->flag |= LEDBAT_CAN_SS; 137 | } 138 | 139 | } 140 | 141 | typedef u32(*ledbat_filter_function) (struct owd_circ_buf *); 142 | 143 | static u32 ledbat_min_circ_buff(struct owd_circ_buf *b) 144 | { 145 | /* 146 | The draft requires all history to be set to +infinity on initialization. 147 | We obtain the same behavior returning +infinity in case of empty history. 148 | */ 149 | if (b->first == b->next) 150 | return 0xffffffff; 151 | return b->buffer[b->min]; 152 | } 153 | 154 | static 155 | u32 ledbat_current_delay(struct ledbat *ledbat, ledbat_filter_function filter) 156 | { 157 | return filter(&(ledbat->noise_filter)); 158 | } 159 | 160 | static u32 ledbat_base_delay(struct ledbat *ledbat) 161 | { 162 | return ledbat_min_circ_buff(&(ledbat->base_history)); 163 | } 164 | 165 | #if DEBUG_NOISE_FILTER || DEBUG_BASE_HISTO 166 | static void print_delay(struct owd_circ_buf *cb, char *name) 167 | { 168 | u16 curr = cb->first; 169 | printk(KERN_DEBUG "%s: time %u ", name, tcp_time_stamp); 170 | 171 | while (curr != cb->next) { 172 | printk(KERN_DEBUG "%u ", cb->buffer[curr]); 173 | curr = (curr + 1) % cb->len; 174 | } 175 | 176 | printk(KERN_DEBUG "min %u, len %u, first %u, next %u\n", 177 | cb->buffer[cb->min], cb->len, cb->first, cb->next); 178 | } 179 | #endif 180 | 181 | static 182 | u32 tcp_ledbat_ssthresh(struct sock *sk) 183 | { 184 | u32 res; 185 | switch (do_ss) { 186 | case DO_NOT_SLOWSTART: 187 | case DO_SLOWSTART: 188 | default: 189 | res = tcp_reno_ssthresh(sk); 190 | break; 191 | case DO_SLOWSTART_WITH_THRESHOLD: 192 | res = ledbat_ssthresh; 193 | break; 194 | } 195 | 196 | return res; 197 | } 198 | 199 | /** 200 | * tcp_ledbat_cong_avoid 201 | */ 202 | static void tcp_ledbat_cong_avoid(struct sock *sk, u32 ack, u32 acked) 203 | // ns2 version takes also this two parameters: u32 rtt, int flag 204 | { 205 | 206 | struct ledbat *ledbat = inet_csk_ca(sk); 207 | struct tcp_sock *tp = tcp_sk(sk); 208 | s64 queue_delay; 209 | s64 offset; 210 | s64 cwnd; 211 | u32 max_cwnd; 212 | s64 current_delay; 213 | s64 base_delay; 214 | 215 | /*if no valid data return */ 216 | if (!(ledbat->flag & LEDBAT_VALID_OWD)) 217 | return; 218 | 219 | max_cwnd = ((u32) (tp->snd_cwnd)) * target; 220 | 221 | /* 222 | This checks that we are not limited by the congestion window nor by the 223 | application, and is basically the same check that it is performed in the 224 | draft. 225 | */ 226 | if (!tcp_is_cwnd_limited(sk)) 227 | return; 228 | 229 | if (tp->snd_cwnd <= 1) 230 | ledbat->flag |= LEDBAT_CAN_SS; 231 | 232 | if (do_ss >= DO_SLOWSTART && tp->snd_cwnd <= tcp_ledbat_ssthresh(sk) && 233 | (ledbat->flag & LEDBAT_CAN_SS)) { 234 | #if DEBUG_SLOW_START 235 | printk(KERN_DEBUG 236 | "slow_start!!! clamp %d cwnd %d sshthresh %d \n", 237 | tp->snd_cwnd_clamp, tp->snd_cwnd, tp->snd_ssthresh); 238 | #endif 239 | acked = tcp_slow_start(tp, acked); 240 | if (!acked) 241 | return; 242 | } else { 243 | ledbat->flag &= ~LEDBAT_CAN_SS; 244 | } 245 | 246 | /* This allows to eventually define new filters for the current delay. */ 247 | current_delay = 248 | ((s64) ledbat_current_delay(ledbat, &ledbat_min_circ_buff)); 249 | base_delay = ((s64) ledbat_base_delay(ledbat)); 250 | 251 | queue_delay = current_delay - base_delay; 252 | offset = ((s64) target) - (queue_delay); 253 | 254 | offset *= gain_num; 255 | do_div(offset, gain_den); 256 | 257 | /* Do not ramp more than TCP. */ 258 | if (offset > target) 259 | offset = target; 260 | 261 | #if DEBUG_DELAY 262 | printk(KERN_DEBUG 263 | "time %u, queue_delay %lld, offset %lld cwnd_cnt %u, " 264 | "cwnd %u, delay %lld, min %lld\n", 265 | tcp_time_stamp, queue_delay, offset, ledbat->snd_cwnd_cnt, 266 | tp->snd_cwnd, current_delay, base_delay); 267 | #endif 268 | 269 | /* calculate the new cwnd_cnt */ 270 | cwnd = ledbat->snd_cwnd_cnt + offset; 271 | if (cwnd >= 0) { 272 | /* if we have a positive number update the cwnd_count */ 273 | ledbat->snd_cwnd_cnt = cwnd; 274 | if (ledbat->snd_cwnd_cnt >= max_cwnd) { 275 | /* increase the cwnd */ 276 | if (tp->snd_cwnd < tp->snd_cwnd_clamp) 277 | tp->snd_cwnd++; 278 | ledbat->snd_cwnd_cnt = 0; 279 | } 280 | } else { 281 | /* we need to decrease the cwnd but we do not want to set it to 0! */ 282 | if (tp->snd_cwnd > 1) { 283 | tp->snd_cwnd--; 284 | /* set the cwnd_cnt to the max value - target */ 285 | ledbat->snd_cwnd_cnt = (tp->snd_cwnd - 1) * target; 286 | } else { 287 | tp->snd_cwnd_cnt = 0; 288 | } 289 | } 290 | 291 | } 292 | 293 | /** 294 | * tcp_ledbat_remote_hz_estimator 295 | * 296 | * Estimate remote HZ. 297 | * We keep on updating the estimated value, where original TCP-LP 298 | * implementation only guesses it once and uses it forever. 299 | */ 300 | static u32 tcp_ledbat_remote_hz_estimator(struct sock *sk) 301 | { 302 | struct tcp_sock *tp = tcp_sk(sk); 303 | struct ledbat *ledbat = inet_csk_ca(sk); 304 | s64 rhz = ledbat->remote_hz << 6; /* remote HZ << 6 */ 305 | s64 m = 0; 306 | 307 | if (ledbat->last_rollover == 0) 308 | ledbat->last_rollover = tcp_time_stamp; 309 | 310 | /* not yet record reference time 311 | * go away!! record it before come back!! */ 312 | if (ledbat->remote_ref_time == 0 || ledbat->local_ref_time == 0) 313 | goto out; 314 | 315 | /* we can't calc remote HZ with no difference!! */ 316 | if (tp->rx_opt.rcv_tsval == ledbat->remote_ref_time 317 | || tp->rx_opt.rcv_tsecr == ledbat->local_ref_time) 318 | goto out; 319 | 320 | m = HZ * (tp->rx_opt.rcv_tsval - 321 | ledbat->remote_ref_time) / (tp->rx_opt.rcv_tsecr - 322 | ledbat->local_ref_time); 323 | if (m < 0) 324 | m = -m; 325 | 326 | if (rhz > 0) { 327 | m -= rhz >> 6; /* m is now wrong in remote HZ est */ 328 | rhz += m; /* 63/64 old + 1/64 new */ 329 | } else 330 | rhz = m << 6; 331 | 332 | out: 333 | /* record time for successful remote HZ calc */ 334 | if ((rhz >> 6) > 0) 335 | ledbat->flag |= LEDBAT_VALID_RHZ; 336 | else 337 | ledbat->flag &= ~LEDBAT_VALID_RHZ; 338 | 339 | /* record reference time stamp */ 340 | 341 | ledbat->remote_ref_time = tp->rx_opt.rcv_tsval; 342 | ledbat->local_ref_time = tp->rx_opt.rcv_tsecr; 343 | 344 | return rhz >> 6; 345 | } 346 | 347 | /** 348 | * tcp_ledbat_owd_calculator 349 | * 350 | * I stole this code from tcp_lp.c. Please also see comment for 351 | * tcp_lp_owd_calculator. 352 | */ 353 | static u32 tcp_ledbat_owd_calculator(struct sock *sk) 354 | { 355 | struct tcp_sock *tp = tcp_sk(sk); 356 | struct ledbat *ledbat = inet_csk_ca(sk); 357 | s64 owd = 0; 358 | 359 | ledbat->remote_hz = tcp_ledbat_remote_hz_estimator(sk); 360 | 361 | if (ledbat->flag & LEDBAT_VALID_RHZ) { 362 | owd = tp->rx_opt.rcv_tsval * (LP_RESOL / ledbat->remote_hz) - 363 | tp->rx_opt.rcv_tsecr * (LP_RESOL / HZ); 364 | if (owd < 0) 365 | owd = -owd; 366 | } 367 | 368 | /* Safe net. */ 369 | if (owd > 0) 370 | ledbat->flag |= LEDBAT_VALID_OWD; 371 | else 372 | ledbat->flag &= ~LEDBAT_VALID_OWD; 373 | 374 | #if DEBUG_OWD_HZ 375 | printk(KERN_DEBUG "my_hz %u, hz %u owd %u\n", HZ, 376 | (u32) ledbat->remote_hz, (u32) owd); 377 | #endif 378 | return owd; 379 | } 380 | 381 | static void ledbat_add_delay(struct owd_circ_buf *cb, u32 owd) 382 | { 383 | u8 i; 384 | 385 | if (cb->next == cb->first) { 386 | /*buffer is empty */ 387 | cb->buffer[cb->next] = owd; 388 | cb->min = cb->next; 389 | cb->next++; 390 | return; 391 | } 392 | 393 | /*set the new delay */ 394 | cb->buffer[cb->next] = owd; 395 | /* update the min if it is the case */ 396 | if (owd < cb->buffer[cb->min]) 397 | cb->min = cb->next; 398 | 399 | /* increment the next pointer */ 400 | cb->next = (cb->next + 1) % cb->len; 401 | 402 | if (cb->next == cb->first) { 403 | /* Discard the first element */ 404 | if (cb->min == cb->first) { 405 | /* Discard the min, search a new one */ 406 | cb->min = i = (cb->first + 1) % cb->len; 407 | while (i != cb->next) { 408 | if (cb->buffer[i] < cb->buffer[cb->min]) 409 | cb->min = i; 410 | i = (i + 1) % cb->len; 411 | } 412 | } 413 | /* move the first */ 414 | cb->first = (cb->first + 1) % cb->len; 415 | } 416 | } 417 | 418 | static void ledbat_update_current_delay(struct ledbat *ledbat, u32 owd) 419 | { 420 | ledbat_add_delay(&(ledbat->noise_filter), owd); 421 | #if DEBUG_NOISE_FILTER 422 | printk(KERN_DEBUG " added delay to noisefilter %u\n", owd); 423 | print_delay(&(ledbat->noise_filter), "noise_filter"); 424 | #endif 425 | } 426 | 427 | static void ledbat_update_base_delay(struct ledbat *ledbat, u32 owd) 428 | { 429 | u32 last; 430 | struct owd_circ_buf *cb = &(ledbat->base_history); 431 | 432 | if (ledbat->base_history.next == ledbat->base_history.first) { 433 | /* empty circular buffer */ 434 | ledbat_add_delay(cb, owd); 435 | return; 436 | } 437 | 438 | if (tcp_time_stamp - ledbat->last_rollover > 60 * HZ) { 439 | /* we have finished a minute */ 440 | #if DEBUG_BASE_HISTO 441 | printk(KERN_DEBUG " time %u, new rollover \n", tcp_time_stamp); 442 | #endif 443 | ledbat->last_rollover = tcp_time_stamp; 444 | ledbat_add_delay(cb, owd); 445 | } else { 446 | /* update the last value and the min if it is the case */ 447 | last = (cb->next + cb->len - 1) % cb->len; 448 | 449 | if (owd < cb->buffer[last]) { 450 | cb->buffer[last] = owd; 451 | if (owd < cb->buffer[cb->min]) 452 | cb->min = last; 453 | } 454 | 455 | } 456 | #if DEBUG_BASE_HISTO 457 | printk(KERN_DEBUG " added delay to base_history %s", "\n"); 458 | print_delay(&(ledbat->base_history), "base_history"); 459 | #endif 460 | } 461 | 462 | /** 463 | * tcp_ledbat_rtt_sample 464 | * 465 | * - calculate the owd 466 | * - add the delay to noise filter 467 | * - if new minute add the delay to base delay or update last delay 468 | */ 469 | static void tcp_ledbat_rtt_sample(struct sock *sk, u32 rtt) 470 | { 471 | struct ledbat *ledbat = inet_csk_ca(sk); 472 | s64 mowd = tcp_ledbat_owd_calculator(sk); 473 | 474 | /* sorry that we don't have valid data */ 475 | if (!(ledbat->flag & LEDBAT_VALID_RHZ) 476 | || !(ledbat->flag & LEDBAT_VALID_OWD)) { 477 | return; 478 | } 479 | 480 | ledbat_update_current_delay(ledbat, (u32) mowd); 481 | ledbat_update_base_delay(ledbat, (u32) mowd); 482 | } 483 | 484 | /** 485 | * tcp_ledbat_pkts_acked 486 | */ 487 | static void tcp_ledbat_pkts_acked(struct sock *sk, 488 | const struct ack_sample *sample) 489 | { 490 | struct ledbat *ledbat = inet_csk_ca(sk); 491 | struct tcp_sock *tp = tcp_sk(sk); 492 | 493 | if (sample->rtt_us > 0) 494 | tcp_ledbat_rtt_sample(sk, sample->rtt_us); 495 | 496 | if (ledbat->last_ack == 0) 497 | ledbat->last_ack = tcp_time_stamp; 498 | else if (after(tcp_time_stamp, ledbat->last_ack + usecs_to_jiffies(tp->srtt_us >> 3))) { 499 | /* we haven't received an acknowledgement for more than a rtt. 500 | Set the congestion window to 1. */ 501 | tp->snd_cwnd = 1; 502 | } 503 | ledbat->last_ack = tcp_time_stamp; 504 | 505 | } 506 | 507 | /** 508 | * tcp_ledbat_undo_cwnd 509 | * required starting kernel v4.10, see 510 | * kernel commmit e97991832a4ea4a5f47d65f068a4c966a2eb5730 511 | */ 512 | static u32 tcp_ledbat_undo_cwnd(struct sock *sk) 513 | { 514 | /* this is the default v4.9 behavior */ 515 | const struct tcp_sock *tp = tcp_sk(sk); 516 | return max(tp->snd_cwnd, tp->snd_ssthresh << 1); 517 | } 518 | 519 | static struct tcp_congestion_ops tcp_ledbat = { 520 | .init = tcp_ledbat_init, 521 | .ssthresh = tcp_ledbat_ssthresh, 522 | .cong_avoid = tcp_ledbat_cong_avoid, 523 | .pkts_acked = tcp_ledbat_pkts_acked, 524 | .undo_cwnd = tcp_ledbat_undo_cwnd, 525 | .release = tcp_ledbat_release, 526 | 527 | .owner = THIS_MODULE, 528 | .name = "ledbat" 529 | }; 530 | 531 | static int __init tcp_ledbat_register(void) 532 | { 533 | BUILD_BUG_ON(sizeof(struct ledbat) > ICSK_CA_PRIV_SIZE); 534 | return tcp_register_congestion_control(&tcp_ledbat); 535 | } 536 | 537 | static void __exit tcp_ledbat_unregister(void) 538 | { 539 | tcp_unregister_congestion_control(&tcp_ledbat); 540 | } 541 | 542 | module_init(tcp_ledbat_register); 543 | module_exit(tcp_ledbat_unregister); 544 | 545 | MODULE_AUTHOR("Silvio Valenti"); 546 | MODULE_LICENSE("GPL"); 547 | MODULE_DESCRIPTION("TCP Ledbat (Low Extra Delay Background Transport)"); 548 | -------------------------------------------------------------------------------- /util/client.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | //#include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define DIRSIZE 8192 14 | 15 | typedef struct _options { 16 | char *hostname; 17 | uint16_t port; 18 | uint32_t count; 19 | uint16_t duration; 20 | } options_data; 21 | 22 | void end_program(int signal) 23 | { 24 | fprintf(stderr, "exiting after duration!!!\n"); 25 | exit(0); 26 | } 27 | 28 | void print_help(char *progname) 29 | { 30 | printf("Usage: %s [-p port] [-t duration] [-c count] hostname\n", 31 | progname); 32 | } 33 | 34 | void parse_opt(int argc, char *argv[], options_data * opts) 35 | { 36 | int opt; 37 | 38 | opts->hostname = ""; 39 | opts->port = 5001; //same as iperf 40 | opts->count = 0; 41 | opts->duration = 0; 42 | 43 | while ((opt = getopt(argc, argv, "hp:c:t:")) != -1) { 44 | switch (opt) { 45 | case 'p': 46 | opts->port = atoi(optarg); 47 | break; 48 | case 'c': 49 | opts->count = atoi(optarg); 50 | break; 51 | case 't': 52 | opts->duration = atoi(optarg); 53 | break; 54 | default: 55 | case 'h': 56 | print_help(argv[0]); 57 | exit(EXIT_SUCCESS); 58 | } 59 | } 60 | 61 | if (optind >= argc) { 62 | fprintf(stderr, "Expected hostname\n"); 63 | exit(EXIT_FAILURE); 64 | } 65 | 66 | opts->hostname = argv[optind]; 67 | return; 68 | } 69 | 70 | int main(int argc, char *argv[]) 71 | { 72 | char dir[DIRSIZE]; 73 | int sd; 74 | struct sockaddr_in sin; 75 | struct sockaddr_in pin; 76 | struct hostent *hp; 77 | 78 | options_data opts; 79 | 80 | parse_opt(argc, argv, &opts); 81 | 82 | signal(SIGALRM, end_program); 83 | 84 | /* go find out about the desired host machine */ 85 | if ((hp = gethostbyname(opts.hostname)) == 0) { 86 | perror("gethostbyname"); 87 | exit(EXIT_FAILURE); 88 | } 89 | 90 | /* fill in the socket structure with host information */ 91 | memset(&pin, 0, sizeof(pin)); 92 | pin.sin_family = AF_INET; 93 | pin.sin_addr.s_addr = ((struct in_addr *)(hp->h_addr))->s_addr; 94 | pin.sin_port = htons(opts.port); 95 | 96 | /* grab an Internet domain socket */ 97 | if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 98 | perror("socket"); 99 | exit(EXIT_FAILURE); 100 | } 101 | 102 | /*set the congestion control algorithm to ledbat */ 103 | if (setsockopt(sd, SOL_TCP, TCP_CONGESTION, "ledbat", 6) == -1) { 104 | perror("setsockopt"); 105 | exit(EXIT_FAILURE); 106 | } 107 | 108 | /* connect to the host */ 109 | if (connect(sd, (struct sockaddr *)&pin, sizeof(pin)) == -1) { 110 | perror("connect"); 111 | exit(EXIT_FAILURE); 112 | } 113 | 114 | if (opts.duration > 0) { 115 | alarm(opts.duration); 116 | } 117 | 118 | int n = DIRSIZE, count = 0; 119 | memset(dir, 1, DIRSIZE); 120 | while (1) { 121 | if (send(sd, dir, n, 0) == -1) { 122 | perror("send"); 123 | exit(EXIT_FAILURE); 124 | } 125 | count += n; 126 | if (opts.count > 0 && count > opts.count) { 127 | exit(EXIT_SUCCESS); 128 | } 129 | } 130 | 131 | close(sd); 132 | } 133 | -------------------------------------------------------------------------------- /util/server.c: -------------------------------------------------------------------------------- 1 | /* Server code in C */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define DIRSIZE 8192 14 | 15 | typedef struct _options { 16 | // char *hostname; 17 | uint16_t port; 18 | // uint32_t count; 19 | } options_data; 20 | 21 | void print_help(char *progname) 22 | { 23 | printf("Usage: %s [-p port]\n", progname); 24 | } 25 | 26 | void parse_opt(int argc, char *argv[], options_data * opts) 27 | { 28 | int opt; 29 | 30 | opts->port = 5001; //same as iperf 31 | 32 | while ((opt = getopt(argc, argv, "p:")) != -1) { 33 | switch (opt) { 34 | case 'p': 35 | opts->port = atoi(optarg); 36 | break; 37 | default: 38 | case 'h': 39 | print_help(argv[0]); 40 | exit(EXIT_SUCCESS); 41 | } 42 | } 43 | 44 | return; 45 | } 46 | 47 | void my_rec(int rec_socket) 48 | { 49 | char buffer[DIRSIZE]; 50 | for (;;) { 51 | if (recv(rec_socket, buffer, DIRSIZE, 0) < 0) { 52 | perror("recv"); 53 | exit(1); 54 | } 55 | } 56 | } 57 | 58 | int main(int argc, char *argv[]) 59 | { 60 | options_data options; 61 | struct sockaddr_in sockaddr; 62 | int sfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 63 | int cfd; 64 | pid_t pid; 65 | 66 | parse_opt(argc, argv, &options); 67 | 68 | if (-1 == sfd) { 69 | perror("Cannot create socket"); 70 | exit(EXIT_FAILURE); 71 | } 72 | 73 | memset(&sockaddr, 0, sizeof(sockaddr)); 74 | 75 | sockaddr.sin_family = AF_INET; 76 | sockaddr.sin_port = htons(options.port); 77 | sockaddr.sin_addr.s_addr = INADDR_ANY; 78 | 79 | if (-1 == bind(sfd, (const void *)&sockaddr, sizeof(sockaddr))) { 80 | perror("Socket bind failed"); 81 | close(sfd); 82 | exit(EXIT_FAILURE); 83 | } 84 | 85 | if (-1 == listen(sfd, 10)) { 86 | perror("Listen failed"); 87 | close(sfd); 88 | exit(EXIT_FAILURE); 89 | } 90 | 91 | for (;;) { 92 | int cfd = accept(sfd, NULL, NULL); 93 | 94 | if (0 > cfd) { 95 | perror("Accept failed"); 96 | close(sfd); 97 | exit(EXIT_FAILURE); 98 | } 99 | 100 | switch (pid = fork()) { 101 | case 0: //child process 102 | close(sfd); 103 | my_rec(cfd); 104 | exit(0); 105 | break; 106 | case -1: //error 107 | perror("fork"); 108 | close(cfd); 109 | exit(1); 110 | break; 111 | default: //parent process 112 | shutdown(cfd, SHUT_RDWR); 113 | break; 114 | } 115 | 116 | } 117 | 118 | close(cfd); 119 | return 0; 120 | } 121 | --------------------------------------------------------------------------------