WIP commit, adding support for receiving into a pbuf.
[akaros.git] / kern / src / net / udp.c
1 /**
2  * Contains shamelessly stolen code from BSD & lwip, both have
3  * BSD-style licenses
4  *
5  */
6 #include <ros/common.h>
7 #include <string.h>
8 #include <kmalloc.h>
9 #include <socket.h>
10 #include <net.h>
11 #include <sys/queue.h> //TODO: use sys/queue.h rather than implementing
12 #include <atomic.h>
13
14 #include <bits/netinet.h>
15 #include <net/ip.h>
16 #include <net/udp.h>
17 #include <slab.h>
18 #include <socket.h>
19
20 struct udp_pcb *udp_pcbs;
21 uint16_t udp_port_num = SOCKET_PORT_START;
22
23
24 struct udp_pcb* udp_new(void){
25         struct udp_pcb *pcb = kmem_cache_alloc(udp_pcb_kcache, 0);
26     // if pcb is only tracking ttl, then no need!
27         if (pcb!= NULL){
28                 pcb->ttl = UDP_TTL;
29         memset(pcb, 0, sizeof(struct udp_pcb));
30         }
31         return pcb;
32 }
33
34 int udp_send(struct udp_pcb *pcb, struct pbuf *p)
35 {
36   /* send to the packet using remote ip and port stored in the pcb */
37   // rip and rport should be in socket not pcb?
38   return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
39 }
40
41
42 int udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
43                     struct in_addr *dst_ip, uint16_t dst_port){
44     // we now have one netif to send to, otherwise we need to route
45     // ip_route();
46     struct udp_hdr *udphdr;
47     struct pbuf *q;
48                 /* TODO: utility to peform inet_ntop and        inet_pton for debugging*/
49                 printd("udp_sendto ip %x, port %d\n", dst_ip->s_addr, dst_port); 
50     // broadcast?
51     if (pcb->local_port == 0) {
52                                 /* if the PCB not bound to a port, bind it and give local ip */
53         if (udp_bind(pcb, &pcb->local_ip, pcb->local_port)!=0)
54                                         warn("udp binding failed \n");
55     }
56     if (pbuf_header(p, UDP_HLEN)){ // we could probably save this check for block.
57         // CHECK: should allocate enough for the other headers too
58         q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
59         if (q == NULL)
60            panic("out of memory");
61         // if the original packet is not empty
62         if (p->tot_len !=0) {
63             pbuf_chain(q,p);
64             // check if it is chained properly ..
65         }
66     } else {
67                                 /* Successfully padded the header*/
68                                 q = p;
69     }
70     udphdr = (struct udp_hdr *) q->payload;
71                 printd("src port %x, dst port %x \n, length %x ", pcb->local_port, dst_port, q->tot_len);
72     udphdr->src_port = htons(pcb->local_port);
73     udphdr->dst_port = (dst_port);
74     udphdr->checksum = 0x0000; //either use brho's checksum or use cards' capabilities
75     udphdr->length = htons(q->tot_len); // 630
76                 // ip_output(q, src_ip, dst_ip, pcb->ttl, pcb->tos, IP_PROTO_UDP);
77                 ip_output(q, &global_ip, dst_ip, IPPROTO_UDP);
78
79     return 0;
80
81     // generate checksum if we need it.. check net.c
82     //src_ip = GLOBAL_IP;
83     // ip_output(blah);
84
85     // check if there is space to operate in place (likely not)
86     // allocate additional pbuf
87     // chain the two bufs together
88     // add udp headers
89     // call ip layer
90
91     // checksum calculation?
92 }
93 /* TODO: use the real queues we have implemented... */
94 int udp_bind(struct udp_pcb *pcb, struct in_addr *ip, uint16_t port){ 
95     int rebind = pcb->local_port;
96     struct udp_pcb *ipcb;
97                 assert(pcb);
98                 /* trying to assign port */
99     if (port != 0)
100         pcb->local_port = port;
101
102     /* no lock needed since we are just traversing/reading */
103     /* Check for double bind and rebind of the same pcb */
104     for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
105         /* is this UDP PCB already on active list? */
106         if (pcb == ipcb) {
107             rebind = 1; //already on the list
108         } else if (ipcb->local_port == port){
109             warn("someone else is using the port %d\n" , port); 
110             return -1;
111         }
112     }
113     /* accept data for all interfaces */
114     if (ip == NULL || (ip->s_addr == INADDR_ANY.s_addr))
115         pcb->local_ip = INADDR_ANY;
116     /* assign a port */
117     if (port == 0) {
118         port = SOCKET_PORT_START; 
119         ipcb = udp_pcbs;
120         while ((ipcb != NULL) && (port != SOCKET_PORT_END)) {
121             if (ipcb->local_port == port) {
122                 /* port is already used by another udp_pcb */
123                 port++;
124                 /* restart scanning all udp pcbs */
125                 ipcb = udp_pcbs;
126             } else {
127                 /* go on with next udp pcb */
128                 ipcb = ipcb->next;
129             }
130         }
131         if (ipcb != NULL){
132             warn("No more udp ports available!");
133         }
134     }
135     if (rebind == 0) {
136         /* place the PCB on the active list if not already there */
137                                 pcb->next = udp_pcbs;
138                                 udp_pcbs = pcb;
139     }
140                 printk("local port bound to 0x%x \n", port);
141     pcb->local_port = port;
142     return 0;
143 }
144
145
146
147