Added support for broadcast packets into UDPv4
[ZeXOS.git] / kernel / core / net / udp.c
blobb891c68bc46690f90f28231b0e0944ac74038802
1 /*
2 * ZeX/OS
3 * Copyright (C) 2008 Tomas 'ZeXx86' Jedrzejek (zexx86@gmail.com)
4 * Copyright (C) 2009 Tomas 'ZeXx86' Jedrzejek (zexx86@gmail.com)
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include <system.h>
22 #include <string.h>
23 #include <dev.h>
24 #include <net/eth.h>
25 #include <net/net.h>
26 #include <net/if.h>
27 #include <net/ip.h>
28 #include <net/packet.h>
29 #include <net/udp.h>
30 #include <net/socket.h>
31 #include <fd.h>
32 #include <errno.h>
35 extern netif_t netif_list;
36 proto_udp_conn_t proto_udp_conn_list;
38 static net_port proto_udp_client_port;
39 static unsigned proto_udp_fd;
41 /* prototypes */
42 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);
43 int net_proto_udp_read_cache (proto_udp_conn_t *conn, char *data, unsigned len);
44 int net_proto_udp_write (proto_udp_conn_t *conn, char *data, unsigned len);
45 proto_udp_conn_t *net_proto_udp_conn_find (int fd);
46 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);
47 unsigned net_proto_udp_conn_del (proto_udp_conn_t *conn);
48 int net_proto_udp_conn_add ();
50 /** UDP protocol
51 * User-friendly socket functions
53 int net_proto_udp_socket (fd_t *fd)
55 return net_proto_udp_conn_add (fd);
58 int net_proto_udp_connect (int fd, sockaddr_in *addr)
60 int ret = -1;
62 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
64 if (!conn)
65 return -1;
67 netif_t *netif;
68 for (netif = netif_list.next; netif != &netif_list; netif = netif->next) {
69 ret = net_proto_udp_conn_set (conn, netif, swap16 (proto_udp_client_port ++), addr->sin_addr, addr->sin_port);
71 /* TODO: connect timeout */
73 /* blocking mode */
74 if (!(conn->flags & O_NONBLOCK)) {
75 if (!ret)
76 return -1;
77 } else
78 /* non-blocking mode */
79 return -1;
81 ret = 0;
84 return ret;
87 int net_proto_udp_send (int fd, char *msg, unsigned size)
89 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
91 if (!conn)
92 return -1;
94 int ret = net_proto_udp_write (conn, msg, size);
96 if (ret) {
97 /* blocking mode */
98 if (!(conn->flags & O_NONBLOCK)) {
100 } else {
101 /* non-blocking mode */
102 /* TODO: ? */
107 return size;
110 int net_proto_udp_sendto (int fd, char *msg, unsigned size, sockaddr_in *to)
112 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
114 if (!conn)
115 return -1;
117 //int ip = conn->ip_dest;
119 /*net_proto_ip_print (conn->ip_dest);
120 kprintf (" | ");
121 net_proto_ip_print (conn->ip_source);*/
123 //conn->ip_source = to->sin_addr;
125 int ret = net_proto_udp_write (conn, msg, size);
127 //conn->ip_dest = ip;
129 if (ret) {
130 /* blocking mode */
131 if (!(conn->flags & O_NONBLOCK)) {
133 } else {
134 /* non-blocking mode */
135 /* TODO: ? */
140 return size;
143 extern unsigned long timer_ticks;
144 int net_proto_udp_recv (int fd, char *msg, unsigned size)
146 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
148 if (!conn)
149 return -3;
151 if (!msg)
152 return -4;
154 int ret = 0;
156 /* blocking mode */
157 if (!(conn->flags & O_NONBLOCK)) {
158 unsigned long stime = timer_ticks;
160 while (!conn->len) {
161 if ((stime+10000) < timer_ticks)
162 return 0;
164 schedule ();
166 } else {
167 if (!conn->len)
168 return 0;
171 if (!conn->data)
172 return -5;
174 if (conn->len >= size)
175 return -2;
177 ret = conn->len;
179 if (conn->len > 0)
180 memcpy (msg, conn->data, conn->len);
182 conn->len = 0;
184 kfree (conn->data);
186 return ret;
189 int net_proto_udp_recvfrom (int fd, char *msg, unsigned size, sockaddr_in *from)
191 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
193 if (!conn)
194 return -3;
196 if (!msg)
197 return -4;
199 int ret = 0;
201 //int ip = conn->ip_dest;
203 from->sin_addr = conn->ip_dest;
204 //kprintf ("recvfrom -> ip: 0x%x\n", from->sin_addr);
206 /* blocking mode */
207 if (!(conn->flags & O_NONBLOCK)) {
208 /* TODO: timeout */
209 while (!conn->len)
210 schedule ();
211 } else {
212 if (!conn->len) {
213 //conn->ip_dest = ip;
214 return 0;
218 //conn->ip_dest = ip; // return old ip address back to structure
220 if (!conn->data)
221 return -5;
223 if (conn->len >= size)
224 return -2;
226 ret = conn->len;
228 if (conn->len > 0)
229 memcpy (msg, conn->data, conn->len);
231 conn->len = 0;
233 kfree (conn->data);
235 return ret;
238 int net_proto_udp_close (int fd)
240 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
242 if (!conn)
243 return -1;
245 int ret = net_proto_udp_conn_del (conn);
247 return ret;
250 int net_proto_udp_fcntl (int fd, int cmd, long arg)
252 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
254 if (!conn)
255 return -1;
257 switch (cmd) {
258 case F_GETFL:
259 return conn->flags;
260 case F_SETFL:
261 conn->flags = arg;
262 return 0;
265 return -1;
268 int net_proto_udp_bind (int fd, sockaddr_in *addr, socklen_t len)
270 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
272 if (!conn)
273 return -1;
275 netif_t *netif;
276 for (netif = netif_list.next; netif != &netif_list; netif = netif->next) {
277 net_proto_udp_conn_set (conn, netif, addr->sin_port, 0, 0);
278 conn->bind = 1;
279 return 0;
282 return -1;
285 /** special functions for kernel purposes **/
286 /* set source ip address as anycast */
287 int net_proto_udp_anycast (int fd)
289 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
291 if (!conn)
292 return -1;
294 conn->ip_source = INADDR_ANY;
296 return 0;
299 /* set source port to specified one */
300 int net_proto_udp_port (int fd, net_port port)
302 proto_udp_conn_t *conn = net_proto_udp_conn_find (fd);
304 if (!conn)
305 return -1;
307 conn->port_source = port;
309 return 0;
312 /** UDP protocol
313 * hardcore code :P
316 /* handler for received udp datagrams */
317 unsigned net_proto_udp_handler (packet_t *packet, proto_ip_t *ip, char *buf, unsigned len)
319 unsigned char ret;
320 proto_udp_t *udp = (proto_udp_t *) buf;
322 /* First check ip address and ports, that we want this data */
323 proto_udp_conn_t *conn = net_proto_udp_conn_check (ip->ip_dest, udp->port_dest, ip->ip_source, udp->port_source, &ret);
325 if (!conn && ret == 0)
326 return 1;
328 if (ret == 1)
329 conn->ip_dest = ip->ip_source;
331 /* HACK: very ugly */
332 conn->port_dest = udp->port_source;
334 /* save data to buffer; +8 because udp packet is 8byte header and then data */
335 int ret2 = net_proto_udp_read_cache (conn, buf+8, len-8);
337 if (ret2 < 1)
338 return 0;
340 return 1;
343 /* save received data to buffer */
344 int net_proto_udp_read_cache (proto_udp_conn_t *conn, char *data, unsigned len)
346 if (!data || !conn || !len)
347 return 0;
349 if (!conn->len)
350 conn->data = (char *) kmalloc (sizeof (char) * (len + 1));
351 else
352 conn->data = (char *) krealloc (conn->data, (sizeof (char) * (conn->len+len)));
354 if (!conn->data)
355 return -1;
357 memcpy (conn->data+conn->len, data, len);
359 conn->len += len;
361 //conn->data[conn->len] = '\0';
363 return conn->len;
366 int net_proto_udp_write (proto_udp_conn_t *conn, char *data, unsigned len)
368 if (!conn || !len || !data)
369 return 0;
371 mac_addr_t mac_dest;
372 unsigned get = arp_cache_get (conn->ip_dest, &mac_dest);
374 if (!get) {
375 arp_send_request (conn->netif, conn->ip_dest);
377 unsigned i = 0;
378 /* 100ms for waiting on ARP reply */
379 while (i < 100) {
380 get = arp_cache_get (conn->ip_dest, &mac_dest);
382 if (get)
383 break;
385 /* TODO: make better waiting for ARP reply */
386 timer_wait (1);
388 schedule ();
390 i ++;
393 if (!get)
394 return 0;
397 /* packet header */
398 packet_t *packet = (packet_t *) kmalloc (sizeof (packet_t));
400 if (!packet)
401 return 0;
403 memcpy (&packet->mac_source, conn->netif->dev->dev_addr, 6);
404 memcpy (&packet->mac_dest, mac_dest, 6);
405 packet->type = NET_PACKET_TYPE_IPV4;
407 /* ip layer */
408 proto_ip_t *ip = (proto_ip_t *) kmalloc (sizeof (proto_ip_t));
410 if (!ip)
411 return 0;
413 /* there are some fixed values - yeah it is horrible */
414 ip->ver = 4;
415 ip->head_len = 5;
417 ip->flags = 0;
418 ip->frag = 0;
419 ip->ttl = 64;
420 ip->checksum = 0;
421 ip->proto = NET_PROTO_IP_TYPE_UDP;
422 ip->ip_source = conn->ip_source;
423 ip->ip_dest = conn->ip_dest;
426 proto_udp_t *udp = (proto_udp_t *) kmalloc (sizeof (proto_udp_t));
428 if (!udp)
429 return 0;
431 udp->port_source = conn->port_source;
432 udp->port_dest = conn->port_dest;
434 udp->len = swap16 (8 + len);
436 udp->checksum = 0;
439 unsigned l = (ip->head_len * 4);
441 /* calculate total length of packet (udp/ip) */
442 ip->total_len = swap16 (l + 8 + len);
444 char *buf_udp = (char *) kmalloc ((9 + len) * sizeof (char));
446 if (!buf_udp)
447 return 0;
449 memcpy (buf_udp, (char *) udp, 8);
450 memcpy (buf_udp+8, data, len);
452 buf_udp[8 + len] = '\0';
454 /* calculate checksum and put it to udp header */
455 // FIXME: wrong checksum
456 proto_udp_t *udp_ = (proto_udp_t *) buf_udp;
457 udp_->checksum = checksum16_udp (conn->ip_source, conn->ip_dest, buf_udp, 8 + len); // checksum16 (buf_udp, 8 + len);
459 /* send udp header+data to ip layer */
460 unsigned ret = net_proto_ip_send (conn->netif, packet, ip, (char *) buf_udp, 8 + len);
462 kfree (buf_udp);
463 kfree (udp);
464 kfree (ip);
465 kfree (packet);
467 return ret;
471 /* Create new UDP connection */
472 int net_proto_udp_conn_add (fd_t *fd)
474 proto_udp_conn_t *conn;
476 /* alloc and init context */
477 conn = (proto_udp_conn_t *) kmalloc (sizeof (proto_udp_conn_t));
479 if (!conn) {
480 errno_set (ENOMEM);
481 return -1;
484 memset (conn, 0, sizeof (proto_udp_conn_t));
486 conn->flags = 0;
488 conn->fd = fd->id;
490 /* add into list */
491 conn->next = &proto_udp_conn_list;
492 conn->prev = proto_udp_conn_list.prev;
493 conn->prev->next = conn;
494 conn->next->prev = conn;
496 return 0;
499 /* Setup new connection */
500 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)
502 if (!conn || !eth)
503 return 0;
505 conn->ip_source = eth->ip;
506 conn->ip_dest = ip_dest;
508 conn->port_source = port_source;
509 conn->port_dest = port_dest;
511 conn->netif = eth;
513 conn->bind = 0;
515 conn->len = 0;
516 conn->data = 0;
518 return 1;
521 /* Delete existing connection from list */
522 unsigned net_proto_udp_conn_del (proto_udp_conn_t *conn)
524 if (!conn)
525 return 0;
527 if (conn->len)
528 kfree (conn->data);
530 conn->len = 0;
532 conn->next->prev = conn->prev;
533 conn->prev->next = conn->next;
535 kfree (conn);
537 return 1;
540 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)
542 *ret = 0;
543 proto_udp_conn_t *conn = NULL;
544 proto_udp_conn_t *conn_ret = NULL;
546 if (conn->ip_dest != INADDR_BROADCAST) {
547 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
548 if (conn->ip_source == ip_source && conn->port_source == port_source) {
549 if (conn->ip_dest == ip_dest) {
550 *ret = 2;
551 return conn;
554 *ret = 1;
556 conn_ret = conn;
559 } else { /* broadcast packet */
560 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
561 if (conn->port_source == port_source && conn->port_dest == port_dest) {
562 conn->ip_source = ip_dest;
563 conn->ip_dest = ip_source;
565 *ret = 2;
566 return conn;
571 if (*ret == 1)
572 if (!conn_ret->bind) {
573 conn_ret = 0;
575 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
576 if (conn->bind) {
577 if (conn->ip_source == ip_source && conn->port_source == port_source)
578 conn_ret = conn;
583 return conn_ret;
586 proto_udp_conn_t *net_proto_udp_conn_find (int fd)
588 proto_udp_conn_t *conn = NULL;
589 for (conn = proto_udp_conn_list.next; conn != &proto_udp_conn_list; conn = conn->next) {
590 if (conn->fd == fd)
591 return conn;
594 return 0;
597 /* init of udp protocol */
598 unsigned init_net_proto_udp ()
600 proto_udp_conn_list.next = &proto_udp_conn_list;
601 proto_udp_conn_list.prev = &proto_udp_conn_list;
603 /* base udp client port, which is used for client's use */
604 proto_udp_client_port = 1024;
605 return 1;