diff --git a/src/config.c b/src/config.c index 2d56411..7dc6122 100644 --- a/src/config.c +++ b/src/config.c @@ -22,6 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include "includes.h" #include "mlvpn.h" @@ -36,6 +37,11 @@ extern struct mlvpn_filters_s mlvpn_filters; extern struct tuntap_s tuntap; extern struct mlvpn_reorder_buffer *reorder_buffer; +char *ip_from_if(char *ifname); +// we'll declair this here, so that any device name used instead of an IP +// address gets translated before we go anywhere else... + + /* Config file reading / re-read. * config_file_fd: fd opened in priv_open_config * first_time: set to 0 for re-read, or 1 for initial configuration @@ -273,6 +279,7 @@ mlvpn_config(int config_file_fd, int first_time) char *dstaddr; char *dstport; uint32_t bwlimit = 0; + uint32_t quota = 0; uint32_t timeout = 30; uint32_t loss_tolerence; int create_tunnel = 1; @@ -311,9 +318,16 @@ mlvpn_config(int config_file_fd, int first_time) config, lastSection, "remoteport", &dstport, NULL, "No remote port specified.\n", 1); } + + bindaddr=ip_from_if(bindaddr); + + _conf_set_uint_from_conf( config, lastSection, "bandwidth_upload", &bwlimit, 0, NULL, 0); + _conf_set_uint_from_conf( + config, lastSection, "quota", "a, 0, + NULL, 0); _conf_set_uint_from_conf( config, lastSection, "timeout", &timeout, default_timeout, NULL, 0); @@ -372,10 +386,16 @@ mlvpn_config(int config_file_fd, int first_time) } if (tmptun->bandwidth != bwlimit) { - log_info("config", "%s bandwidth changed from %d to %d", + log_info("config", "%s bandwidth changed from %d to %d", tmptun->name, tmptun->bandwidth, bwlimit); tmptun->bandwidth = bwlimit; } + if (tmptun->quota != quota) + { + log_info("config", "%s quota changed from %d to %d", + tmptun->name, tmptun->quota, quota); + tmptun->quota = quota; + } if (tmptun->loss_tolerence != loss_tolerence) { log_info("config", "%s loss tolerence changed from %d%% to %d%%", @@ -393,7 +413,7 @@ mlvpn_config(int config_file_fd, int first_time) mlvpn_rtun_new( lastSection, bindaddr, bindport, bindfib, dstaddr, dstport, default_server_mode, timeout, fallback_only, - bwlimit, loss_tolerence); + bwlimit, loss_tolerence, quota); } if (bindaddr) free(bindaddr); @@ -486,3 +506,43 @@ mlvpn_config(int config_file_fd, int first_time) log_warnx("config", "parse error"); return 1; } + + +/* This is a filter function, it takes an name, if the name turns out to be an + * interface, it translates it to it's IP address, + * the resulting filtered name is returned (whether it has matched an interface + * or not */ +char *ip_from_if(char *ifname) +{ + + struct ifaddrs *ifaddr, *ifa; + int s; + char host[NI_MAXHOST]; + + if (getifaddrs(&ifaddr) == -1) + { + log_warn(NULL, "unable to collect ifaddrs"); + return ifname; + } + + + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) + { + if (ifa->ifa_addr == NULL) + continue; + + s=getnameinfo(ifa->ifa_addr,sizeof(struct sockaddr_in),host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); + + if((strcmp(ifa->ifa_name,ifname)==0)&&(ifa->ifa_addr->sa_family==AF_INET)) + { + if (s == 0) + { + if (ifname) free(ifname); + ifname = strdup(host); + } + } + } + + freeifaddrs(ifaddr); + return ifname; +} diff --git a/src/control.c b/src/control.c index 7f53098..d05cf90 100644 --- a/src/control.c +++ b/src/control.c @@ -68,9 +68,11 @@ void mlvpn_control_write_status(struct mlvpn_control *ctrl); " \"bandwidth\": %u,\n" \ " \"srtt\": %u,\n" \ " \"loss\": %u,\n" \ + " \"permitted\": %u,\n" \ " \"disconnects\": %u,\n" \ " \"last_packet\": %u,\n" \ - " \"timeout\": %u\n" \ + " \"timeout\": %u,\n" \ + " \"weight\": %.3f\n" \ "}%s\n" #define JSON_STATUS_ERROR_UNKNOWN_COMMAND "{\"error\": 'unknown command'}\n" @@ -428,9 +430,11 @@ void mlvpn_control_write_status(struct mlvpn_control *ctrl) 0, (uint32_t)t->srtt, mlvpn_loss_ratio(t), + (uint32_t)(t->permitted/1000000), t->disconnects, (uint32_t)t->last_activity, (uint32_t)t->timeout, + t->weight, (LIST_NEXT(t, entries) ? "," : "") ); mlvpn_control_write(ctrl, buf, ret); diff --git a/src/mlvpn.c b/src/mlvpn.c index 7a2a332..666e7f9 100644 --- a/src/mlvpn.c +++ b/src/mlvpn.c @@ -87,6 +87,10 @@ char *process_title = NULL; int logdebug = 0; static uint64_t data_seq = 0; +ev_tstamp lastsent=0; +uint64_t bandwidthdata=0; +double bandwidth=0; +uint64_t permitted_preset=0; struct mlvpn_status_s mlvpn_status = { .start_time = 0, @@ -126,7 +130,7 @@ struct mlvpn_filters_s mlvpn_filters = { struct mlvpn_reorder_buffer *reorder_buffer; freebuffer_t *freebuf; -static char *optstr = "c:n:u:hvVD:"; +static char *optstr = "c:n:u:hvVD:p:"; static struct option long_options[] = { {"config", required_argument, 0, 'c' }, {"debug", no_argument, 0, 2 }, @@ -138,6 +142,7 @@ static struct option long_options[] = { {"quiet", no_argument, 0, 'q' }, {"version", no_argument, 0, 'V' }, {"yes-run-as-root",no_argument, 0, 3 }, + {"permitted", required_argument, 0, 'p' }, {0, 0, 0, 0 } }; @@ -181,6 +186,7 @@ usage(char **argv) " -v --verbose increase verbosity\n" " -q --quiet decrease verbosity\n" " -V, --version output version information and exit\n" + " -p, --permitted Set all tunnels with a quota to this value of permitted bytes\n" "\n" "For more details see mlvpn(1) and mlvpn.conf(5).\n", argv[0]); exit(2); @@ -226,7 +232,9 @@ static void mlvpn_rtun_reorder_drain_timeout(EV_P_ ev_timer *w, int revents) { log_debug("reorder", "reorder timeout. Packet loss?"); - mlvpn_rtun_reorder_drain(0); +// printf("Reorder timeout\n"); + mlvpn_reorder_skip(reorder_buffer); + mlvpn_rtun_reorder_drain(1); // MARK = 1, old = 0 if (freebuf->used == 0) { ev_timer_stop(EV_A_ w); } @@ -265,32 +273,46 @@ mlvpn_rtun_reorder_drain(uint32_t reorder) static void mlvpn_loss_update(mlvpn_tunnel_t *tun, uint64_t seq) { - if (seq > tun->seq_last + 64) { - /* consider a connection reset. */ - tun->seq_vect = (uint64_t) -1; - tun->seq_last = seq; - } else if (seq > tun->seq_last) { - /* new sequence number -- recent message arrive */ - tun->seq_vect <<= seq - tun->seq_last; - tun->seq_vect |= 1; - tun->seq_last = seq; - } else if (seq >= tun->seq_last - 63) { - tun->seq_vect |= (1 << (tun->seq_last - seq)); +// If a tunnel moves forward, leaving a 'hole' - then we GUESS the hole is for +// the other tunnel, and if it's not filled in, will be a loss marked for the +// other tunnel. + + mlvpn_tunnel_t *t; + LIST_FOREACH(t, &rtuns, entries) { + + if (seq > t->seq_last + 64) { + /* consider a connection reset. */ + t->seq_vect = (uint64_t) -1; + t->seq_last = seq; + } else if (seq > t->seq_last) { + /* new sequence number -- recent message arrive */ + t->seq_vect <<= seq - t->seq_last; + if (t==tun) { + t->seq_vect |= ~((uint64_t)-1<<(seq - t->seq_last)); + // If I move it forward, I claim all the other holes are somebody elses + // problem to fill in...., so not my error ! + } else { + t->seq_vect |= 1; + } + t->seq_last = seq; + } else if (seq >= t->seq_last - 63) { + t->seq_vect |= (1 << (t->seq_last - seq)); } + } } int mlvpn_loss_ratio(mlvpn_tunnel_t *tun) { int loss = 0; - int i; + unsigned int i; /* Count zeroes */ for (i = 0; i < 64; i++) { - if (! (1 & (tun->seq_vect >> i))) { - loss++; - } + if ( (1 & (tun->seq_vect >> i)) == 0 ) { + loss++; + } } - return loss * 100 / 64; + return (loss * 100) / 64; } static int @@ -363,6 +385,9 @@ mlvpn_rtun_read(EV_P_ ev_io *w, int revents) tun->recvbytes += len; tun->recvpackets += 1; + if (tun->quota) { + tun->permitted -= len; + } if (! tun->addrinfo) fatalx("tun->addrinfo is NULL!"); @@ -448,6 +473,7 @@ mlvpn_protocol_read( log_warnx("protocol", "%s invalid packet size: %d", tun->name, rlen); goto fail; } + proto.seq = be64toh(proto.seq); proto.timestamp = be16toh(proto.timestamp); proto.timestamp_reply = be16toh(proto.timestamp_reply); @@ -518,8 +544,8 @@ mlvpn_rtun_send(mlvpn_tunnel_t *tun, circular_buffer_t *pktbuf) mlvpn_proto_t proto; uint64_t now64 = mlvpn_timestamp64(ev_now(EV_DEFAULT_UC)); memset(&proto, 0, sizeof(proto)); - mlvpn_pkt_t *pkt = mlvpn_pktbuffer_read(pktbuf); + pkt->reorder = 1; if (pkt->type == MLVPN_PKT_DATA && pkt->reorder) { proto.data_seq = data_seq++; @@ -535,15 +561,22 @@ mlvpn_rtun_send(mlvpn_tunnel_t *tun, circular_buffer_t *pktbuf) proto.reorder = pkt->reorder; /* we have a recent received timestamp */ - if (now64 - tun->saved_timestamp_received_at < 1000 ) { + if (tun->saved_timestamp != -1) { + if (now64 - tun->saved_timestamp_received_at < 1000 ) { /* send "corrected" timestamp advanced by how long we held it */ /* Cast to uint16_t there intentional */ proto.timestamp_reply = tun->saved_timestamp + (now64 - tun->saved_timestamp_received_at); tun->saved_timestamp = -1; tun->saved_timestamp_received_at = 0; - } else { + } else { proto.timestamp_reply = -1; + log_debug("rtt","(%s) No timestamp added, time too long! (%lu > 1000)",tun->name, tun->saved_timestamp + (now64 - tun->saved_timestamp_received_at )); + } + } else { + proto.timestamp_reply = -1; + log_debug("rtt","(%s) No timestamp added, time too long! (%lu > 1000)",tun->name, tun->saved_timestamp + (now64 - tun->saved_timestamp_received_at )); } + proto.timestamp = mlvpn_timestamp16(now64); #ifdef ENABLE_CRYPTO if (mlvpn_options.cleartext_data && pkt->type == MLVPN_PKT_DATA) { @@ -590,6 +623,10 @@ mlvpn_rtun_send(mlvpn_tunnel_t *tun, circular_buffer_t *pktbuf) } else { tun->sentpackets++; tun->sentbytes += ret; + if (tun->quota) { + tun->permitted -= ret; + } + if (wlen != ret) { log_warnx("net", "%s write error %d/%u", @@ -626,7 +663,7 @@ mlvpn_rtun_new(const char *name, const char *destaddr, const char *destport, int server_mode, uint32_t timeout, int fallback_only, uint32_t bandwidth, - uint32_t loss_tolerence) + uint32_t loss_tolerence, uint32_t quota) { mlvpn_tunnel_t *new; @@ -661,6 +698,12 @@ mlvpn_rtun_new(const char *name, new->sentpackets = 0; new->sentbytes = 0; new->recvbytes = 0; + if (quota) { + new->permitted = permitted_preset; + } else { + new->permitted = 0; + } + new->quota = quota; new->seq = 0; new->expected_receiver_seq = 0; new->saved_timestamp = -1; @@ -729,42 +772,117 @@ mlvpn_rtun_drop(mlvpn_tunnel_t *t) update_process_title(); } + +static void +mlvpn_rtun_recalc_weight_srtt() +{ + mlvpn_tunnel_t *t; + double totalsrtt=0; + + LIST_FOREACH(t, &rtuns, entries) + { + totalsrtt+=t->srtt; + } + double totalf=0; + + LIST_FOREACH(t, &rtuns, entries) + { + if (t->srtt > 0) { + totalf += totalsrtt / t->srtt; + } + } + LIST_FOREACH(t, &rtuns, entries) + { + double st=t->srtt; + if (st > 0) { + // should be 1 / (t->srtt / totalsrtt) + // e.g. (1 / (srtt / totalsrtt)) * (100 / totalf) + mlvpn_rtun_set_weight(t, ((totalsrtt * 100) / (st * totalf))); + if (t->weight < 1) mlvpn_rtun_set_weight(t,1); + if (t->weight > 100) mlvpn_rtun_set_weight(t,100); + log_debug("wrr", "%s weight = %f%%", t->name, t->weight); + } + } +} + /* Based on tunnel bandwidth, compute a "weight" value * to balance correctly the round robin rtun_choose. */ static void -mlvpn_rtun_recalc_weight() +mlvpn_rtun_recalc_weight_bw() { - mlvpn_tunnel_t *t; - uint32_t bandwidth_total = 0; - int warned = 0; - /* If the bandwidth limit is not set on all interfaces, then - * it's impossible to balance correctly! */ + mlvpn_tunnel_t *t; + int unset=0; + uint32_t bandwidth_total = 0; + + LIST_FOREACH(t, &rtuns, entries) + { + if (t->bandwidth == 0) + unset++; + bandwidth_total += t->bandwidth; + } + if (unset) { + return mlvpn_rtun_recalc_weight_srtt(); + } else { LIST_FOREACH(t, &rtuns, entries) { - if (t->bandwidth == 0) - warned++; - bandwidth_total += t->bandwidth; + /* useless, but we want to be sure not to divide by 0 ! */ + if (t->bandwidth > 0 && bandwidth_total > 0) + { + mlvpn_rtun_set_weight(t, (((double)t->bandwidth / + (double)bandwidth_total) * 100.0)); + log_debug("wrr", "%s weight = %f (%u %u)", t->name, t->weight, + t->bandwidth, bandwidth_total); + } } - if (warned && bandwidth_total > 0) { - log_warnx("config", "you must set the bandwidth on every tunnel"); + } +} + + +/* Based on tunnel bandwidth, with priority compute a "weight" value + * to balance correctly the round robin rtun_choose. + */ +static void +mlvpn_rtun_recalc_weight_prio() +{ + if (bandwidth<=0) { + return mlvpn_rtun_recalc_weight_bw(); + } + mlvpn_tunnel_t *t; + double bwneeded=bandwidth*1.5; + double bw=bwneeded; + LIST_FOREACH(t, &rtuns, entries) { + if ((t->quota == 0) && (t->status >= MLVPN_AUTHOK)) { + mlvpn_rtun_set_weight(t, (t->bandwidth*80) / bwneeded); + bw-=(t->bandwidth*0.8); + } else { + if (bw>0 && (t->quota==0 || t->permitted > (t->bandwidth*3)) && (t->status >= MLVPN_AUTHOK)) { + if (t->bandwidth*0.8 > bw) { + mlvpn_rtun_set_weight(t, (bw*100) / bwneeded); + } else { + mlvpn_rtun_set_weight(t, (t->bandwidth*80) / bwneeded); + } + bw-=(t->bandwidth*0.8); + } else { + mlvpn_rtun_set_weight(t, 0); } - if (warned == 0) - { - LIST_FOREACH(t, &rtuns, entries) - { - /* useless, but we want to be sure not to divide by 0 ! */ - if (t->bandwidth > 0 && bandwidth_total > 0) - { - t->weight = (((double)t->bandwidth / - (double)bandwidth_total) * 100.0); - log_debug("wrr", "%s weight = %f (%u %u)", t->name, t->weight, - t->bandwidth, bandwidth_total); - } - } } + + } + if (bw==bwneeded) { + return mlvpn_rtun_recalc_weight_bw(); + } } + + +static void +mlvpn_rtun_recalc_weight() +{ + mlvpn_rtun_recalc_weight_prio(); +} + + static int mlvpn_rtun_bind(mlvpn_tunnel_t *t) { @@ -1127,12 +1245,39 @@ mlvpn_rtun_tick_connect(mlvpn_tunnel_t *t) } } +void mlvpn_calc_bandwidth(uint32_t len) +{ + ev_tstamp now=ev_now(EV_A); + if (lastsent==0) lastsent=now; + ev_tstamp diff=now - lastsent; + bandwidthdata+=len; + if (diff>3.0) { + lastsent=now; + bandwidth=((((double)bandwidthdata*8) / diff))/1000; // kbits/sec +// printf("%10.1f %lu %5.2f\n",bandwidth, bandwidthdata, diff); + bandwidthdata=0; + + // what we can do here is add any bandwidth allocation + // The allocation should be per second. + // permittedis in bytes. + mlvpn_tunnel_t *t; + LIST_FOREACH(t, &rtuns, entries) { + // permitted is in BYTES per second. + if (t->quota) { + t->permitted+=(((double)t->quota * diff)*1000.0)/8.0; // listed in kbps + } + } + mlvpn_rtun_recalc_weight(); + } +} + mlvpn_tunnel_t * -mlvpn_rtun_choose() +mlvpn_rtun_choose(uint32_t len) { - mlvpn_tunnel_t *tun; - tun = mlvpn_rtun_wrr_choose(); - return tun; + mlvpn_calc_bandwidth(len); + mlvpn_tunnel_t *tun; + tun = mlvpn_rtun_wrr_choose(len); + return tun; } static void @@ -1433,6 +1578,10 @@ main(int argc, char **argv) case 'q': /* --quiet */ mlvpn_options.verbose--; break; + case 'p': /* preset the current 'permitted' values (for all tunnels with + * a quota, which is a pritty rubish plan*/ + permitted_preset=atoll(optarg); + break; case 'h': /* --help */ default: usage(argv); diff --git a/src/mlvpn.h b/src/mlvpn.h index 0026dfa..d1490a5 100644 --- a/src/mlvpn.h +++ b/src/mlvpn.h @@ -162,6 +162,8 @@ typedef struct mlvpn_tunnel_s uint64_t recvpackets; /* 64bit packets recv counter */ uint64_t sentbytes; /* 64bit bytes sent counter */ uint64_t recvbytes; /* 64bit bytes recv counter */ + int64_t permitted; /* how many bytes we can send */ + uint32_t quota; /* how many bytes per second we can send */ uint32_t timeout; /* configured timeout in seconds */ uint32_t bandwidth; /* bandwidth in bytes per second */ circular_buffer_t *sbuf; /* send buffer */ @@ -191,14 +193,15 @@ int mlvpn_sock_set_nonblocking(int fd); int mlvpn_loss_ratio(mlvpn_tunnel_t *tun); int mlvpn_rtun_wrr_reset(struct rtunhead *head, int use_fallbacks); +void mlvpn_rtun_set_weight(mlvpn_tunnel_t *t, double weight); mlvpn_tunnel_t *mlvpn_rtun_wrr_choose(); -mlvpn_tunnel_t *mlvpn_rtun_choose(); +mlvpn_tunnel_t *mlvpn_rtun_choose(uint32_t len); mlvpn_tunnel_t *mlvpn_rtun_new(const char *name, const char *bindaddr, const char *bindport, uint32_t bindfib, const char *destaddr, const char *destport, int server_mode, uint32_t timeout, int fallback_only, uint32_t bandwidth, - uint32_t loss_tolerence); + uint32_t loss_tolerence, uint32_t quota); void mlvpn_rtun_drop(mlvpn_tunnel_t *t); void mlvpn_rtun_status_down(mlvpn_tunnel_t *t); #ifdef HAVE_FILTERS diff --git a/src/privsep.c b/src/privsep.c index 8005909..9907d06 100644 --- a/src/privsep.c +++ b/src/privsep.c @@ -159,7 +159,7 @@ priv_init(char *argv[], char *username) if (is_root && pw) { if (chroot(pw->pw_dir) != 0) - err(1, "unable to chroot"); + err(1, "unable to chroot..."); } /* May be usefull to chose chdir directory ? */ diff --git a/src/reorder.c b/src/reorder.c index adf3164..2fedb75 100644 --- a/src/reorder.c +++ b/src/reorder.c @@ -39,6 +39,8 @@ #include "reorder.h" #include "log.h" +#define MARK + /* A generic circular buffer */ struct cir_buffer { unsigned int size; /**< Number of pkts that can be stored */ @@ -48,6 +50,16 @@ struct cir_buffer { mlvpn_pkt_t **pkts; }; +#ifdef MARK + +struct pktlist +{ + mlvpn_pkt_t *pkt; + struct pktlist *last; + struct pktlist *next; +}; +#endif + /* The reorder buffer data structure itself */ struct mlvpn_reorder_buffer { uint64_t min_seqn; /**< Lowest seq. number that can be in the buffer */ @@ -55,8 +67,238 @@ struct mlvpn_reorder_buffer { struct cir_buffer ready_buf; /**< temp buffer for dequeued pkts */ struct cir_buffer order_buf; /**< buffer used to reorder pkts */ int is_initialized; + +#ifdef MARK + struct pktlist *pool; + struct pktlist *list; + struct pktlist *tail; + int list_size; + int list_size_av; + int max_size; +#endif }; + +#ifdef MARK + +struct mlvpn_reorder_buffer * +mlvpn_reorder_init(struct mlvpn_reorder_buffer *b, unsigned int bufsize, + unsigned int size) +{ + b->max_size=10; + b->pool=NULL; + b->list=NULL; + b->tail=NULL; + b->list_size=0; + b->list_size_av=10; + b->is_initialized = 0; + + return b; +} +struct mlvpn_reorder_buffer* +mlvpn_reorder_create(unsigned int size) +{ + struct mlvpn_reorder_buffer *b = malloc(sizeof(struct mlvpn_reorder_buffer)); + mlvpn_reorder_init(b, 0, size); + return b; +} +void +mlvpn_reorder_reset(struct mlvpn_reorder_buffer *b) +{ + if (b->tail) { + b->tail->next=b->pool; + b->pool=b->list; + } + b->list=NULL; + b->tail=NULL; + b->list_size=0; +} +void mlvpn_reorder_free(struct mlvpn_reorder_buffer *b) +{ + struct pktlist *l,*n; + for (l=b->list;l;l=n) {n=l->next;free(l);} + for (l=b->pool;l;l=n) {n=l->next;free(l);} + free(b); +} + +int +mlvpn_reorder_insert(struct mlvpn_reorder_buffer *b, mlvpn_pkt_t *pkt) +{ + struct pktlist *p; + if (b->pool) { + p=b->pool; + b->pool=b->pool->next; + } else { + p=malloc(sizeof (struct pktlist)); + } + p->pkt=pkt; + + if (!b->is_initialized) { + b->min_seqn = pkt->seq; + b->is_initialized = 1; + log_debug("reorder", "initial sequence: %"PRIu64"", pkt->seq); + } + + + /* + * calculate the offset from the head pointer we need to go. + * The subtraction takes care of the sequence number wrapping. + * For example (using 16-bit for brevity): + * min_seqn = 0xFFFD + * pkt_seq = 0x0010 + * offset = 0x0010 - 0xFFFD = 0x13 + * Then we cast to a signed int, if the subtraction ends up in a large + * number, that will be seen as negative when casted.... + */ + struct pktlist *l; + for (l=b->list;l && ((int64_t)(pkt->seq - l->pkt->seq)<=0);l=l->next){} + if (!l) { + if (b->tail) { + p->last=b->tail; + b->tail->next=p; + b->tail=p; + p->next=NULL; + } else { + p->last=NULL; + p->next=NULL; + b->list=p; + b->tail=p; + } + } else { + p->last=l->last; + if (p->last) { + p->last->next=p; + } else { + b->list=p; + } + p->next=l; + l->last=p; + } + + b->list_size++; +// printf("Insert %lu list size %d min %lu\n",pkt->seq, b->list_size, b->min_seqn); + + if (b->tail && ((int64_t)(b->min_seqn - b->tail->pkt->seq) > 0)) { + log_debug("reorder", "got old (insert) consider increasing buffer (%d behind)\n",(int)(b->min_seqn - b->tail->pkt->seq)); + } + + return 0; +} + +void mlvpn_reorder_skip(struct mlvpn_reorder_buffer *b) +{ +// printf("number in list %u list_size_av %u min %lu tail %lu\n",b->list_size, b->list_size_av, b->min_seqn, b->tail->pkt->seq); + if (b->tail) { + b->min_seqn=b->tail->pkt->seq; // Jump over any hole !!!! + } +} + + + +unsigned int +mlvpn_reorder_drain(struct mlvpn_reorder_buffer *b, mlvpn_pkt_t **pkts, + unsigned max_pkts) +{ + + unsigned int drain_cnt = 0; + +// offset = pkt->seq - b->min_seqn; + +// printf("min_seqn %lu, top %lu\n",b->min_seqn, b->list->pkt->seq); + +/* uint64_t old=0,new=0; + mlvpn_tunnel_t *t; + old=rtuns.lh_first->seq; + new=rtuns.lh_first->seq; + LIST_FOREACH(t, &rtuns, entries) { + if ((int64_t)(t->seq - old) > 0) old=t->seq; + if ((int64_t)(new - t->seq) > 0) new=t->seq; + } +// No point being behind the oldest tunnel ! + if (b->list && ((int64_t)(old - b->min_seqn) > 0)) { + printf("Skipping (no old tunnels) %d\n",(int)(old - b->min_seqn)); + b->min_seqn=b->list->pkt->seq; + } + + +// no point being older than the newest thing in - max_size + if (b->list && ((int64_t)(new - b->min_seqn) > b->max_size)) { + printf("Skipping (newest is too far ahead) %d\n",(int)(old - b->min_seqn)); + b->min_seqn=b->list->pkt->seq; + } +*/ + +/* + if (b->list && ((int64_t)(b->list->pkt->seq - b->min_seqn) > b->max_size)) { + printf("Skipping (whole) %d\n",(int)(b->list->pkt->seq - b->min_seqn)); + b->min_seqn=b->list->pkt->seq; // we have a whole, skip over !!! + } else +*/ +/* if (b->list && ((int64_t)(b->min_seqn - b->list->pkt->seq) > 0)) { + printf("got old (drain) %d\n",(int)(b->min_seqn - b->list->pkt->seq)); + b->min_seqn=b->list->pkt->seq; // we have a whole, skip over !!! + } +*/ + + while (b->tail && ((b->list_size>((b->list_size_av*2))) || ((int64_t)(b->min_seqn - b->tail->pkt->seq)>=0)) && (drain_cnt < max_pkts)) { + struct pktlist *l=b->tail; + pkts[drain_cnt++]=l->pkt; + if (l->last) { + b->tail=l->last; + b->tail->next=NULL; + } else { + b->list=NULL; + b->tail=NULL; + } + l->next=b->pool; + b->pool=l; + b->list_size--; + + b->min_seqn=l->pkt->seq+1; + +// do we need to do this again here? +/* if (b->list && ((int64_t)(b->list->pkt->seq - b->min_seqn) > b->max_size)) { + printf("Skipping (hole - b) %d\n",(int)(b->list->pkt->seq - b->min_seqn)); + b->min_seqn=b->list->pkt->seq; // we have a whole, skip over !!! + } +*/ + + } + if (drain_cnt > 1) { + int last=b->list_size_av; + b->list_size_av = ((b->list_size_av*9) + (b->list_size + drain_cnt) + 5)/10; + if (b->list_size_av > 64) { + b->list_size_av = 64; + if (b->list_size_av != last ) { + log_info("reorder", "List size reached limit (64)\n"); + } + } + if (b->list_size_av < 4) { + b->list_size_av = 4; + if (b->list_size_av != last ) { + log_debug("reorder", "List size reached limit (4)\n"); + } + } +// if (b->list_size_av != last ) { +// log_debug("reorder", "Changed in list_size_av %d\n",b->list_size_av); +// printf("reorder: Changed in list_size_av %d\n",b->list_size_av); +// } + } + +// if (b->tail) b->min_seqn++; + +// printf("Drain %d %lu list size %d min %lu\n",drain_cnt, pkts[0]->seq, b->list_size, b->min_seqn); + return drain_cnt; +} + +#else + + +// OLD CODE.... +void mlvpn_reorder_skip(struct mlvpn_reorder_buffer *b) +{ +} + struct mlvpn_reorder_buffer * mlvpn_reorder_init(struct mlvpn_reorder_buffer *b, unsigned int bufsize, unsigned int size) @@ -256,3 +498,4 @@ mlvpn_reorder_drain(struct mlvpn_reorder_buffer *b, mlvpn_pkt_t **pkts, } return drain_cnt; } +#endif diff --git a/src/reorder.h b/src/reorder.h index 8e048fd..c5a5ecf 100644 --- a/src/reorder.h +++ b/src/reorder.h @@ -150,4 +150,10 @@ unsigned int mlvpn_reorder_drain(struct mlvpn_reorder_buffer *b, mlvpn_pkt_t **pkts, unsigned max_pkts); + +/* skip over any holes in the buffer to try and force more data to be available + * to drain */ +void mlvpn_reorder_skip(struct mlvpn_reorder_buffer *b); + + #endif /* MLVPN_REORDER_H */ diff --git a/src/tuntap_generic.c b/src/tuntap_generic.c index 1141c35..c2f72ff 100644 --- a/src/tuntap_generic.c +++ b/src/tuntap_generic.c @@ -15,7 +15,7 @@ mlvpn_tuntap_generic_read(u_char *data, uint32_t len) } #endif if (!rtun) { - rtun = mlvpn_rtun_choose(); + rtun = mlvpn_rtun_choose(len); /* Not connected to anyone. read and discard packet. */ if (! rtun) return len; diff --git a/src/tuntap_linux.c b/src/tuntap_linux.c index f46314a..bea2d43 100644 --- a/src/tuntap_linux.c +++ b/src/tuntap_linux.c @@ -16,15 +16,16 @@ mlvpn_tuntap_read(struct tuntap_s *tuntap) { ssize_t ret; u_char data[DEFAULT_MTU]; - ret = read(tuntap->fd, &data, tuntap->maxmtu); + + ret = read(tuntap->fd, &data, DEFAULT_MTU); + + if (ret<0 && (errno==EAGAIN || errno==EWOULDBLOCK)) { + return -1; + } + if (ret < 0) { - if (errno != EAGAIN && errno != EWOULDBLOCK) { - /* read error on tuntap is not recoverable. We must die. */ - fatal("tuntap", "unrecoverable read error"); - } else { - /* false reading from libev read would block, we can't read */ - return 0; - } + /* read error on tuntap is not recoverable. We must die. */ + fatal("tuntap", "unrecoverable read error"); } else if (ret == 0) { /* End of file */ fatalx("tuntap device closed"); } else if (ret > tuntap->maxmtu) { @@ -71,7 +72,7 @@ mlvpn_tuntap_alloc(struct tuntap_s *tuntap) int fd; if ((fd = priv_open_tun(tuntap->type, - tuntap->devname, tuntap->maxmtu)) <= 0 ) + tuntap->devname, tuntap->maxmtu)) <= 0 ) fatalx("failed to open /dev/net/tun read/write"); tuntap->fd = fd; return fd; @@ -119,6 +120,9 @@ root_tuntap_open(int tuntapmode, char *devname, int mtu) return -1; } + int flags = fcntl(fd, F_GETFL, 0); + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + /* set tun MTU */ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { warn("socket creation failed"); diff --git a/src/wrr.c b/src/wrr.c index 8e0cb6e..f5d0113 100644 --- a/src/wrr.c +++ b/src/wrr.c @@ -17,13 +17,14 @@ static struct mlvpn_wrr wrr = { static int wrr_min_index() { - double min = 100.0; int min_index = 0; int i; + double min = wrr.tunval[0]; for(i = 0; i < wrr.len; i++) { - if (wrr.tunval[i] < min) + if ((wrr.tunval[i] < min) && + (wrr.tunnel[i]->quota==0 || wrr.tunnel[i]->permitted>0)) { min = wrr.tunval[i]; min_index = i; @@ -57,24 +58,38 @@ int mlvpn_rtun_wrr_reset(struct rtunhead *head, int use_fallbacks) return 0; } +void mlvpn_rtun_set_weight(mlvpn_tunnel_t *t, double weight) +{ + if (t->weight!=weight) { + t->weight=weight; + for (int i = 0; i< wrr.len; i++) { + wrr.tunval[i] = 0.0; + } + } +} + mlvpn_tunnel_t * mlvpn_rtun_wrr_choose() { - int i; - int idx; - if (wrr.len == 0) - return NULL; - - idx = wrr_min_index(); - if (idx < 0) - fatalx("Programming error: wrr_min_index < 0!"); - - for(i = 0; i < wrr.len; i++) - { - if (wrr.tunval[i] > 0) - wrr.tunval[i] -= 1; + int idx = wrr_min_index(); + + double total=0; + for (int i = 0; i< wrr.len; i++) { + total+= wrr.tunnel[i]->weight; + } + + if (wrr.tunval[idx]<=0 || wrr.tunval[idx] > 10000) { + for (int i = 0; i< wrr.len; i++) { + if (wrr.tunnel[i]->weight) { + wrr.tunval[i]=total / wrr.tunnel[i]->weight; + } else { + wrr.tunval[i]=wrr.len; // handle initial setup fairly + } } - wrr.tunval[idx] = (double) 100.0 / wrr.tunnel[idx]->weight; - return wrr.tunnel[idx]; + } else { + wrr.tunval[idx]+=total / wrr.tunnel[idx]->weight; + } + + return wrr.tunnel[idx]; }