| /** | 
 |  * Contains shamelessly stolen code from BSD & lwip, both have | 
 |  * BSD-style licenses | 
 |  * | 
 |  */ | 
 | #include <ros/common.h> | 
 | #include <string.h> | 
 | #include <kmalloc.h> | 
 | #include <socket.h> | 
 | #include <net.h> | 
 | #include <sys/queue.h> | 
 | #include <atomic.h> | 
 |  | 
 | #include <bits/netinet.h> | 
 | #include <net/ip.h> | 
 | #include <net/udp.h> | 
 | #include <slab.h> | 
 | #include <socket.h> | 
 | #include <debug.h> | 
 |  | 
 | struct udp_pcb *udp_pcbs; | 
 | uint16_t udp_port_num = SOCKET_PORT_START; | 
 |  | 
 | struct udp_pcb* udp_new(void){ | 
 | 	struct udp_pcb *pcb = kmem_cache_alloc(udp_pcb_kcache, 0); | 
 |     // if pcb is only tracking ttl, then no need! | 
 | 	if (pcb!= NULL){ | 
 | 		pcb->ttl = UDP_TTL; | 
 |     	memset(pcb, 0, sizeof(struct udp_pcb)); | 
 | 	} | 
 | 	return pcb; | 
 | } | 
 |  | 
 | int udp_send(struct udp_pcb *pcb, struct pbuf *p) | 
 | { | 
 |   /* send to the packet using remote ip and port stored in the pcb */ | 
 |   // rip and rport should be in socket not pcb? | 
 |   return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port); | 
 | } | 
 |  | 
 | typedef unsigned char u16; | 
 | typedef unsigned long u32; | 
 |  | 
 | u16 udp_sum_calc(u16 len_udp, u16 src_addr[],u16 dest_addr[],  int padding, u16 buff[]) | 
 | { | 
 | u16 prot_udp=17; | 
 | u16 padd=0; | 
 | u16 word16; | 
 | u32 sum; | 
 | int i; | 
 | 	 | 
 | 	// Find out if the length of data is even or odd number. If odd, | 
 | 	// add a padding byte = 0 at the end of packet | 
 | 	if ((padding&1)==1){ | 
 | 		padd=1; | 
 | 		buff[len_udp]=0; | 
 | 	} | 
 | 	 | 
 | 	//initialize sum to zero | 
 | 	sum=0; | 
 | 	 | 
 | 	// make 16 bit words out of every two adjacent 8 bit words and  | 
 | 	// calculate the sum of all 16 vit words | 
 | 	for (i=0;i<len_udp+padd;i=i+2){ | 
 | 		word16 =((buff[i]<<8)&0xFF00)+(buff[i+1]&0xFF); | 
 | 		sum = sum + (unsigned long)word16; | 
 | 	}	 | 
 | 	// add the UDP pseudo header which contains the IP source and destinationn addresses | 
 | 	for (i=0;i<4;i=i+2){ | 
 | 		word16 =((src_addr[i]<<8)&0xFF00)+(src_addr[i+1]&0xFF); | 
 | 		sum=sum+word16;	 | 
 | 	} | 
 | 	for (i=0;i<4;i=i+2){ | 
 | 		word16 =((dest_addr[i]<<8)&0xFF00)+(dest_addr[i+1]&0xFF); | 
 | 		sum=sum+word16; 	 | 
 | 	} | 
 | 	// the protocol number and the length of the UDP packet | 
 | 	sum = sum + prot_udp + len_udp; | 
 |  | 
 | 	// keep only the last 16 bits of the 32 bit calculated sum and add the carries | 
 |     	while (sum>>16) | 
 | 		sum = (sum & 0xFFFF)+(sum >> 16); | 
 | 		 | 
 | 	// Take the one's complement of sum | 
 | 	sum = ~sum; | 
 |  | 
 | return ((u16) sum); | 
 | } | 
 |  | 
 | int udp_sendto(struct udp_pcb *pcb, struct pbuf *p, | 
 |                     struct in_addr *dst_ip, uint16_t dst_port){ | 
 |     // we now have one netif to send to, otherwise we need to route | 
 |     // ip_route(); | 
 |     struct udp_hdr *udphdr; | 
 |     struct pbuf *q; | 
 | 		printd("udp_sendto ip %x, port %d\n", dst_ip->s_addr, dst_port);  | 
 |     // broadcast? | 
 |     if (pcb->local_port == 0) { | 
 | 				/* if the PCB not bound to a port, bind it and give local ip */ | 
 |         if (udp_bind(pcb, &pcb->local_ip, pcb->local_port)!=0) | 
 | 					warn("udp binding failed \n"); | 
 |     } | 
 |     if (pbuf_header(p, UDP_HLEN)){ // we could probably save this check for block. | 
 |         // CHECK: should allocate enough for the other headers too | 
 |         q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM); | 
 |         if (q == NULL) | 
 |            panic("out of memory"); | 
 |         // if the original packet is not empty | 
 |         if (p->tot_len !=0) { | 
 |             pbuf_chain(q,p); | 
 |             // check if it is chained properly .. | 
 |         } | 
 |     } else { | 
 | 				/* Successfully padded the header*/ | 
 | 				q = p; | 
 |     } | 
 |  | 
 |     udphdr = (struct udp_hdr *) q->payload; | 
 | 		printd("src port %d, dst port %d \n, length %d ", pcb->local_port, ntohs(dst_port), q->tot_len); | 
 |     udphdr->src_port = htons(pcb->local_port); | 
 |     udphdr->dst_port = (dst_port); | 
 |     udphdr->length = htons(q->tot_len);  | 
 | 		udphdr->checksum = 0; // just to be sure. | 
 | 		// printd("checksum inet_chksum %x \n", udphdr->checksum); | 
 | 		printd("params src addr %x, dst addr %x, length %x \n", LOCAL_IP_ADDR.s_addr, (dst_ip->s_addr),  | 
 | 					  q->tot_len); | 
 |  | 
 |     udphdr->checksum = inet_chksum_pseudo(q, htonl(LOCAL_IP_ADDR.s_addr), dst_ip->s_addr, | 
 | 											 IPPROTO_UDP, q->tot_len); | 
 | 		printd ("method ours %x\n", udphdr->checksum); | 
 | 		// 0x0000; //either use brho's checksum or use cards' capabilities | 
 | 		ip_output(q, &LOCAL_IP_ADDR, dst_ip, pcb->ttl, pcb->tos, IPPROTO_UDP); | 
 | 		// ip_output(q, &global_ip, dst_ip, IPPROTO_UDP); | 
 |     return 0; | 
 | } | 
 | /* TODO: use the real queues we have implemented... */ | 
 | int udp_bind(struct udp_pcb *pcb, const struct in_addr *ip, uint16_t port){  | 
 |     int rebind = pcb->local_port; | 
 |     struct udp_pcb *ipcb; | 
 | 		assert(pcb); | 
 | 		/* trying to assign port */ | 
 |     if (port != 0) | 
 |         pcb->local_port = port; | 
 |  | 
 |     /* no lock needed since we are just traversing/reading */ | 
 |     /* Check for double bind and rebind of the same pcb */ | 
 |     for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) { | 
 |         /* is this UDP PCB already on active list? */ | 
 |         if (pcb == ipcb) { | 
 |             rebind = 1; //already on the list | 
 |         } else if (ipcb->local_port == port){ | 
 |             warn("someone else is using the port %d\n" , port);  | 
 |             return -1; | 
 |         } | 
 |     } | 
 |     /* accept data for all interfaces */ | 
 |     if (ip == NULL || (ip->s_addr == INADDR_ANY.s_addr)) | 
 | 		/* true right now */ | 
 |         pcb->local_ip = INADDR_ANY; | 
 |     /* assign a port */ | 
 |     if (port == 0) { | 
 |         port = SOCKET_PORT_START;  | 
 |         ipcb = udp_pcbs; | 
 |         while ((ipcb != NULL) && (port != SOCKET_PORT_END)) { | 
 |             if (ipcb->local_port == port) { | 
 |                 /* port is already used by another udp_pcb */ | 
 |                 port++; | 
 |                 /* restart scanning all udp pcbs */ | 
 |                 ipcb = udp_pcbs; | 
 |             } else { | 
 |                 /* go on with next udp pcb */ | 
 |                 ipcb = ipcb->next; | 
 |             } | 
 |         } | 
 |         if (ipcb != NULL){ | 
 |             warn("No more udp ports available!"); | 
 |         } | 
 |     } | 
 |     if (rebind == 0) { | 
 |         /* place the PCB on the active list if not already there */ | 
 | 				pcb->next = udp_pcbs; | 
 | 				udp_pcbs = pcb; | 
 |     } | 
 | 		printk("local port bound to 0x%x \n", port); | 
 |     pcb->local_port = port; | 
 |     return 0; | 
 | } | 
 |  | 
 | /* port are in host order, ips are in network order */ | 
 | /* Think: a pcb is here, if someone is waiting for a connection or the udp conn | 
 |  * has been established */ | 
 | static struct udp_pcb* find_pcb(struct udp_pcb* list, uint16_t src_port, uint16_t dst_port, | 
 | 								uint16_t srcip, uint16_t dstip) { | 
 | 	struct udp_pcb* uncon_pcb = NULL; | 
 | 	struct udp_pcb* pcb = NULL; | 
 | 	uint8_t local_match = 0; | 
 |  | 
 | 	for (pcb = list; pcb != NULL; pcb = pcb->next) { | 
 | 		local_match = 0; | 
 | 		if ((pcb->local_port == dst_port)  | 
 | 			&& (pcb->local_ip.s_addr == dstip  | 
 | 			|| ip_addr_isany(&pcb->local_ip))){ | 
 | 				local_match = 1; | 
 |         if ((uncon_pcb == NULL) &&  | 
 |             ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) { | 
 |           /* the first unconnected matching PCB */ | 
 |           uncon_pcb = pcb; | 
 |         } | 
 | 		} | 
 |  | 
 | 		if (local_match && (pcb->remote_port == src_port) && | 
 | 				(ip_addr_isany(&pcb->remote_ip) || | 
 | 				 pcb->remote_ip.s_addr == srcip)) | 
 | 			/* perfect match */ | 
 | 			return pcb; | 
 | 	} | 
 | 	return uncon_pcb; | 
 | } | 
 |  | 
 | #if 0 // not working yet | 
 | // need to have pbuf queue support | 
 | int udp_attach(struct pbuf *p, struct sock *socket) { | 
 | 	// pretend the attaching of packet is succesful | 
 | 	/* | 
 | 	recv_q->last->next = p; | 
 | 	recv_q->last=p->last | 
 | 	*/  | 
 | } | 
 |  | 
 | #endif  | 
 |  | 
 | /** Process an incoming UDP datagram.  | 
 |  * Given an incoming UDP datagram, this function finds the right PCB | 
 |  * which links to the right socket buffer, and attaches the datagram | 
 |  * to the right socket.  | 
 |  * If no appropriate PCB is found, the pbuf is freed. | 
 |  */  | 
 |  | 
 | /** TODO: think about combining udp_input and ip_input together */ | 
 | // TODO: figure out if we even need a PCB? or just socket buff.  | 
 | // TODO: test out looking up pcbs.. since matching function may fail | 
 |  | 
 | int udp_input(struct pbuf *p){ | 
 | 	struct udp_hdr *udphdr; | 
 |  | 
 | 	struct udp_pcb *pcb, uncon_pcb; | 
 | 	struct ip_hdr *iphdr; | 
 | 	uint16_t src, dst; | 
 | 	bool local_match = 0; | 
 | 	iphdr = (struct ip_hdr *)p->payload; | 
 | 	/* Move the header to where the udp header is */ | 
 | 	if (pbuf_header(p, -((int16_t)((iphdr->hdr_len) * 4)))) { | 
 | 		warn("udp_input: Did not find a matching PCB for a udp packet\n"); | 
 | 		pbuf_free(p); | 
 | 		return -1; | 
 | 	} | 
 | 	printk("start of udp %p\n", p->payload); | 
 | 	udphdr = (struct udp_hdr *)p->payload; | 
 | 	/* convert the src port and dst port to host order */ | 
 | 	src = ntohs(udphdr->src_port); | 
 | 	dst = ntohs(udphdr->dst_port); | 
 | 	pcb = find_pcb(udp_pcbs, src, dst, iphdr->src_addr, iphdr->dst_addr); | 
 | 	/* TODO: Possibly adjust the pcb to the head of the queue? */ | 
 | 	/* TODO: Linux uses a set of hashtables to lookup PCBs  | 
 | 	 * Look at __udp4_lib_lookup function in Linux kernel 2.6.21.1 | 
 | 	 */ | 
 | 	/* Anything that is not directed at this pcb should have been dropped */ | 
 | 	if (pcb == NULL){ | 
 | 		warn("udp_input: Did not find a matching PCB for a udp packet\n"); | 
 | 		pbuf_free(p); | 
 | 		return -1; | 
 | 	} | 
 |  | 
 | 	/* checksum check */ | 
 |   if (udphdr->checksum != 0) { | 
 |     if (inet_chksum_pseudo(p, (iphdr->src_addr), (iphdr->dst_addr),  | 
 | 		                 IPPROTO_UDP, p->tot_len) != 0){ | 
 | 			warn("udp_input: UPD datagram discarded due to failed chksum!"); | 
 | 			pbuf_free(p); | 
 | 			return -1; | 
 |     } | 
 | 	} | 
 |   /* ignore SO_REUSE */ | 
 | 	if (pcb != NULL && pcb->pcbsock != NULL){ | 
 | 		/* For each in the pbuf chain, disconnect from the chain and add it to the | 
 | 		 * recv_buff of the correct socket  | 
 | 		 */  | 
 | 		struct socket *sock = pcb->pcbsock; | 
 | 		attach_pbuf(p, &sock->recv_buff); | 
 | 		struct kthread *kthread; | 
 | 		int8_t irq_state = 0; | 
 | 		/* First notify any blocking recv calls, | 
 | 		 * then notify anyone who might be waiting in a select | 
 | 		 */  | 
 | 		// multiple people might be waiting on the socket here.. | 
 | 		/* TODO: consider a helper with this and tcp.c */ | 
 | 		if (!sem_up_irqsave(&sock->sem, &irq_state)) { | 
 | 			// wake up all waiters | 
 | 			struct semaphore_entry *sentry, *sentry_tmp; | 
 | 			spin_lock(&sock->waiter_lock); | 
 | 			LIST_FOREACH_SAFE(sentry, &sock->waiters, link, sentry_tmp) { | 
 | 				//should only wake up one waiter | 
 | 				sem_up_irqsave(&sentry->sem, &irq_state); | 
 | 				LIST_REMOVE(sentry, link); | 
 | 				/* do not need to free since all the sentry are stack-based vars | 
 | 				 * */ | 
 | 			} | 
 | 			spin_unlock(&sock->waiter_lock); | 
 | 		} | 
 | 		// the attaching of pbuf should have increfed pbuf ref, so free is simply a decref | 
 | 		pbuf_free(p); | 
 | 	} | 
 | 	return 0; | 
 | } |