e3782333f7263afb4ff7abd269d35625c424b15f
[akaros.git] / kern / src / socket.c
1 /*
2  * Copyright (c) 2011 The Regents of the University of California
3  * David Zhu <yuzhu@cs.berkeley.edu>
4  * See LICENSE for details.
5  * 
6  * Socket layer on top of TCP abstraction. Similar to the BSD implementation.
7  *
8  */
9 #include <ros/common.h>
10 #include <socket.h>
11 #include <vfs.h>
12 #include <kref.h>
13 #include <syscall.h>
14 #include <sys/uio.h>
15 #include <mbuf.h>
16 #include <ros/errno.h>
17 #include <net.h>
18 #include <net/udp.h>
19 #include <net/pbuf.h>
20 #include <umem.h>
21 /*
22  *TODO: Figure out which socket.h is used where
23  *There are several socket.h in kern, and a couple more in glibc. Perhaps the glibc ones
24  *should grab from here..
25  */
26
27 struct kmem_cache *sock_kcache;
28 struct kmem_cache *mbuf_kcache;
29 struct kmem_cache *udp_pcb_kcache;
30 // file ops needed to support read/write on socket fd
31 static struct file_operations socket_op = {
32         0,
33         0,//soo_read,
34         0,//soo_write,
35         0,
36         0,
37         0,
38         0,
39         0,
40         0,
41         0,//soo_poll,
42         0,
43         0,
44         0, // sendpage might apply here
45         0,
46 };
47 static struct socket* getsocket(struct proc *p, int fd){
48         /* look up fd -> file */
49         struct file *so_file = get_file_from_fd(&(p->open_files), fd);
50
51         /* get socket and verify its type */
52         if (so_file == NULL){
53                 printd("getsocket() fd -> null file: fd %d\n", fd);
54                 return NULL;
55         }
56         if (so_file->f_op != &socket_op) {
57                 set_errno(ENOTSOCK);
58                 printd("fd %d maps to non-socket file\n");
59                 return NULL;
60         } else
61                 return (struct socket*) so_file->f_privdata;
62 }
63
64 struct socket* alloc_sock(int socket_family, int socket_type, int protocol){
65         struct socket *newsock = kmem_cache_alloc(sock_kcache, 0);
66         assert(newsock);
67
68         newsock->so_family = socket_family;
69         newsock->so_type = socket_type;
70         newsock->so_protocol = protocol;
71         newsock->so_state = SS_ISDISCONNECTED;
72         pbuf_head_init(&newsock->recv_buff);
73         pbuf_head_init(&newsock->send_buff);
74         if (socket_type == SOCK_DGRAM){
75                 newsock->so_pcb = udp_new();
76                 /* back link */
77                 ((struct udp_pcb*) (newsock->so_pcb))->pcbsock = newsock;
78         }
79         return newsock;
80
81 }
82 // TODO: refactor vfs so we can allocate fd and do the basic initialization
83 struct file *alloc_socket_file(struct socket* sock) {
84         struct file *file = alloc_file();
85         if (file == NULL) return 0;
86
87         // Linux fakes a dentry and an inode for socks, see socket.c : sock_alloc_file
88         file->f_dentry = NULL; // This might break things?
89         file->f_vfsmnt = 0;
90         file->f_flags = 0;
91
92         file->f_mode = S_IRUSR | S_IWUSR; // both read and write for socket files
93
94         file->f_pos = 0;
95         file->f_uid = 0;
96         file->f_gid = 0;
97         file->f_error = 0;
98
99         file->f_op = &socket_op;
100         file->f_privdata = sock;
101         file->f_mapping = 0;
102         return file;
103 }
104
105 void socket_init(){
106         
107         /* allocate buf for socket */
108         sock_kcache = kmem_cache_create("socket", sizeof(struct socket),
109                                                                         __alignof__(struct socket), 0, 0, 0);
110         udp_pcb_kcache = kmem_cache_create("udppcb", sizeof(struct udp_pcb), 
111                                                                         __alignof__(struct udp_pcb), 0, 0, 0);
112
113         pbuf_init();
114
115 }
116
117 intreg_t sys_socket(struct proc *p, int socket_family, int socket_type, int protocol){
118         //check validity of params
119         if (socket_family !=AF_INET && socket_type!=SOCK_DGRAM)
120                 return 0;
121         struct socket *sock = alloc_sock(socket_family, socket_type, protocol);
122         struct file *file = alloc_socket_file(sock);
123         
124         if (file == NULL) return -1;
125         int fd = insert_file(&p->open_files, file, 0);
126         if (fd < 0) {
127                 warn("File insertion for socket open failed");
128                 return -1;
129         }
130         kref_put(&file->f_kref);
131         printk("Socket open, res = %d\n", fd);
132         return fd;
133 }
134 intreg_t send_iov(struct socket* sock, struct iovec* iov, int flags){
135         
136         // COPY_COUNT: for each iov, copy into mbuf, and send
137         // should not copy here, copy in the protocol..
138         // should be esomething like this sock->so_proto->pr_send(sock, iov, flags);
139         // make it datagram specific for now...
140         send_datagram(sock, iov, flags);
141         // finally time to check for validity of UA, in the protocol send
142         return 0;       
143 }
144 /*TODO: iov support currently broken */
145 int send_datagram(struct socket* sock, struct iovec* iov, int flags){
146         // is this a connection oriented protocol? 
147         struct pbuf *prev = NULL;
148         struct pbuf *curr = NULL;
149         if (sock->so_type == SOCK_STREAM){
150                 set_errno(ENOTCONN);
151                 return -1;
152         }
153         
154         // possible sock locks needed
155         if ((sock->so_state & SS_ISCONNECTED) == 0){
156                 set_errno(EINVAL);
157                 return -1;
158         }
159     // pbuf_ref needs to map in the user ref
160         for (int i = 0; i< sizeof(iov) / sizeof (struct iovec); i++){
161                 prev = curr;
162                 curr = pbuf_alloc(PBUF_TRANSPORT, iov[i].iov_len, PBUF_REF);
163                 if (prev!=NULL) pbuf_chain(prev, curr);
164         }
165         // struct pbuf* pb = pbuf_alloc(PBUF_TRANSPORT, PBUF_REF);
166         udp_send(sock->so_pcb, prev);
167         return 0;
168         
169 }
170
171 /* sys_sendto can send SOCK_DGRAM and eventually SOCK_STREAM 
172  * SOCK_DGRAM uses PBUF_REF since UDP does not need to wait for ack
173  * SOCK_STREAM uses PBUF_
174  *
175  */
176 intreg_t sys_sendto(struct proc *p_proc, int fd, const void *buffer, size_t length, 
177                         int flags, const struct sockaddr *dest_addr, socklen_t dest_len){
178         // look up the socket
179         struct socket* sock = getsocket(p_proc, fd);
180         int error;
181         struct sockaddr_in *in_addr;
182         uint16_t r_port;
183         if (sock == NULL) {
184                 set_errno(EBADF);
185                 return -1;      
186         }
187         if (sock->so_type == SOCK_DGRAM){
188                 in_addr = (struct sockaddr_in *)dest_addr;
189                 struct pbuf* buf = pbuf_alloc(PBUF_TRANSPORT, length, PBUF_REF);
190                 if (buf != NULL)
191                         buf->payload = (void*)buffer;
192                 else 
193                         warn("pbuf alloc failed \n");
194                 // potentially unsafe cast to udp_pcb 
195                 return udp_sendto((struct udp_pcb*) sock->so_pcb, buf, &in_addr->sin_addr, in_addr->sin_port);
196         }
197
198         return -1;
199   //TODO: support for sendmsg and iovectors? Let's get the basis working first!
200         #if 0 
201         // use iovector to handle sendmsg calls too, and potentially scatter-gather
202         struct msghdr msg;
203         struct iovec iov;
204         struct uio auio;
205         
206         // checking for permission only when you are sending it
207         // potential bug TOCTOU, especially with async calls
208                 
209     msg.msg_name = dest_addr;
210     msg.msg_namelen = dest_len;
211     msg.msg_iov = &iov;
212     msg.msg_iovlen = 1;
213     msg.msg_control = 0;
214     
215         iov.iov_base = buffer;
216     iov.iov_len = length;
217         
218
219         // this is why we need another function to populate auio
220
221         auio.uio_iov = iov;
222         auio.uio_iovcnt = 1;
223         auio.uio_offset = 0;
224         auio.uio_resid = 0;
225         auio.uio_rw = UIO_WRITE;
226         auio.uio_proc = p;
227
228         // consider changing to send_uaio, since we care about progress.
229     error = send_iov(soc, iov, flags);
230         #endif
231 }
232 intreg_t sys_recvfrom(struct proc *p, int socket, void *restrict buffer, size_t length, int flags, struct sockaddr *restrict address, socklen_t *restrict address_len){
233         struct socket* sock = getsocket(p, socket);     
234         int copied = 0;
235         int returnval = 0;
236         if (sock == NULL) {
237                 set_errno(EBADF);
238                 return -1;
239         }
240         if (sock->so_type == SOCK_DGRAM){
241                 struct pbuf_head *ph = &(sock->recv_buff);
242                 struct pbuf* buf = NULL;
243                 /* TODO: busy poll the socket buffer for now */
244                 while (buf == NULL){
245                         buf = detach_pbuf(ph);
246                         if (buf){
247                                 copied = buf->len - sizeof(struct udp_hdr);
248                                 if (copied > length)
249                                         copied = length;
250                                 
251                         pbuf_header(buf, -PBUF_TRANSPORT_HLEN);
252                         // copy it to user space
253                         returnval = memcpy_to_user_errno(p, buffer, buf->payload, copied);
254                         }
255                 }
256         }
257         return returnval;
258 }