New developer version 0.6.8; added select () function; added demonstrating example...
[ZeXOS.git] / kernel / core / net / udp.c
blob15c0674e84bd3b0ad10ffc073044a35497076d47
1 /*
2 * ZeX/OS
3 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
4 * Copyright (C) 2009 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
5 * Copyright (C) 2010 Tomas 'ZeXx86' Jedrzejek (zexx86@zexos.org)
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include <system.h>
23 #include <string.h>
24 #include <dev.h>
25 #include <net/eth.h>
26 #include <net/net.h>
27 #include <net/if.h>
28 #include <net/ip.h>
29 #include <net/packet.h>
30 #include <net/udp.h>
31 #include <net/socket.h>
32 #include <fd.h>
33 #include <errno.h>
35 extern netif_t netif_list;
36 proto_udp_conn_t proto_udp_conn_list;
38 /* prototypes */
39 proto_udp_conn_t *net_proto_udp_conn_check (net_ipv4 ip_source, net_port port_source, net_ipv4 ip_dest, net_port port_dest, unsigned char *ret);
40 int net_proto_udp_read_cache (proto_udp_conn_t *conn, char *data, unsigned len);
41 int net_proto_udp_write (proto_udp_conn_t *conn, char *data, unsigned len);
42 proto_udp_conn_t *net_proto_udp_conn_find (int fd);
43 int net_proto_udp_conn_set (proto_udp_conn_t *conn, netif_t *eth, net_port port_source, net_ipv4 ip_dest, net_port port_dest);
44 unsigned net_proto_udp_conn_del (proto_udp_conn_t *conn);
45 int net_proto_udp_conn_add ();
47 /** UDP protocol
48 * User-friendly socket functions
50 int net_proto_udp_socket (fd_t *fd)
52 return net_proto_udp_conn_add (fd);
55 int net_proto_udp_connect (int fd, sockaddr_in *addr)
57 int ret = -1;
59 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
61 if (!conn)
62 return -1;
64 netif_t *netif;
65 for (netif = netif_list.next; netif != &netif_list; netif = netif->next) {
66 ret = net_proto_udp_conn_set (conn, netif, swap16 (netif_port_get ()), addr->sin_addr, addr->sin_port);
68 /* TODO: connect timeout */
70 /* blocking mode */
71 if (!(conn->flags & O_NONBLOCK)) {
72 if (!ret)
73 return -1;
74 } else
75 /* non-blocking mode */
76 return -1;
78 ret = 0;
81 return ret;
84 int net_proto_udp_send (int fd, char *msg, unsigned size)
86 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
88 if (!conn)
89 return -1;
91 int ret = net_proto_udp_write (conn, msg, size);
93 if (ret) {
94 /* blocking mode */
95 if (!(conn->flags & O_NONBLOCK)) {
97 } else {
98 /* non-blocking mode */
99 /* TODO: ? */
104 return size;
107 int net_proto_udp_sendto (int fd, char *msg, unsigned size, sockaddr_in *to)
109 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
111 if (!conn)
112 return -1;
114 //int ip = conn->ip_dest;
116 /*net_proto_ip_print (conn->ip_dest);
117 kprintf (" | ");
118 net_proto_ip_print (conn->ip_source);*/
120 //conn->ip_source = to->sin_addr;
122 int ret = net_proto_udp_write (conn, msg, size);
124 //conn->ip_dest = ip;
126 if (ret) {
127 /* blocking mode */
128 if (!(conn->flags & O_NONBLOCK)) {
130 } else {
131 /* non-blocking mode */
132 /* TODO: ? */
137 return size;
140 extern unsigned long timer_ticks;
141 int net_proto_udp_recv (int fd, char *msg, unsigned size)
143 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
145 if (!conn)
146 return -3;
148 if (!msg)
149 return -4;
151 int ret = 0;
153 /* blocking mode */
154 if (!(conn->flags & O_NONBLOCK)) {
155 unsigned long stime = timer_ticks;
157 while (!conn->len) {
158 if ((stime+10000) < timer_ticks)
159 return 0;
161 schedule ();
163 } else {
164 if (!conn->len)
165 return 0;
168 if (!conn->data)
169 return -5;
171 if (conn->len >= size)
172 return -2;
174 ret = conn->len;
176 if (conn->len > 0)
177 memcpy (msg, conn->data, conn->len);
179 conn->len = 0;
181 kfree (conn->data);
183 return ret;
186 int net_proto_udp_recvfrom (int fd, char *msg, unsigned size, sockaddr_in *from)
188 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
190 if (!conn)
191 return -3;
193 if (!msg)
194 return -4;
196 int ret = 0;
198 //int ip = conn->ip_dest;
200 from->sin_addr = conn->ip_dest;
201 //kprintf ("recvfrom -> ip: 0x%x\n", from->sin_addr);
203 /* blocking mode */
204 if (!(conn->flags & O_NONBLOCK)) {
205 /* TODO: timeout */
206 while (!conn->len)
207 schedule ();
208 } else {
209 if (!conn->len) {
210 //conn->ip_dest = ip;
211 return 0;
215 //conn->ip_dest = ip; // return old ip address back to structure
217 if (!conn->data)
218 return -5;
220 if (conn->len >= size)
221 return -2;
223 ret = conn->len;
225 if (conn->len > 0)
226 memcpy (msg, conn->data, conn->len);
228 conn->len = 0;
230 kfree (conn->data);
232 return ret;
235 int net_proto_udp_close (int fd)
237 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
239 if (!conn)
240 return -1;
242 int ret = net_proto_udp_conn_del (conn);
244 return ret;
247 int net_proto_udp_fcntl (int fd, int cmd, long arg)
249 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
251 if (!conn)
252 return -1;
254 switch (cmd) {
255 case F_GETFL:
256 return conn->flags;
257 case F_SETFL:
258 conn->flags = arg;
259 return 0;
262 return -1;
265 int net_proto_udp_bind (int fd, sockaddr_in *addr, socklen_t len)
267 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
269 if (!conn)
270 return -1;
272 netif_t *netif;
273 for (netif = netif_list.next; netif != &netif_list; netif = netif->next) {
274 net_proto_udp_conn_set (conn, netif, addr->sin_port, 0, 0);
275 conn->bind = 1;
276 return 0;
279 return -1;
282 /** special functions for kernel purposes **/
283 /* set source ip address as anycast */
284 int net_proto_udp_anycast (int fd)
286 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
288 if (!conn)
289 return -1;
291 conn->ip_source = INADDR_ANY;
293 return 0;
296 /* set source port to specified one */
297 int net_proto_udp_port (int fd, net_port port)
299 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
301 if (!conn)
302 return -1;
304 conn->port_source = port;
306 return 0;
309 int net_proto_udp_select (int readfd, int writefd, int exceptfd)
311 int fd = -1;
313 if (readfd)
314 fd = readfd;
315 else if (writefd)
316 fd = writefd;
317 else if (exceptfd)
318 fd = exceptfd;
320 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
322 if (!conn)
323 return -1;
325 /* Are some data available ? */
326 if (conn->len)
327 return 1;
329 return 0;
332 /** UDP protocol
333 * hardcore code :P
336 /* handler for received udp datagrams */
337 unsigned net_proto_udp_handler (packet_t *packet, proto_ip_t *ip, char *buf, unsigned len)
339 unsigned char ret;
340 proto_udp_t *udp = (proto_udp_t *) buf;
342 /* First check ip address and ports, that we want this data */
343 proto_udp_conn_t *conn = net_proto_udp_conn_check (ip->ip_dest, udp->port_dest, ip->ip_source, udp->port_source, &ret);
345 if (!conn && ret == 0)
346 return 1;
348 if (ret == 1)
349 conn->ip_dest = ip->ip_source;
351 /* HACK: very ugly */
352 conn->port_dest = udp->port_source;
354 /* save data to buffer; +8 because udp packet is 8byte header and then data */
355 int ret2 = net_proto_udp_read_cache (conn, buf+8, len-8);
357 if (ret2 < 1)
358 return 0;
360 return 1;
363 /* save received data to buffer */
364 int net_proto_udp_read_cache (proto_udp_conn_t *conn, char *data, unsigned len)
366 if (!data || !conn || !len)
367 return 0;
369 if (!conn->len)
370 conn->data = (char *) kmalloc (sizeof (char) * (len + 1));
371 else
372 conn->data = (char *) krealloc (conn->data, (sizeof (char) * (conn->len+len)));
374 if (!conn->data)
375 return -1;
377 memcpy (conn->data+conn->len, data, len);
379 conn->len += len;
381 //conn->data[conn->len] = '\0';
383 return conn->len;
386 int net_proto_udp_write (proto_udp_conn_t *conn, char *data, unsigned len)
388 if (!conn || !len || !data)
389 return 0;
391 mac_addr_t mac_dest;
392 unsigned get = arp_cache_get (conn->ip_dest, &mac_dest);
394 if (!get) {
395 arp_send_request (conn->netif, conn->ip_dest);
397 unsigned i = 0;
398 /* 100ms for waiting on ARP reply */
399 while (i < 100) {
400 get = arp_cache_get (conn->ip_dest, &mac_dest);
402 if (get)
403 break;
405 /* TODO: make better waiting for ARP reply */
406 timer_wait (1);
408 schedule ();
410 i ++;
413 if (!get)
414 return 0;
417 /* packet header */
418 packet_t *packet = (packet_t *) kmalloc (sizeof (packet_t));
420 if (!packet)
421 return 0;
423 memcpy (&packet->mac_source, conn->netif->dev->dev_addr, 6);
424 memcpy (&packet->mac_dest, mac_dest, 6);
425 packet->type = NET_PACKET_TYPE_IPV4;
427 /* ip layer */
428 proto_ip_t *ip = (proto_ip_t *) kmalloc (sizeof (proto_ip_t));
430 if (!ip)
431 return 0;
433 /* there are some fixed values - yeah it is horrible */
434 ip->ver = 4;
435 ip->head_len = 5;
437 ip->flags = 0;
438 ip->frag = 0;
439 ip->ttl = 64;
440 ip->checksum = 0;
441 ip->proto = NET_PROTO_IP_TYPE_UDP;
442 ip->ip_source = conn->ip_source;
443 ip->ip_dest = conn->ip_dest;
446 proto_udp_t *udp = (proto_udp_t *) kmalloc (sizeof (proto_udp_t));
448 if (!udp)
449 return 0;
451 udp->port_source = conn->port_source;
452 udp->port_dest = conn->port_dest;
454 udp->len = swap16 (8 + len);
456 udp->checksum = 0;
459 unsigned l = (ip->head_len * 4);
461 /* calculate total length of packet (udp/ip) */
462 ip->total_len = swap16 (l + 8 + len);
464 char *buf_udp = (char *) kmalloc ((9 + len) * sizeof (char));
466 if (!buf_udp)
467 return 0;
469 memcpy (buf_udp, (char *) udp, 8);
470 memcpy (buf_udp+8, data, len);
472 buf_udp[8 + len] = '\0';
474 /* calculate checksum and put it to udp header */
475 // FIXME: wrong checksum
476 proto_udp_t *udp_ = (proto_udp_t *) buf_udp;
477 udp_->checksum = checksum16_udp (conn->ip_source, conn->ip_dest, buf_udp, 8 + len); // checksum16 (buf_udp, 8 + len);
479 /* send udp header+data to ip layer */
480 unsigned ret = net_proto_ip_send (conn->netif, packet, ip, (char *) buf_udp, 8 + len);
482 kfree (buf_udp);
483 kfree (udp);
484 kfree (ip);
485 kfree (packet);
487 return ret;
491 /* Create new UDP connection */
492 int net_proto_udp_conn_add (fd_t *fd)
494 proto_udp_conn_t *conn;
496 /* alloc and init context */
497 conn = (proto_udp_conn_t *) kmalloc (sizeof (proto_udp_conn_t));
499 if (!conn) {
500 errno_set (ENOMEM);
501 return -1;
504 memset (conn, 0, sizeof (proto_udp_conn_t));
506 conn->flags = 0;
508 conn->fd = fd->id;
510 /* add into list */
511 conn->next = &proto_udp_conn_list;
512 conn->prev = proto_udp_conn_list.prev;
513 conn->prev->next = conn;
514 conn->next->prev = conn;
516 return 0;
519 /* Setup new connection */
520 int net_proto_udp_conn_set (proto_udp_conn_t *conn, netif_t *eth, net_port port_source, net_ipv4 ip_dest, net_port port_dest)
522 if (!conn || !eth)
523 return 0;
525 conn->ip_source = eth->ip;
526 conn->ip_dest = ip_dest;
528 conn->port_source = port_source;
529 conn->port_dest = port_dest;
531 conn->netif = eth;
533 conn->bind = 0;
535 conn->len = 0;
536 conn->data = 0;
538 return 1;
541 /* Delete existing connection from list */
542 unsigned net_proto_udp_conn_del (proto_udp_conn_t *conn)
544 if (!conn)
545 return 0;
547 if (conn->len)
548 kfree (conn->data);
550 conn->len = 0;
552 conn->next->prev = conn->prev;
553 conn->prev->next = conn->next;
555 kfree (conn);
557 return 1;
560 proto_udp_conn_t *net_proto_udp_conn_check (net_ipv4 ip_source, net_port port_source, net_ipv4 ip_dest, net_port port_dest, unsigned char *ret)
562 *ret = 0;
563 proto_udp_conn_t *conn = NULL;
564 proto_udp_conn_t *conn_ret = NULL;
566 if (ip_source != INADDR_BROADCAST) {
567 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
568 if (conn->ip_source == ip_source && conn->port_source == port_source) {
569 if (conn->ip_dest == ip_dest) {
570 *ret = 2;
571 return conn;
574 *ret = 1;
576 conn_ret = conn;
579 } else { /* broadcast packet */
580 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
581 if (conn->port_source == port_source && conn->port_dest == port_dest) {
582 conn->ip_source = ip_dest;
583 conn->ip_dest = ip_source;
585 *ret = 2;
586 return conn;
591 if (*ret == 1)
592 if (!conn_ret->bind) {
593 conn_ret = 0;
595 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
596 if (conn->bind) {
597 if (conn->ip_source == ip_source && conn->port_source == port_source)
598 conn_ret = conn;
603 return conn_ret;
606 proto_udp_conn_t *net_proto_udp_conn_find (int fd)
608 proto_udp_conn_t *conn = NULL;
609 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
610 if (conn->fd == fd)
611 return conn;
614 return 0;
617 /* init of udp protocol */
618 unsigned init_net_proto_udp ()
620 proto_udp_conn_list.next = &proto_udp_conn_list;
621 proto_udp_conn_list.prev = &proto_udp_conn_list;
623 return 1;