www.pudn.com > dsr-uu-0.2.rar > dsr-rreq.c


/* Copyright (C) Uppsala University
 *
 * This file is distributed under the terms of the GNU general Public
 * License (GPL), see the file LICENSE
 *
 * Author: Erik Nordström, 
 */
#ifdef __KERNEL__
#include 
#include 
#include 
#include 

#include "dsr-dev.h"
#endif

#ifdef NS2
#include "ns-agent.h"
#endif

#include "debug.h"
#include "dsr.h"
#include "tbl.h"
#include "dsr-rrep.h"
#include "dsr-rreq.h"
#include "dsr-opt.h"
#include "link-cache.h"
#include "send-buf.h"
#include "neigh.h"

#ifndef NS2

#define RREQ_TBL_PROC_NAME "dsr_rreq_tbl"

static TBL(rreq_tbl, RREQ_TBL_MAX_LEN);
static unsigned int rreq_seqno;
#endif

#ifndef MAXTTL
#define MAXTTL 255
#endif

#define STATE_IDLE          0
#define STATE_IN_ROUTE_DISC 1

struct rreq_tbl_entry {
	list_t l;
	int state;
	struct in_addr node_addr;
	int ttl;
	DSRUUTimer *timer;
	struct timeval tx_time;
	struct timeval last_used;
	usecs_t timeout;
	unsigned int num_rexmts;
	struct tbl rreq_id_tbl;
};

struct id_entry {
	list_t l;
	struct in_addr trg_addr;
	unsigned short id;
};
struct rreq_tbl_query {
	struct in_addr *initiator;
	struct in_addr *target;
	unsigned int *id;
};

static inline int crit_addr(void *pos, void *data)
{
	struct rreq_tbl_entry *e = (struct rreq_tbl_entry *)pos;
	struct in_addr *a = (struct in_addr *)data;

	if (e->node_addr.s_addr == a->s_addr)
		return 1;
	return 0;
}

static inline int crit_duplicate(void *pos, void *data)
{
	struct rreq_tbl_entry *e = (struct rreq_tbl_entry *)pos;
	struct rreq_tbl_query *q = (struct rreq_tbl_query *)data;

	if (e->node_addr.s_addr == q->initiator->s_addr) {
		list_t *p;

		list_for_each(p, &e->rreq_id_tbl.head) {
			struct id_entry *id_e = (struct id_entry *)p;

			if (id_e->trg_addr.s_addr == q->target->s_addr &&
			    id_e->id == *(q->id))
				return 1;
		}
	}
	return 0;
}

void NSCLASS rreq_tbl_set_max_len(unsigned int max_len)
{
	rreq_tbl.max_len = max_len;
}
#ifdef __KERNEL__
static int rreq_tbl_print(struct tbl *t, char *buf)
{
	list_t *pos1, *pos2;
	int len = 0;
	int first = 1;
	struct timeval now;

	gettime(&now);

	DSR_READ_LOCK(&t->lock);

	len +=
	    sprintf(buf, "# %-15s %-6s %-8s %15s:%s\n", "IPAddr", "TTL", "Used",
		    "TargetIPAddr", "ID");

	list_for_each(pos1, &t->head) {
		struct rreq_tbl_entry *e = (struct rreq_tbl_entry *)pos1;
		struct id_entry *id_e;

		if (TBL_EMPTY(&e->rreq_id_tbl))
			len +=
			    sprintf(buf + len, "  %-15s %-6u %-8lu %15s:%s\n",
				    print_ip(e->node_addr), e->ttl,
				    timeval_diff(&now, &e->last_used) / 1000000,
				    "-", "-");
		else {
			id_e = (struct id_entry *)TBL_FIRST(&e->rreq_id_tbl);
			len +=
			    sprintf(buf + len, "  %-15s %-6u %-8lu %15s:%u\n",
				    print_ip(e->node_addr), e->ttl,
				    timeval_diff(&now, &e->last_used) / 1000000,
				    print_ip(id_e->trg_addr), id_e->id);
		}
		list_for_each(pos2, &e->rreq_id_tbl.head) {
			id_e = (struct id_entry *)pos2;
			if (!first)
				len +=
				    sprintf(buf + len, "%49s:%u\n",
					    print_ip(id_e->trg_addr), id_e->id);
			first = 0;
		}
	}

	DSR_READ_UNLOCK(&t->lock);
	return len;

}
#endif /* __KERNEL__ */

void NSCLASS rreq_tbl_timeout(unsigned long data)
{
	struct rreq_tbl_entry *e = (struct rreq_tbl_entry *)data;
	struct timeval expires;

	if (!e)
		return;

	tbl_detach(&rreq_tbl, &e->l);

	DEBUG("RREQ Timeout dst=%s timeout=%lu rexmts=%d \n",
	      print_ip(e->node_addr), e->timeout, e->num_rexmts);

	if (e->num_rexmts >= ConfVal(MaxRequestRexmt)) {
		DEBUG("MAX RREQs reached for %s\n", print_ip(e->node_addr));

		e->state = STATE_IDLE;

/* 		DSR_WRITE_UNLOCK(&rreq_tbl); */
		tbl_add_tail(&rreq_tbl, &e->l);
		return;
	}

	e->num_rexmts++;

	/* if (e->ttl == 1) */
/* 		e->timeout = ConfValToUsecs(RequestPeriod);  */
/* 	else */
	e->timeout *= 2;	/* Double timeout */

	e->ttl *= 2;		/* Double TTL */

	if (e->ttl > MAXTTL)
		e->ttl = MAXTTL;

	if (e->timeout > ConfValToUsecs(MaxRequestPeriod))
		e->timeout = ConfValToUsecs(MaxRequestPeriod);

	gettime(&e->last_used);

	dsr_rreq_send(e->node_addr, e->ttl);

	expires = e->last_used;
	timeval_add_usecs(&expires, e->timeout);

	/* Put at end of list */
	tbl_add_tail(&rreq_tbl, &e->l);

	set_timer(e->timer, &expires);
}

struct rreq_tbl_entry *NSCLASS __rreq_tbl_entry_create(struct in_addr node_addr)
{
	struct rreq_tbl_entry *e;

	e = (struct rreq_tbl_entry *)MALLOC(sizeof(struct rreq_tbl_entry),
					    GFP_ATOMIC);

	if (!e)
		return NULL;

	e->state = STATE_IDLE;
	e->node_addr = node_addr;
	e->ttl = 0;
	memset(&e->tx_time, 0, sizeof(struct timeval));;
	e->num_rexmts = 0;
#ifdef NS2
	e->timer = new DSRUUTimer(this, "RREQTblTimer");
#else
	e->timer = MALLOC(sizeof(DSRUUTimer), GFP_ATOMIC);
#endif

	if (!e->timer) {
		FREE(e);
		return NULL;
	}

	init_timer(e->timer);

	e->timer->function = &NSCLASS rreq_tbl_timeout;
	e->timer->data = (unsigned long)e;

	INIT_TBL(&e->rreq_id_tbl, ConfVal(RequestTableIds));

	return e;
}

struct rreq_tbl_entry *NSCLASS __rreq_tbl_add(struct in_addr node_addr)
{
	struct rreq_tbl_entry *e;

	e = __rreq_tbl_entry_create(node_addr);

	if (!e)
		return NULL;

	if (TBL_FULL(&rreq_tbl)) {
		struct rreq_tbl_entry *f;

		f = (struct rreq_tbl_entry *)TBL_FIRST(&rreq_tbl);

		__tbl_detach(&rreq_tbl, &f->l);

		del_timer_sync(f->timer);
#ifdef NS2
		delete f->timer;
#else
		FREE(f->timer);
#endif
		tbl_flush(&f->rreq_id_tbl, NULL);

		FREE(f);
	}
	__tbl_add_tail(&rreq_tbl, &e->l);

	return e;
}

int NSCLASS
rreq_tbl_add_id(struct in_addr initiator, struct in_addr target,
		unsigned short id)
{
	struct rreq_tbl_entry *e;
	struct id_entry *id_e;
	int res = 0;

	DSR_WRITE_LOCK(&rreq_tbl.lock);

	e = (struct rreq_tbl_entry *)__tbl_find(&rreq_tbl, &initiator,
						crit_addr);

	if (!e)
		e = __rreq_tbl_add(initiator);
	else {
		/* Put it last in the table */
		__tbl_detach(&rreq_tbl, &e->l);
		__tbl_add_tail(&rreq_tbl, &e->l);
	}

	if (!e) {
		res = -ENOMEM;
		goto out;
	}

	gettime(&e->last_used);

	if (TBL_FULL(&e->rreq_id_tbl))
		tbl_del_first(&e->rreq_id_tbl);

	id_e = (struct id_entry *)MALLOC(sizeof(struct id_entry), GFP_ATOMIC);

	if (!id_e) {
		res = -ENOMEM;
		goto out;
	}

	id_e->trg_addr = target;
	id_e->id = id;

	tbl_add_tail(&e->rreq_id_tbl, &id_e->l);
      out:
	DSR_WRITE_UNLOCK(&rreq_tbl.lock);

	return 1;
}

int NSCLASS rreq_tbl_route_discovery_cancel(struct in_addr dst)
{
	struct rreq_tbl_entry *e;

	e = (struct rreq_tbl_entry *)tbl_find_detach(&rreq_tbl, &dst,
						     crit_addr);

	if (!e) {
		DEBUG("%s not in RREQ table\n", print_ip(dst));
		return -1;
	}

	if (e->state == STATE_IN_ROUTE_DISC)
		del_timer_sync(e->timer);

	e->state = STATE_IDLE;
	gettime(&e->last_used);

	tbl_add_tail(&rreq_tbl, &e->l);

	return 1;
}

int NSCLASS dsr_rreq_route_discovery(struct in_addr target)
{
	struct rreq_tbl_entry *e;
	int ttl, res = 0;
	struct timeval expires;

#define	TTL_START 1

	DSR_WRITE_LOCK(&rreq_tbl.lock);

	e = (struct rreq_tbl_entry *)__tbl_find(&rreq_tbl, &target, crit_addr);

	if (!e)
		e = __rreq_tbl_add(target);
	else {
		/* Put it last in the table */
		__tbl_detach(&rreq_tbl, &e->l);
		__tbl_add_tail(&rreq_tbl, &e->l);
	}

	if (!e) {
		res = -ENOMEM;
		goto out;
	}

	if (e->state == STATE_IN_ROUTE_DISC) {
		DEBUG("Route discovery for %s already in progress\n",
		      print_ip(target));
		goto out;
	}
	DEBUG("Route discovery for %s\n", print_ip(target));

	gettime(&e->last_used);
	e->ttl = ttl = TTL_START;
	/* The draft does not actually specify how these Request Timeout values
	 * should be used... ??? I am just guessing here. */

	if (e->ttl == 1)
		e->timeout = ConfValToUsecs(NonpropRequestTimeout);
	else
		e->timeout = ConfValToUsecs(RequestPeriod);

	e->state = STATE_IN_ROUTE_DISC;
	e->num_rexmts = 0;

	expires = e->last_used;
	timeval_add_usecs(&expires, e->timeout);

	set_timer(e->timer, &expires);

	DSR_WRITE_UNLOCK(&rreq_tbl.lock);

	dsr_rreq_send(target, ttl);

	return 1;
      out:
	DSR_WRITE_UNLOCK(&rreq_tbl.lock);

	return res;
}

int NSCLASS dsr_rreq_duplicate(struct in_addr initiator, struct in_addr target,
			       unsigned int id)
{
	struct {
		struct in_addr *initiator;
		struct in_addr *target;
		unsigned int *id;
	} d;

	d.initiator = &initiator;
	d.target = ⌖
	d.id = &id;

	return in_tbl(&rreq_tbl, &d, crit_duplicate);
}

static struct dsr_rreq_opt *dsr_rreq_opt_add(char *buf, unsigned int len,
					     struct in_addr target,
					     unsigned int seqno)
{
	struct dsr_rreq_opt *rreq_opt;

	if (!buf || len < DSR_RREQ_HDR_LEN)
		return NULL;

	rreq_opt = (struct dsr_rreq_opt *)buf;

	rreq_opt->type = DSR_OPT_RREQ;
	rreq_opt->length = 6;
	rreq_opt->id = htons(seqno);
	rreq_opt->target = target.s_addr;

	return rreq_opt;
}

int NSCLASS dsr_rreq_send(struct in_addr target, int ttl)
{
	struct dsr_pkt *dp;
	char *buf;
	int len = DSR_OPT_HDR_LEN + DSR_RREQ_HDR_LEN;

	dp = dsr_pkt_alloc(NULL);

	if (!dp) {
		DEBUG("Could not allocate DSR packet\n");
		return -1;
	}
	dp->dst.s_addr = DSR_BROADCAST;
	dp->nxt_hop.s_addr = DSR_BROADCAST;
	dp->src = my_addr();

	buf = dsr_pkt_alloc_opts(dp, len);


	if (!buf)
		goto out_err;

	dp->nh.iph =
	    dsr_build_ip(dp, dp->src, dp->dst, IP_HDR_LEN, IP_HDR_LEN + len,
			 IPPROTO_DSR, ttl);

	if (!dp->nh.iph)
		goto out_err;

	dp->dh.opth = dsr_opt_hdr_add(buf, len, DSR_NO_NEXT_HDR_TYPE);

	if (!dp->dh.opth) {
		DEBUG("Could not create DSR opt header\n");
		goto out_err;
	}

	buf += DSR_OPT_HDR_LEN;
	len -= DSR_OPT_HDR_LEN;

	dp->rreq_opt = dsr_rreq_opt_add(buf, len, target, ++rreq_seqno);

	if (!dp->rreq_opt) {
		DEBUG("Could not create RREQ opt\n");
		goto out_err;
	}
#ifdef NS2
	DEBUG("Sending RREQ src=%s dst=%s target=%s ttl=%d iph->saddr()=%d\n",
	      print_ip(dp->src), print_ip(dp->dst), print_ip(target), ttl,
	      dp->nh.iph->saddr());
#endif

	dp->flags |= PKT_XMIT_JITTER;

	XMIT(dp);

	return 0;

      out_err:
	dsr_pkt_free(dp);

	return -1;
}

int NSCLASS dsr_rreq_opt_recv(struct dsr_pkt *dp, struct dsr_rreq_opt *rreq_opt)
{
	struct in_addr myaddr;
	struct in_addr trg;
	struct dsr_srt *srt_rev, *srt_rc;
	int action = DSR_PKT_NONE;
	int i, n;

	if (!dp || !rreq_opt || dp->flags & PKT_PROMISC_RECV)
		return DSR_PKT_DROP;
	
	dp->num_rreq_opts++;
	
	if (dp->num_rreq_opts > 1) {
		DEBUG("More than one RREQ opt!!! - Ignoring\n");
		return DSR_PKT_ERROR;
	}

	dp->rreq_opt = rreq_opt;

	myaddr = my_addr();
	
	trg.s_addr = rreq_opt->target;

	if (dsr_rreq_duplicate(dp->src, trg, ntohs(rreq_opt->id))) {
		DEBUG("Duplicate RREQ from %s\n", print_ip(dp->src));
		return DSR_PKT_DROP;
	}

	rreq_tbl_add_id(dp->src, trg, ntohs(rreq_opt->id));

	dp->srt = dsr_srt_new(dp->src, myaddr, DSR_RREQ_ADDRS_LEN(rreq_opt),
			      (char *)rreq_opt->addrs);

	if (!dp->srt) {
		DEBUG("Could not extract source route\n");
		return DSR_PKT_ERROR;
	}
	DEBUG("RREQ target=%s src=%s dst=%s laddrs=%d\n",
	      print_ip(trg), print_ip(dp->src),
	      print_ip(dp->dst), DSR_RREQ_ADDRS_LEN(rreq_opt));

	/* Add reversed source route */
	srt_rev = dsr_srt_new_rev(dp->srt);

	if (!srt_rev) {
		DEBUG("Could not reverse source route\n");
		return DSR_PKT_ERROR;
	}
	DEBUG("srt: %s\n", print_srt(dp->srt));
	DEBUG("srt_rev: %s\n", print_srt(srt_rev));

	dsr_rtc_add(srt_rev, ConfValToUsecs(RouteCacheTimeout), 0);

	/* Set previous hop */
	if (srt_rev->laddrs > 0)
		dp->prv_hop = srt_rev->addrs[0];
	else
		dp->prv_hop = srt_rev->dst;

	neigh_tbl_add(dp->prv_hop, dp->mac.ethh);

	/* Send buffered packets */
	send_buf_set_verdict(SEND_BUF_SEND, srt_rev->dst);

	if (rreq_opt->target == myaddr.s_addr) {

		DEBUG("RREQ OPT for me - Send RREP\n");

		/* According to the draft, the dest addr in the IP header must
		 * be updated with the target address */
#ifdef NS2
		dp->nh.iph->daddr() = (nsaddr_t) rreq_opt->target;
#else
		dp->nh.iph->daddr = rreq_opt->target;
#endif
		dsr_rrep_send(srt_rev, dp->srt);

		action = DSR_PKT_NONE;
		goto out;
	} 
	
	n = DSR_RREQ_ADDRS_LEN(rreq_opt) / sizeof(struct in_addr);
	
	if (dp->srt->src.s_addr == myaddr.s_addr)
		return DSR_PKT_DROP;
	
	for (i = 0; i < n; i++)
		if (dp->srt->addrs[i].s_addr == myaddr.s_addr) {
			action = DSR_PKT_DROP;
			goto out;
		}

	/* TODO: Check Blacklist */
	srt_rc = lc_srt_find(myaddr, trg);
	
	if (srt_rc) {
		struct dsr_srt *srt_cat;
		/* Send cached route reply */
		
		DEBUG("Send cached RREP\n");

		srt_cat = dsr_srt_concatenate(dp->srt, srt_rc);
		
		FREE(srt_rc);

		if (!srt_cat) {
			DEBUG("Could not concatenate\n");
			goto rreq_forward;
		}

		DEBUG("srt_cat: %s\n", print_srt(srt_cat));
		
		if (dsr_srt_check_duplicate(srt_cat) > 0) {
			DEBUG("Duplicate address in source route!!!\n");
			FREE(srt_cat);
			goto rreq_forward;				
		}
#ifdef NS2
		dp->nh.iph->daddr() = (nsaddr_t) rreq_opt->target;
#else
		dp->nh.iph->daddr = rreq_opt->target;
#endif
		DEBUG("Sending cached RREP to %s\n", print_ip(dp->src));
		dsr_rrep_send(srt_rev, srt_cat);
		
		action = DSR_PKT_NONE;	
		
		FREE(srt_cat);
	} else {

	rreq_forward:	
		dsr_pkt_alloc_opts_expand(dp, sizeof(struct in_addr));

		if (!DSR_LAST_OPT(dp, rreq_opt)) {
			char *to, *from;
			to = (char *)rreq_opt + rreq_opt->length + 2 +
			    sizeof(struct in_addr);
			from = (char *)rreq_opt + rreq_opt->length + 2;

			memmove(to, from, sizeof(struct in_addr));
		}
		rreq_opt->addrs[n] = myaddr.s_addr;
		rreq_opt->length += sizeof(struct in_addr);

		dp->dh.opth->p_len = htons(ntohs(dp->dh.opth->p_len) +
					   sizeof(struct in_addr));
#ifdef __KERNEL__
		dsr_build_ip(dp, dp->src, dp->dst, IP_HDR_LEN,
			     ntohs(dp->nh.iph->tot_len) +
			     sizeof(struct in_addr), IPPROTO_DSR,
			     dp->nh.iph->ttl);
#endif
		/* Forward RREQ */
		action = DSR_PKT_FORWARD_RREQ;
	}
      out:
	FREE(srt_rev);
	return action;
}

#ifdef __KERNEL__

static int
rreq_tbl_proc_info(char *buffer, char **start, off_t offset, int length)
{
	int len;

	len = rreq_tbl_print(&rreq_tbl, buffer);

	*start = buffer + offset;
	len -= offset;
	if (len > length)
		len = length;
	else if (len < 0)
		len = 0;
	return len;
}

#endif				/* __KERNEL__ */

int __init NSCLASS rreq_tbl_init(void)
{
	INIT_TBL(&rreq_tbl, RREQ_TBL_MAX_LEN);

#ifdef __KERNEL__
	proc_net_create(RREQ_TBL_PROC_NAME, 0, rreq_tbl_proc_info);
	get_random_bytes(&rreq_seqno, sizeof(unsigned int));
#else
	rreq_seqno = 0;
#endif
	return 0;
}

void __exit NSCLASS rreq_tbl_cleanup(void)
{
	struct rreq_tbl_entry *e;

	while ((e = (struct rreq_tbl_entry *)tbl_detach_first(&rreq_tbl))) {
		del_timer_sync(e->timer);
#ifdef NS2
		delete e->timer;
#else
		FREE(e->timer);
#endif
		tbl_flush(&e->rreq_id_tbl, crit_none);
	}
#ifdef __KERNEL__
	proc_net_remove(RREQ_TBL_PROC_NAME);
#endif
}