New developer version 0.6.8; added select () function; added demonstrating example...
[ZeXOS.git] / kernel / core / net / udp6.c
blob2ffd39047755f23c7d7c98d40b72e836e1b87afc
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>
36 extern netif_t netif_list;
37 proto_udp6_conn_t proto_udp6_conn_list;
39 /* prototypes */
40 proto_udp6_conn_t *net_proto_udp6_conn_check (net_ipv6 ip_source, net_port port_source, net_ipv6 ip_dest, net_port port_dest, unsigned char *ret);
41 int net_proto_udp6_read_cache (proto_udp6_conn_t *conn, char *data, unsigned len);
42 int net_proto_udp6_write (proto_udp6_conn_t *conn, char *data, unsigned len);
43 proto_udp6_conn_t *net_proto_udp6_conn_find (int fd);
44 int net_proto_udp6_conn_set (proto_udp6_conn_t *conn, netif_t *eth, net_port port_source, net_ipv6 ip_dest, net_port port_dest);
45 unsigned net_proto_udp6_conn_del (proto_udp6_conn_t *conn);
46 int net_proto_udp6_conn_add ();
48 /** UDPv6 protocol
49 * User-friendly socket functions
51 int net_proto_udp6_socket (fd_t *fd)
53 return net_proto_udp6_conn_add (fd);
56 int net_proto_udp6_connect (int fd, sockaddr_in6 *addr)
58 int ret = -1;
60 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
62 if (!conn)
63 return -1;
65 netif_t *netif;
66 for (netif = netif_list.next; netif != &netif_list; netif = netif->next) {
67 ret = net_proto_udp6_conn_set (conn, netif, swap16 (netif_port_get ()), addr->sin6_addr, addr->sin6_port);
69 /* TODO: connect timeout */
71 /* blocking mode */
72 if (!(conn->flags & O_NONBLOCK)) {
73 if (!ret)
74 return -1;
75 } else
76 /* non-blocking mode */
77 return -1;
79 ret = 0;
82 return ret;
85 int net_proto_udp6_send (int fd, char *msg, unsigned size)
87 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
89 if (!conn)
90 return -1;
92 int ret = net_proto_udp6_write (conn, msg, size);
94 if (ret) {
95 /* blocking mode */
96 if (!(conn->flags & O_NONBLOCK)) {
98 } else {
99 /* non-blocking mode */
100 /* TODO: ? */
105 return size;
108 int net_proto_udp6_sendto (int fd, char *msg, unsigned size, sockaddr_in6 *to)
110 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
112 if (!conn)
113 return -1;
115 //int ip = conn->ip_dest;
117 /*net_proto_ip_print (conn->ip_dest);
118 kprintf (" | ");
119 net_proto_ip_print (conn->ip_source);*/
121 //conn->ip_source = to->sin6_addr;
123 int ret = net_proto_udp6_write (conn, msg, size);
125 //conn->ip_dest = ip;
127 if (ret) {
128 /* blocking mode */
129 if (!(conn->flags & O_NONBLOCK)) {
131 } else {
132 /* non-blocking mode */
133 /* TODO: ? */
138 return size;
141 extern unsigned long timer_ticks;
142 int net_proto_udp6_recv (int fd, char *msg, unsigned size)
144 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
146 if (!conn)
147 return -3;
149 if (!msg)
150 return -4;
152 int ret = 0;
154 /* blocking mode */
155 if (!(conn->flags & O_NONBLOCK)) {
156 unsigned long stime = timer_ticks;
158 while (!conn->len) {
159 if ((stime+10000) < timer_ticks)
160 return 0;
162 schedule ();
164 } else {
165 if (!conn->len)
166 return 0;
169 if (!conn->data)
170 return -5;
172 if (conn->len >= size)
173 return -2;
175 ret = conn->len;
177 if (conn->len > 0)
178 memcpy (msg, conn->data, conn->len);
180 conn->len = 0;
182 kfree (conn->data);
184 return ret;
187 int net_proto_udp6_recvfrom (int fd, char *msg, unsigned size, sockaddr_in6 *from)
189 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
191 if (!conn)
192 return -3;
194 if (!msg)
195 return -4;
197 int ret = 0;
199 //int ip = conn->ip_dest;
201 memcpy (from->sin6_addr, (void *) conn->ip_dest, sizeof (net_ipv6));
202 //kprintf ("recvfrom -> ip: 0x%x\n", from->sin6_addr);
204 /* blocking mode */
205 if (!(conn->flags & O_NONBLOCK)) {
206 /* TODO: timeout */
207 while (!conn->len)
208 schedule ();
209 } else {
210 if (!conn->len) {
211 //conn->ip_dest = ip;
212 return 0;
216 //conn->ip_dest = ip; // return old ip address back to structure
218 if (!conn->data)
219 return -5;
221 if (conn->len >= size)
222 return -2;
224 ret = conn->len;
226 if (conn->len > 0)
227 memcpy (msg, conn->data, conn->len);
229 conn->len = 0;
231 kfree (conn->data);
233 return ret;
236 int net_proto_udp6_close (int fd)
238 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
240 if (!conn)
241 return -1;
243 int ret = net_proto_udp6_conn_del (conn);
245 return ret;
248 int net_proto_udp6_fcntl (int fd, int cmd, long arg)
250 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
252 if (!conn)
253 return -1;
255 switch (cmd) {
256 case F_GETFL:
257 return conn->flags;
258 case F_SETFL:
259 conn->flags = arg;
260 return 0;
263 return -1;
266 int net_proto_udp6_bind (int fd, sockaddr_in6 *addr, socklen_t len)
268 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
270 if (!conn)
271 return -1;
273 netif_t *netif;
274 for (netif = netif_list.next; netif != &netif_list; netif = netif->next) {
275 net_proto_udp6_conn_set (conn, netif, addr->sin6_port, 0, 0);
276 conn->bind = 1;
277 return 0;
280 return -1;
283 int net_proto_udp6_select (int readfd, int writefd, int exceptfd)
285 int fd = -1;
287 if (readfd)
288 fd = readfd;
289 else if (writefd)
290 fd = writefd;
291 else if (exceptfd)
292 fd = exceptfd;
294 proto_udp6_conn_t *conn = net_proto_udp6_conn_find (fd);
296 if (!conn)
297 return -1;
299 /* Are some data available ? */
300 if (conn->len)
301 return 1;
303 return 0;
307 /** UDP protocol
308 * hardcore code :P
311 /* handler for received udp datagrams */
312 unsigned net_proto_udp6_handler (packet_t *packet, proto_ipv6_t *ip, char *buf, unsigned len)
314 unsigned char ret;
315 proto_udp_t *udp = (proto_udp_t *) buf;
317 /* First check ip address and ports, that we want this data */
318 proto_udp6_conn_t *conn = net_proto_udp6_conn_check (ip->ip_dest, udp->port_dest, ip->ip_source, udp->port_source, &ret);
320 if (!conn && ret == 0)
321 return 1;
323 if (ret == 1)
324 memcpy (conn->ip_dest, (void *) ip->ip_source, sizeof (net_ipv6));
326 /* HACK: very ugly */
327 conn->port_dest = udp->port_source;
329 /* save data to buffer; +8 because udp packet is 8byte header and then data */
330 int ret2 = net_proto_udp6_read_cache (conn, buf+8, len-8);
332 if (ret2 < 1)
333 return 0;
335 return 1;
338 /* save received data to buffer */
339 int net_proto_udp6_read_cache (proto_udp6_conn_t *conn, char *data, unsigned len)
341 if (!data || !conn || !len)
342 return 0;
344 if (!conn->len)
345 conn->data = (char *) kmalloc (sizeof (char) * (len + 1));
346 else
347 conn->data = (char *) krealloc (conn->data, (sizeof (char) * (conn->len+len)));
349 if (!conn->data)
350 return -1;
352 memcpy (conn->data+conn->len, data, len);
354 conn->len += len;
356 //conn->data[conn->len] = '\0';
358 return conn->len;
361 int net_proto_udp6_write (proto_udp6_conn_t *conn, char *data, unsigned len)
363 if (!conn || !len || !data)
364 return 0;
366 mac_addr_t mac_dest;
367 if (!tun6_addr_get ()) {
368 unsigned get = ndp_cache_get (conn->ip_dest, &mac_dest);
370 if (!get) {
371 ndp_send_nbrequest (conn->netif, conn->ip_dest);
373 unsigned i = 0;
374 /* 100ms for waiting on ARP reply */
375 while (i < 100) {
376 get = ndp_cache_get (conn->ip_dest, &mac_dest);
378 if (get)
379 break;
381 /* TODO: make better waiting for ARP reply */
382 timer_wait (1);
384 schedule ();
386 i ++;
389 if (!get)
390 return 0;
392 } else {
393 /* IPv6 tunnel stuff */
394 net_ipv4 tunnel = tun6_addr_get ();
395 unsigned get = arp_cache_get (tunnel, &mac_dest);
397 if (!get) {
398 arp_send_request (conn->netif, tunnel);
400 unsigned i = 0;
401 /* 100ms for waiting on ARP reply */
402 while (i < 100) {
403 get = arp_cache_get (tunnel, &mac_dest);
405 if (get)
406 break;
408 /* TODO: make better waiting for ARP reply */
409 timer_wait (1);
411 schedule ();
413 i ++;
416 if (!get)
417 return 0;
421 /* packet header */
422 packet_t *packet = (packet_t *) kmalloc (sizeof (packet_t));
424 if (!packet)
425 return 0;
427 memcpy (&packet->mac_source, conn->netif->dev->dev_addr, 6);
428 memcpy (&packet->mac_dest, mac_dest, 6);
429 packet->type = NET_PACKET_TYPE_IPV6;
431 /* ip layer */
432 proto_ipv6_t *ip = (proto_ipv6_t *) kmalloc (sizeof (proto_ipv6_t));
434 if (!ip)
435 return 0;
437 /* there are some fixed values - yeah it is horrible */
438 ip->ver = 0x60;
439 ip->tclass = 0x0;
440 ip->flabel = 0x0;
441 ip->pl_len = swap16 (len);
442 ip->nhead = NET_PROTO_IP_TYPE_UDP6;
443 ip->hop = 0xff;
444 memcpy (ip->ip_source, (void *) conn->ip_source, sizeof (net_ipv6));
445 memcpy (ip->ip_dest, (void *) conn->ip_dest, sizeof (net_ipv6));
448 proto_udp_t *udp = (proto_udp_t *) kmalloc (sizeof (proto_udp_t));
450 if (!udp)
451 return 0;
453 udp->port_source = conn->port_source;
454 udp->port_dest = conn->port_dest;
456 udp->len = ip->pl_len;
458 udp->checksum = 0;
461 /* calculate total length of packet (udp/ip) */
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 proto_udp_t *udp_ = (proto_udp_t *) buf_udp;
476 udp_->checksum = checksum16_ipv6 (conn->ip_source, conn->ip_dest, buf_udp, 8 + len, NET_PROTO_IP_TYPE_UDP6);
478 unsigned ret = 0;
480 /* send udp header+data to ip layer */
481 if (!tun6_addr_get ())
482 ret = net_proto_ipv6_send (conn->netif, packet, ip, (char *) buf_udp, 8 + len);
483 else
484 ret = net_proto_tun6_send (conn->netif, packet, ip, (char *) buf_udp, 8 + len);
486 kfree (buf_udp);
487 kfree (udp);
488 kfree (ip);
489 kfree (packet);
491 return ret;
495 /* Create new UDP connection */
496 int net_proto_udp6_conn_add (fd_t *fd)
498 proto_udp6_conn_t *conn;
500 /* alloc and init context */
501 conn = (proto_udp6_conn_t *) kmalloc (sizeof (proto_udp6_conn_t));
503 if (!conn) {
504 errno_set (ENOMEM);
505 return -1;
508 memset (conn, 0, sizeof (proto_udp6_conn_t));
510 conn->flags = 0;
512 conn->fd = fd->id;
514 /* add into list */
515 conn->next = &proto_udp6_conn_list;
516 conn->prev = proto_udp6_conn_list.prev;
517 conn->prev->next = conn;
518 conn->next->prev = conn;
520 return 0;
523 /* Setup new connection */
524 int net_proto_udp6_conn_set (proto_udp6_conn_t *conn, netif_t *eth, net_port port_source, net_ipv6 ip_dest, net_port port_dest)
526 if (!conn || !eth)
527 return 0;
529 memcpy (conn->ip_source, (void *) eth->ipv6, sizeof (net_ipv6));
530 memcpy (conn->ip_dest, (void *) ip_dest, sizeof (net_ipv6));
532 conn->port_source = port_source;
533 conn->port_dest = port_dest;
535 conn->netif = eth;
537 conn->bind = 0;
539 conn->len = 0;
540 conn->data = 0;
542 return 1;
545 /* Delete existing connection from list */
546 unsigned net_proto_udp6_conn_del (proto_udp6_conn_t *conn)
548 if (!conn)
549 return 0;
551 if (conn->len)
552 kfree (conn->data);
554 conn->len = 0;
556 conn->next->prev = conn->prev;
557 conn->prev->next = conn->next;
559 kfree (conn);
561 return 1;
564 proto_udp6_conn_t *net_proto_udp6_conn_check (net_ipv6 ip_source, net_port port_source, net_ipv6 ip_dest, net_port port_dest, unsigned char *ret)
566 *ret = 0;
567 proto_udp6_conn_t *conn = NULL;
568 proto_udp6_conn_t *conn_ret = NULL;
570 for (conn = proto_udp6_conn_list.next; conn != &proto_udp6_conn_list; conn = conn->next) {
571 if (conn->ip_source == ip_source && conn->port_source == port_source) {
572 if (conn->ip_dest == ip_dest /*&& conn->port_dest == port_dest*/) {
573 *ret = 2;
574 return conn;
577 *ret = 1;
579 conn_ret = conn;
583 if (*ret == 1)
584 if (!conn_ret->bind) {
585 conn_ret = 0;
587 for (conn = proto_udp6_conn_list.next; conn != &proto_udp6_conn_list; conn = conn->next) {
588 if (conn->bind) {
589 if (conn->ip_source == ip_source && conn->port_source == port_source)
590 conn_ret = conn;
595 return conn_ret;
598 proto_udp6_conn_t *net_proto_udp6_conn_find (int fd)
600 proto_udp6_conn_t *conn = NULL;
601 for (conn = proto_udp6_conn_list.next; conn != &proto_udp6_conn_list; conn = conn->next) {
602 if (conn->fd == fd)
603 return conn;
606 return 0;
609 /* init of udpv6 protocol */
610 unsigned init_net_proto_udp6 ()
612 proto_udp6_conn_list.next = &proto_udp6_conn_list;
613 proto_udp6_conn_list.prev = &proto_udp6_conn_list;
615 return 1;