/* * Copyright (c) 1995, 1998 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * ber.c: Bit error rate generators for experiments. * The BER generator is called in ipintr(), which means that it is * driven by the receiver and is therefore more realistic than all other * options. This does mean that it is independent of the actual connection * or source-destination pair, which is perhaps not too realistic for * multiple simultaneous base stations but that's a nicety we can add later. * * The underlying math behind the probabilistic models is straightforward * and the algorithms are efficient. The only difference from standard * ways of implementing randomness arises because the kernel doesn't let * you deal with floating point numbers (FPE is turned off in most config * file), so everything has to be simulated using integers. We use a * (large) table lookup to do this (rand.c is a large table of random #s). */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int snoop_err_prob0 = ERR_PROB0; int snoop_err_prob1 = ERR_PROB1; int snoop_ber_model = BER_POISSON; int snoop_trans0 = TRANS_PERC0; int snoop_trans1 = TRANS_PERC1; int snoop_timergran = 100000; struct ber_state ber; static u_int poisson_dist(int); static int two_state_markov(struct ber_state *,struct mbuf *, int, timev); static int poisson_error(struct ber_state *, struct mbuf *, int, timev); extern u_int rands[]; #define NUM_MODELS 2 struct ber_model { char *name; int (*model)(struct ber_state *, struct mbuf *, int, timev); } ber_models[NUM_MODELS] = { {"poisson", poisson_error}, {"bursty", two_state_markov}, }; void ber_init() { int i; char *olds; char *model_name = MODEL; ber.model = snoop_ber_model; #ifdef notdef for (i = 0; i < NUM_MODELS; i++) if (strcmp(ber_models[i].name, model_name) == 0) { ber.model = ber_models[i].model; printf("Error Model: %s\n", model_name); break; } if (ber.model == 0) { printf("ber_init: Unknown error model: %s\n", MODEL); exit(1); } #endif ber.time_granularity = snoop_timergran; ber.markov_state = 0; /* probability of going from 0 to 1 */ ber.trans_perc[0] = snoop_trans0; /* probability of going from 1 to 0 */ ber.trans_perc[1] = snoop_trans1; ber.error_prob[0] = snoop_err_prob0; ber.error_prob[1] = snoop_err_prob1; if (ber.error_prob[1] > 1) ber.error_prob[1] = 1; ber.bytes_to_next_error = poisson_dist(ERR_PROB0); ber.burst_rate = 1; /* default number of pkts to blow away */ ber.protocol = IPPROTO_TCP; /* default is TCP */ ber.port = 80; /* default in http port */ ber.disable = 1; /* disable error generation */ } /* Return a Poisson variate with specified mean */ static u_int poisson_dist(int mean) { int foo; int ret, temp; foo = random() % NUM_RANDS; temp = RAND_MEAN / mean; ret = (rands[foo] / temp); return ret; } /* Poisson error generator. */ int poisson_error(struct ber_state *s, struct mbuf *m, int size, timev now) { static int err_byte = 0; static int first_time = 1; int retval = 0; struct ip *ip = mtod(m, struct ip *); struct tcpiphdr *tcpip_hdr; if (ip->ip_p != s->protocol) return 0; tcpip_hdr = mtod(m, struct tcpiphdr *); while (err_byte < size) { if (first_time) first_time = 0; #if defined(MOBILE_HOST) && defined(PERFECT_ELN) else { if (err_byte < (ip->ip_hl<<2)+(tcpip_hdr->ti_off<<2)) { /* error in IP or TCP header */ ++ip->ip_sum; } else { ++tcpip_hdr->ti_t.th_sum; /* TCP data */ } } #else else ++ip->ip_sum; /* ensure that packet is dropped */ #endif ++retval; err_byte += poisson_dist(s->error_prob[0]); } err_byte -= size; if (err_byte < 0) err_byte = 0; #ifdef DEBUG printf("errbyte %d\n", err_byte); #endif return retval; } /* * This is a two-state Markov process with 2 states (1 & 2) * and transition probabilities, trans_prob1 and trans_prob2, from either * state to the other. In each state, the probability of error * is err_prob, and this is a Poisson variate with the error probability * as its parameter. The idea is that this models (to a large extent) the * error probability of bits on a wireless link since errors usually occur * in bursts, and there is a smaller probability of error in the non-bursty * state too. With suitably chosen values, this can model handoff too, since * handoffs are characterized by error_prob = 1 until the handoff is over * and connection reestablished with some (other) base station. For now, * we use this only to model and study situations where errors are the * dominating effect. */ int two_state_markov(struct ber_state *s, struct mbuf *m, int size, timev now) { int elapsed_time; /* since last call */ static int err_byte = 0; static int first_time = 1; int state_change = 0, temp, retval = 0; struct ip *ip = mtod(m, struct ip *); struct tcpiphdr *tcpip_hdr; if (ip->ip_p != s->protocol) return 0; tcpip_hdr = mtod(m, struct tcpiphdr *); elapsed_time = (now.tv_sec - s->last_call_time.tv_sec) * 1000000 + (now.tv_usec - s->last_call_time.tv_usec); /* do not update the markov state at all if * elapsed_time <= s->time_granularity * next var really should be called s->last_state_update_time */ if (elapsed_time > s->time_granularity) { s->last_call_time = now; } for (; elapsed_time > s->time_granularity; elapsed_time -= s->time_granularity) { if (s->markov_state == 0 && (random() % 100) <= s->trans_perc[0]) { #ifdef DEBUG printf("State change from 0 to 1\n"); #endif state_change = 1; s->markov_state = 1; } else if (s->markov_state == 1 && (random() % 100) <= s->trans_perc[1]) { #ifdef DEBUG printf("State change from 1 to 0\n"); #endif state_change = 1; s->markov_state = 0; } else { /* No transition */ #ifdef DEBUG printf("No state transition.\n"); #endif } } if (state_change) { temp = poisson_dist(s->error_prob[s->markov_state]); if (temp < err_byte) err_byte = temp; } while (err_byte < size) { if (first_time) first_time = 0; else { if (err_byte < (ip->ip_hl << 2)) { ++ip->ip_sum; /* error in IP hdr */ #ifdef DEBUG printf("ip hdr error\n"); #endif ++retval; } else if (err_byte < (ip->ip_hl<<2) + (tcpip_hdr->ti_off<<2)) { #ifdef DEBUG printf("tcp hdr error\n"); #endif /* *(u_char *)(tcpip_hdr + s->bytes_to_next_error) { (unsigned char) (random() % 256); */ ++tcpip_hdr->ti_t.th_sum; ++retval; } else { #ifdef DEBUG printf("tcp data error\n"); #endif ++tcpip_hdr->ti_t.th_sum; /* TCP data */ ++retval; } } err_byte += poisson_dist(s->error_prob[s->markov_state]); } err_byte -= size; if (err_byte < 0) err_byte = 0; #ifdef DEBUG printf("errbyte %d\n", err_byte); #endif return retval; } /* all error models should be as follows: * * int error_func(ber_state *s, struct mbuf *m, int size) * int tcphdr_size, timev now, char errortype) * size = number of bytes to process incl. ip and tcp headers * return value = int, number of errors (most of the time XXX) */ int wireless_drop(struct mbuf *m, int size) { timev now; static int burst_pending = 0; struct ip *ip = mtod(m, struct ip *); struct tcpiphdr *ti = mtod(m, struct tcpiphdr *); microtime(&now); if (!ber.disable) if (!burst_pending) { if (ber_models[ber.model].model(&ber, m, size, now)) { burst_pending = ber.burst_rate - 1; return 1; } } else { ++ip->ip_sum; --burst_pending; return 1; } return 0; }