/* Kernel AODV v2.1 National Institute of Standards and Technology Luke Klein-Berndt ----------------------------------------------------- Version 2.1 new features: * Much more stable! * Added locks around important areas * Multihop Internet gatewaying now works * Multicast hack added * Many bug fixes! ----------------------------------------------------- Originally based upon MadHoc code. I am not sure how much of it is left anymore, but MadHoc proved to be a great starting point. MadHoc was written by - Fredrik Lilieblad, Oskar Mattsson, Petra Nylund, Dan Ouchterlony and Anders Roxenhag Mail: mad-hoc@flyinglinux.net This software is Open Source under the GNU General Public Licence. */ #include "utils.h" /**************************************************** utils.h ---------------------------------------------------- Contains many misc funcations that provide basic functionality. ****************************************************/ extern u_int32_t g_broadcast_ip; extern u_int32_t g_my_ip; extern struct route_table_entry *g_my_entry; static struct sockaddr_in sin; //the port we are sending from #ifdef AODV_MULTICAST static struct socket *multicast_sock; #endif #ifdef AODV_SIGNAL static struct socket *iw_sock; #endif /**************************************************** init_sock ---------------------------------------------------- Creates a socket for sending out data ****************************************************/ #ifdef AODV_MULTICAST int init_multicast_sock(void) { int error; struct ifreq interface; mm_segment_t oldfs; int bool=1; char loop=0; char ttl; int choice=1; error=sock_create(PF_INET, SOCK_RAW, IPPROTO_RAW, &(multicast_sock)); memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr=g_my_ip; sin.sin_port = htons(AODVPORT); multicast_sock->sk->reuse =1; multicast_sock->sk->allocation = GFP_ATOMIC; multicast_sock->sk->priority = GFP_ATOMIC; error = multicast_sock->ops->bind(multicast_sock,(struct sockaddr*)&sin,sizeof(struct sockaddr_in)); /* #ifdef ARM strncpy(interface.ifr_ifrn.ifrn_name, "eth0", IFNAMSIZ); #else strncpy(interface.ifr_ifrn.ifrn_name, "eth1", IFNAMSIZ); #endif */ if (g_my_entry) strncpy(interface.ifr_ifrn.ifrn_name, g_my_entry->dev->name, IFNAMSIZ); else strncpy(interface.ifr_ifrn.ifrn_name, "eth0", IFNAMSIZ); oldfs = get_fs(); set_fs(get_ds()); // error=sys_fcntl(&(multicast_sock->sk),F_SETFL,O_NONBLOCK); //printk("error: %d\n",error); if (sock_setsockopt(multicast_sock,IPPROTO_IP,SO_BINDTODEVICE, (char *) &interface, sizeof(interface))<0) { printk(KERN_WARNING " Couldn't bind to route!\n"); } if ((error=sock_setsockopt(multicast_sock,IPPROTO_IP,SO_DONTROUTE,(char *) &choice, sizeof(int)))<0) { printk(KERN_WARNING " Couldn't mark not to route! %d\n",error); } if (error<0) { printk(KERN_ERR "Kernel AODV: Error, %d binding socket. This means that some other \n",error); printk(KERN_ERR " daemon is (or was a short time axgo) using port %i.\n",AODVPORT); return 0; } if((error =multicast_sock->ops->setsockopt(multicast_sock, IPPROTO_IP, IP_HDRINCL, (char *)&bool, sizeof(bool)))<0) { printk(KERN_WARNING "Error: %d In init_sock... I have problem in setting the multicast socket to have the IP hdr (%d) ...\n", error, bool); } /* //Set the interface on which the multicast packets will be sent if((error=sock->ops->setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, (char *)& ip, sizeof(ip)))<0) { printk("Error: %d In init_sock... I have a problem setting the interface on which the multicast packets leave...\n", error); } if((error=sock->ops->setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *)&loop, sizeof(loop)))<0) { printk("Error: %d In init_sock...I have a problem in unsetting the LOOP_BACK for the multicast packets...\n", error); } */ set_fs(oldfs); return 0; } #endif int init_sock(struct socket *sock, u_int32_t ip, char *dev_name) { int error; struct ifreq interface; mm_segment_t oldfs; //set the address we are sending from memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr=ip; sin.sin_port = htons(AODVPORT); sock->sk->reuse =1; sock->sk->allocation = GFP_ATOMIC; sock->sk->priority = GFP_ATOMIC; error = sock->ops->bind(sock,(struct sockaddr*)&sin,sizeof(struct sockaddr_in)); strncpy(interface.ifr_ifrn.ifrn_name,dev_name,IFNAMSIZ); oldfs = get_fs(); set_fs(KERNEL_DS); //thank to Soyeon Anh and Dinesh Dharmaraju for spotting this bug! error=sock_setsockopt(sock,SOL_SOCKET,SO_BINDTODEVICE, (char *) &interface, sizeof(interface))<0; set_fs(oldfs); if (error<0) { printk(KERN_ERR "Kernel AODV: Error, %d binding socket. This means that some other \n",error); printk(KERN_ERR " daemon is (or was a short time axgo) using port %i.\n",AODVPORT); return 0; } return 0; } /**************************************************** close_sock ---------------------------------------------------- Closes the socket ****************************************************/ void close_sock(void) { struct interface_list_entry *tmp_interface,*dead_interface; #ifdef AODV_MULTICAST sock_release(multicast_sock); #endif tmp_interface=find_first_interface_entry(); while(tmp_interface!=NULL) { sock_release(tmp_interface->sock); dead_interface=tmp_interface; tmp_interface=tmp_interface->next; kfree(dead_interface); } } #ifdef AODV_SIGNAL void init_iw_sock(void) { int error; error = sock_create(AF_INET,SOCK_DGRAM,0,&iw_sock); if (error<0) { printk(KERN_ERR "Error during creation of socket; terminating, %d\n",error); } } void close_iw_sock(void) { sock_release(iw_sock); } #endif #ifdef AODV_SIGNAL int set_spy() { int errno; int i; int nbr; /* Number of valid addresses */ mm_segment_t oldfs; struct neighbor_list_entry *tmp_neigh; struct interface_list_entry *tmp_interface; struct sockaddr iw_sa[IW_MAX_SPY]; struct iwreq wrq; unsigned char *ucp; tmp_interface=find_first_interface_entry(); while (tmp_interface!=NULL) { if ((tmp_interface->dev->get_wireless_stats!=NULL) && (tmp_interface->dev->do_ioctl!=NULL)) { i=0; tmp_neigh=find_first_neighbor_list_entry(); while (tmp_neigh!=NULL) { //ucp=(unsigned char *)&(tmp_neigh->ip); if ((tmp_interface->dev==tmp_neigh->dev))// && (((ucp[3] & 0xff)<111) &&((ucp[3] & 0xff)>100) )) { if ( ihw_addr),sizeof(struct sockaddr)); i++; tmp_neigh->link=255; } /* else { update_link_by_hw(tmp_neigh->hw_addr,255); tmp_neigh->link=255; }*/ } tmp_neigh=tmp_neigh->next; } strncpy(wrq.ifr_name, tmp_interface->dev->name, IFNAMSIZ); wrq.u.data.pointer = (caddr_t) &(iw_sa); wrq.u.data.length = i; wrq.u.data.flags = 0; oldfs = get_fs(); set_fs(KERNEL_DS); errno=tmp_interface->dev->do_ioctl(tmp_interface->dev, (struct ifreq * ) &wrq,SIOCSIWSPY); set_fs(oldfs); if (errno<0) printk(KERN_WARNING "AODV: Error with SIOCSIWSPY: %d\n", errno); } tmp_interface=tmp_interface->next; } } int get_range_info(struct net_device *dev,char * ifname, struct iw_range * range) { struct iwreq wrq; char buffer[sizeof(struct iw_range) * 2]; /* Large enough */ /* Cleanup */ memset(buffer, 0, sizeof(range)); strcpy(wrq.ifr_name, ifname); wrq.u.data.pointer = (caddr_t) buffer; wrq.u.data.length = 0; wrq.u.data.flags = 0; if(dev->do_ioctl(dev, (struct ifreq * ) &wrq,SIOCGIWRANGE) < 0) return(-1); /* Copy stuff at the right place, ignore extra */ memcpy((char *) range, buffer, sizeof(struct iw_range)); return(0); } void get_wireless_stats() { int n,i,has_range=0; char buffer[(sizeof(struct iw_quality) + sizeof(struct sockaddr)) * IW_MAX_SPY]; u_int8_t temp; struct iwreq wrq; struct neighbor_list_entry *tmp_neigh; struct interface_list_entry *tmp_interface; struct sockaddr hwa[IW_MAX_SPY]; struct iw_quality qual[IW_MAX_SPY]; struct iw_range range; tmp_interface=find_first_interface_entry(); while (tmp_interface!=NULL) { if ((tmp_interface->dev->get_wireless_stats!=NULL) && (tmp_interface->dev->do_ioctl!=NULL)) { strncpy(wrq.ifr_name,tmp_interface->dev->name , IFNAMSIZ); wrq.u.data.pointer = (caddr_t) buffer; wrq.u.data.length = 0; wrq.u.data.flags = 0; tmp_interface->dev->do_ioctl(tmp_interface->dev,(struct ifreq * ) &wrq,SIOCGIWSPY ); if(get_range_info(tmp_interface->dev, tmp_interface->dev->name , &(range)) >= 0) { has_range = 1; } n = wrq.u.data.length; memcpy(hwa, buffer, n * sizeof(struct sockaddr)); memcpy(qual, buffer + n*sizeof(struct sockaddr), n*sizeof(struct iw_quality)); for(i = 0; i < n; i++) { if(has_range && (qual[i].level != 0)) { if (range.max_qual.qual!=0) { update_link_by_hw(hwa[i].sa_data,qual[i].level); } } else { update_link_by_hw(hwa[i].sa_data,qual[i].level); } } } tmp_interface=tmp_interface->next; } } int read_signal_proc(char *buffer, char **buffer_location, off_t offset, int buffer_length,int *eof,void *data) { static char *my_buffer; char temp_buffer[200]; struct neighbor_list_entry *tmp_entry; int len; char dst[16]; int n,i; char stats_buffer[(sizeof(struct iw_quality) + sizeof(struct sockaddr)) * IW_MAX_SPY]; struct iwreq wrq; struct interface_list_entry *tmp_interface; struct sockaddr *hwa; struct iw_quality *qual; mm_segment_t oldfs; // struct sockaddr hwa[IW_MAX_SPY]; //struct iw_quality qual[IW_MAX_SPY]; my_buffer=buffer; strcpy(my_buffer,""); tmp_interface=find_first_interface_entry(); //reset_route_table_link(); while (tmp_interface!=NULL) { if ((tmp_interface->dev->get_wireless_stats!=NULL) && (tmp_interface->dev->do_ioctl!=NULL)) { strncpy(wrq.ifr_name,tmp_interface->dev->name , IFNAMSIZ); wrq.u.data.pointer = (caddr_t) stats_buffer; wrq.u.data.length = 64; //IW_MAX_GET_SPY; wrq.u.data.flags = 0; oldfs = get_fs(); set_fs(KERNEL_DS); tmp_interface->dev->do_ioctl(tmp_interface->dev,(struct ifreq * ) &wrq,SIOCGIWSPY ); set_fs(oldfs); n = wrq.u.data.length; hwa = (struct sockaddr *) stats_buffer; qual = (struct iw_quality *) (stats_buffer + (sizeof(struct sockaddr) * n)); for(i = 0; i < n; i++) { //printk ("%u %d\n",hwa[i].sa_data,qual[i].level - 0x100); //update_link_by_hw(hwa[i].sa_data,qual[i].level); tmp_entry= find_neighbor_list_entry_by_hw(hwa[i].sa_data); if(tmp_entry!=NULL) { strcpy(dst,inet_ntoa(tmp_entry->ip)); sprintf(temp_buffer,"1 %s, %d \n",dst ,qual[i].level - 0x100); strcat(my_buffer,temp_buffer); } } } tmp_interface=tmp_interface->next; } len = strlen(my_buffer); *buffer_location = my_buffer + offset; len -= offset; if (len > buffer_length) len = buffer_length; else if (len < 0) len = 0; return len; } #endif int read_stats_proc(char *buffer, char **buffer_location, off_t offset, int buffer_length,int *eof,void *data) { static char *my_buffer; char temp_buffer[200]; char temp[20]; int len=0,i; u_int32_t curr_time; char dst[16]; char hop[16]; u_int64_t numerator; my_buffer=buffer; numerator = (getcurrtime()-monitor.last_read); curr_time=do_div( numerator, 1000 ); if (curr_time!=0) { sprintf(my_buffer,"Bytes: %d\nPackets: %d\nRouting: %d\nRREP: %d\nRREQ: %d\nRERR: %d\n",monitor.bytes/curr_time,monitor.packets/curr_time,monitor.routing_packets/curr_time,monitor.rrep/curr_time,monitor.rreq/curr_time,monitor.rrer/curr_time); monitor.last_read=getcurrtime(); monitor.bytes=0; monitor.packets=0; monitor.routing_packets=0; monitor.rreq=0; monitor.rrep=0; monitor.rrer=0; len = strlen(my_buffer); *buffer_location = my_buffer + offset; len -= offset; if (len > buffer_length) len = buffer_length; else if (len < 0) len = 0; } return len; } /**************************************************** send_datagram ---------------------------------------------------- Used to send out a UDP packet through a socket ****************************************************/ #ifdef AODV_MULTICAST int rebroadcast(u_int32_t dst,u_int16_t datalen, void *data, u_int8_t ttl) { struct msghdr msg; struct iovec iov; mm_segment_t oldfs; unsigned char *ucp; u_int32_t space; struct iphdr *iph=data; int retval; int len=0; /* First, test if the socket has any buffer-space left. If not, no need to actually try to send something. */ ucp=(unsigned char *)&(g_my_ip); space = sock_wspace(multicast_sock->sk); if (spaceiov_len = datalen; msg.msg_iov->iov_base = (char*) data; multicast_sock->sk->broadcast=1; multicast_sock->sk->protinfo.af_inet.ttl=ttl; oldfs = get_fs(); set_fs(KERNEL_DS); len = sock_sendmsg(multicast_sock,&msg,datalen); if (len<0) { printk("REBROADCAST: Error sending! err no: %d, Dst: %s\n",len,inet_ntoa(dst)); } set_fs(oldfs); return 0; } #endif int local_broadcast(u_int8_t ttl, void *data, int datalen) { struct interface_list_entry *tmp_interface; struct msghdr msg; struct iovec iov; u_int64_t curr_time; mm_segment_t oldfs; int len=0; curr_time=getcurrtime(); if (ttl == 0 ) return 0; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr= g_broadcast_ip; sin.sin_port = htons((unsigned short)AODVPORT); //define the message we are going to be sending out msg.msg_name = (void *) &(sin); msg.msg_namelen = sizeof(sin); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL; msg.msg_iov->iov_len = (__kernel_size_t) datalen; msg.msg_iov->iov_base = (char*) data; tmp_interface=find_first_interface_entry(); while ( (tmp_interface) && (tmp_interface->sock) && ( sock_wspace(tmp_interface->sock->sk) >= datalen) ) { tmp_interface->sock->sk->broadcast=1; tmp_interface->sock->sk->protinfo.af_inet.ttl=ttl; tmp_interface->last_broadcast=curr_time; oldfs = get_fs(); set_fs(KERNEL_DS); len = sock_sendmsg(tmp_interface->sock,&msg,datalen); if (len<0) printk(KERN_WARNING "AODV: Error sending! err no: %d,on interface: %s\n",len,tmp_interface->dev->name); set_fs(oldfs); monitor.routing_packets++; tmp_interface=tmp_interface->next; } return len; } int send_message(u_int32_t dst_ip,u_int8_t ttl, void *data, int datalen) { mm_segment_t oldfs; struct msghdr msg; struct interface_list_entry *tmp_interface; struct route_table_entry *tmp_route; struct iovec iov; u_int64_t curr_time; int len; u_int32_t space; memset(&sin,0,sizeof(sin)); sin.sin_family = AF_INET; sin.sin_addr.s_addr= dst_ip; sin.sin_port = htons((unsigned short)AODVPORT); //define the message we are going to be sending out msg.msg_name = (void *) &(sin); msg.msg_namelen = sizeof(sin); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = MSG_DONTWAIT|MSG_NOSIGNAL; msg.msg_iov->iov_len = datalen; msg.msg_iov->iov_base = (char*) data; if (ttl == 0 ) return 0; curr_time=getcurrtime(); tmp_route=find_route_table_entry(dst_ip); if (tmp_route==NULL) { printk(KERN_WARNING "AODV: Can't find route to: %s \n",inet_ntoa(dst_ip)); return -EHOSTUNREACH; } tmp_interface=find_interface_by_dev(tmp_route->dev); if (tmp_interface==NULL) { printk(KERN_WARNING "AODV: Error sending! Unable to find interface!\n"); return -ENODEV; } space = sock_wspace(tmp_interface->sock->sk); if (spacesock->sk->broadcast=0; tmp_interface->sock->sk->protinfo.af_inet.ttl=ttl; tmp_interface->last_broadcast=curr_time; oldfs = get_fs(); set_fs(KERNEL_DS); len = sock_sendmsg(tmp_interface->sock,&msg,datalen); if (len<0) { printk(KERN_WARNING "AODV: Error sending! err no: %d, Dst: %s\n",len,inet_ntoa(dst_ip)); } set_fs(oldfs); monitor.routing_packets++; return 0; } /**************************************************** getcurrtime ---------------------------------------------------- Returns the current time ****************************************************/ u_int64_t getcurrtime() { struct timeval tv; u_int64_t result; do_gettimeofday(&tv); //This is a fix for an error that occurs on ARM Linux Kernels because they do 64bits differently //Thanks to S. Peter Li for coming up with this fix! result = (u_int64_t)tv.tv_usec; do_div(result, 1000); return ((u_int64_t)tv.tv_sec) * 1000 + result; } /**************************************************** inet_ntoa ---------------------------------------------------- Converts a IP address repersented in a 32 bit unsigned int into a string ****************************************************/ char *inet_ntoa(__u32 ina) { static char buf[4*sizeof "123"]; unsigned char *ucp = (unsigned char *)&ina; sprintf(buf, "%d.%d.%d.%d", ucp[0] & 0xff, ucp[1] & 0xff, ucp[2] & 0xff, ucp[3] & 0xff); return buf; } #ifdef AODV_GATEWAY /**************************************************** adhoc_subnet_test ---------------------------------------------------- Tests to see if the address is the subnet defined as the Ad Hoc Subnet ****************************************************/ int adhoc_subnet_test( u_int32_t ina) { unsigned char *ucp = (unsigned char *)&ina; unsigned char *uco = (unsigned char *)&g_aodv_subnet; if (g_aodv_subnet==0) return 1; if ((!(uco[0] & 0xff) || ((uco[0] & 0xff) == (ucp[0] & 0xff))) && (!(uco[1] & 0xff) || ((uco[1] & 0xff) == (ucp[1] & 0xff))) && (!(uco[2] & 0xff) || ((uco[2] & 0xff) == (ucp[2] & 0xff))) && (!(uco[3] & 0xff) || ((uco[3] & 0xff) == (ucp[3] & 0xff)))) return 1; printk(" AODV: %s is not in the AODV subnet\n",inet_ntoa(ina)); return 0; } #endif #ifdef AODV_MULTICAST /**************************************************** multicast_test ---------------------------------------------------- Tests to see if the address is a multicast address ****************************************************/ int multicast_test( u_int32_t ina) { unsigned char *ucp = (unsigned char *)&ina; if (((ucp[0] & 0xff) >=224) && ((ucp[0] & 0xff)<239)) return 1; else return 0; } #endif int seq_less_or_equal(u_int32_t seq_one,u_int32_t seq_two) { int *comp_seq_one = &seq_one; int *comp_seq_two = &seq_two; if ( ( *comp_seq_one - *comp_seq_two ) > 0 ) { return 0; } else return 1; } int seq_greater(u_int32_t seq_one,u_int32_t seq_two) { int *comp_seq_one = &seq_one; int *comp_seq_two = &seq_two; if ( ( *comp_seq_one - *comp_seq_two ) < 0 ) return 0; else return 1; } /**************************************************** inet_aton ---------------------------------------------------- Converts a string into a 32-bit unsigned int ****************************************************/ int inet_aton(const char *cp, __u32 *addr) { unsigned int val; int base, n; char c; u_int parts[4]; u_int *pp = parts; for (;;) { //Collect number up to ``.''. Values are specified as for C: // 0x=hex, 0=octal, other=decimal. val = 0; base = 10; if (*cp == '0') { if (*++cp == 'x' || *cp == 'X') base = 16, cp++; else base = 8; } while ((c = *cp) != '\0') { if (isascii(c) && isdigit(c)) { val = (val * base) + (c - '0'); cp++; continue; } if (base == 16 && isascii(c) && isxdigit(c)) { val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A')); cp++; continue; } break; } if (*cp == '.') { // Internet format: a.b.c.d a.b.c (with c treated as // 16-bits) a.b (with b treated as 24 bits) if (pp >= parts + 3 || val > 0xff) return (0); *pp++ = val, cp++; } else break; } // Check for trailing characters. if (*cp && (!isascii(*cp) || !isspace(*cp))) return (0); // Concoct the address according to the number of parts specified. n = pp - parts + 1; switch (n) { case 1: // a -- 32 bits break; case 2: //a.b -- 8.24 bits if (val > 0xffffff) return (0); val |= parts[0] << 24; break; case 3: //a.b.c -- 8.8.16 bits if (val > 0xffff) return (0); val |= (parts[0] << 24) | (parts[1] << 16); break; case 4: // a.b.c.d -- 8.8.8.8 bits if (val > 0xff) return (0); val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); break; } if (addr) *addr= htonl(val); return (1); }