From 5c60297a1eaab7b10d6f584ba329493a41b812d0 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Sun, 6 Mar 2011 15:53:58 +0100 Subject: Restructure code, moving most code out of packet.c Also, move copyright notice out of COPYING and into every file. --- lib/udp.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 lib/udp.c (limited to 'lib/udp.c') diff --git a/lib/udp.c b/lib/udp.c new file mode 100644 index 0000000..3573033 --- /dev/null +++ b/lib/udp.c @@ -0,0 +1,85 @@ +/* Copyright 2011 NORDUnet A/S. All rights reserved. + See the file COPYING for licensing information. */ + +#if defined HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include "debug.h" +#include "event.h" +#include "compat.h" +#include "udp.h" + +/* Callback for conn->wev and conn->rev. FIXME: Rename. */ +static void +_evcb (evutil_socket_t fd, short what, void *user_data) +{ + //rs_debug (("%s: fd=%d what=0x%x\n", __func__, fd, what)); + if (what & EV_TIMEOUT) + { + struct rs_connection *conn = (struct rs_connection *) user_data; + assert (conn); + conn->is_connecting = 0; + rs_debug (("%s: UDP timeout NYI", __func__)); + } + else if (what & EV_READ) + { + struct rs_connection *conn = (struct rs_connection *) user_data; + assert (conn); + /* read a single UDP packet and stick it in a new struct + rs_packet */ + + rs_debug (("%s: UDP read NYI", __func__)); + } + else if (what & EV_WRITE) + { + struct rs_packet *pkt = (struct rs_packet *) user_data; + assert (pkt); + /* Socket ready for writing, possibly as a result of a + successful connect. */ + if (!pkt->conn->is_connected) + event_on_connect (pkt->conn, pkt); + if (pkt->conn->out_queue) + { + /* Send one packet, the first. */ + ssize_t r = 0; + struct rs_packet *p = pkt->conn->out_queue; + + assert (p->rpkt); + assert (p->rpkt->data); + r = compat_send (fd, p->rpkt->data, p->rpkt->data_len, 0); + if (r == -1) + { + int sockerr = evutil_socket_geterror (p->conn->fd); + if (sockerr != EAGAIN) + rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__, + "%d: send: %d (%s)", fd, sockerr, + evutil_socket_error_to_string (sockerr)); + return; /* Don't unlink packet. */ + } + pkt->conn->out_queue = p->next; + } + } +} + +int +udp_init (struct rs_connection *conn, struct rs_packet *pkt) +{ + assert (!conn->bev); + + conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn); + conn->wev = event_new (conn->evb, conn->fd, EV_WRITE|EV_PERSIST, _evcb, pkt); + if (!conn->rev || !conn->wev) + { + if (conn->rev) + event_free (conn->rev); + /* ENOMEM _or_ EINVAL but EINVAL only if we use EV_SIGNAL, at + least for now (libevent-2.0.5). */ + return rs_err_conn_push_fl (conn, RSE_NOMEM, __FILE__, __LINE__, NULL); + } + return RSE_OK; +} -- cgit v1.1 From 7385db38d82b26736ba56bc7aec4c79acc420824 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Wed, 9 Mar 2011 10:10:04 +0100 Subject: Don't make UDP write event persistant. rs_packet_send() adds the event again when there's a packet to send and the write callback does the same if it doesn't drain the queue. --- lib/udp.c | 68 ++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 22 deletions(-) (limited to 'lib/udp.c') diff --git a/lib/udp.c b/lib/udp.c index 3573033..c602cbd 100644 --- a/lib/udp.c +++ b/lib/udp.c @@ -14,6 +14,44 @@ #include "compat.h" #include "udp.h" +/* Send one packet, the first in queue. */ +static void +_send (struct rs_connection *conn, int fd) +{ + ssize_t r = 0; + struct rs_packet *pkt = conn->out_queue; + + assert (pkt->rpkt); + assert (pkt->rpkt->data); + + /* Send. */ + r = compat_send (fd, pkt->rpkt->data, pkt->rpkt->data_len, 0); + if (r == -1) + { + int sockerr = evutil_socket_geterror (pkt->conn->fd); + if (sockerr != EAGAIN) + rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, + "%d: send: %d (%s)", fd, sockerr, + evutil_socket_error_to_string (sockerr)); + return; /* Don't unlink packet. */ + } + + /* Unlink the packet. */ + conn->out_queue = pkt->next; + + /* If there are more packets in queue, add the write event again. */ + if (pkt->conn->out_queue) + { + r = event_add (pkt->conn->wev, NULL); + if (r < 0) + { + rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, + "event_add: %s", evutil_gai_strerror (r)); + return; + } + } +} + /* Callback for conn->wev and conn->rev. FIXME: Rename. */ static void _evcb (evutil_socket_t fd, short what, void *user_data) @@ -33,36 +71,22 @@ _evcb (evutil_socket_t fd, short what, void *user_data) /* read a single UDP packet and stick it in a new struct rs_packet */ + /* TODO: Verify that reception of an unsolicited response packet + results in connection being closed. */ rs_debug (("%s: UDP read NYI", __func__)); + + /* TODO: delete retransmit timer */ } else if (what & EV_WRITE) { struct rs_packet *pkt = (struct rs_packet *) user_data; assert (pkt); - /* Socket ready for writing, possibly as a result of a - successful connect. */ + if (!pkt->conn->is_connected) event_on_connect (pkt->conn, pkt); - if (pkt->conn->out_queue) - { - /* Send one packet, the first. */ - ssize_t r = 0; - struct rs_packet *p = pkt->conn->out_queue; - assert (p->rpkt); - assert (p->rpkt->data); - r = compat_send (fd, p->rpkt->data, p->rpkt->data_len, 0); - if (r == -1) - { - int sockerr = evutil_socket_geterror (p->conn->fd); - if (sockerr != EAGAIN) - rs_err_conn_push_fl (p->conn, RSE_SOCKERR, __FILE__, __LINE__, - "%d: send: %d (%s)", fd, sockerr, - evutil_socket_error_to_string (sockerr)); - return; /* Don't unlink packet. */ - } - pkt->conn->out_queue = p->next; - } + if (pkt->conn->out_queue) + _send (pkt->conn, fd); } } @@ -72,7 +96,7 @@ udp_init (struct rs_connection *conn, struct rs_packet *pkt) assert (!conn->bev); conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn); - conn->wev = event_new (conn->evb, conn->fd, EV_WRITE|EV_PERSIST, _evcb, pkt); + conn->wev = event_new (conn->evb, conn->fd, EV_WRITE, _evcb, pkt); if (!conn->rev || !conn->wev) { if (conn->rev) -- cgit v1.1 From ce4d6dfe1728e5633a8f49fc4b16c36df0d23521 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Wed, 9 Mar 2011 10:18:06 +0100 Subject: Add retransmission timer support (UDP). --- lib/udp.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'lib/udp.c') diff --git a/lib/udp.c b/lib/udp.c index c602cbd..ac4e487 100644 --- a/lib/udp.c +++ b/lib/udp.c @@ -107,3 +107,18 @@ udp_init (struct rs_connection *conn, struct rs_packet *pkt) } return RSE_OK; } + +int +udp_init_retransmit_timer (struct rs_connection *conn) +{ + assert (conn); + + if (conn->tev) + event_free (conn->tev); + conn->tev = evtimer_new (conn->evb, event_retransmit_timeout_cb, conn); + if (!conn->tev) + return rs_err_conn_push_fl (conn, RSE_EVENT, __FILE__, __LINE__, + "evtimer_new"); + + return RSE_OK; +} -- cgit v1.1 From 0a7d803b9aa40512cf0f0c574d397ccba3ff1d13 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Wed, 9 Mar 2011 23:09:26 +0100 Subject: Get UDP working. For UDP, activate retransmit timer before receiving rather than sending makes the event loop break nicely after sending a message (which is important for blocking mode). Not quite sure that this is really accurate wrt to retransmission timing though but it should do for now. For UDP, set the user_data member for the read callback in rs_conn_receive_packet -- the one from udp_init() doesn't do much good now. For UDP, implement receiving message. Add compat_recv(). --- lib/udp.c | 114 +++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 84 insertions(+), 30 deletions(-) (limited to 'lib/udp.c') diff --git a/lib/udp.c b/lib/udp.c index ac4e487..19968b3 100644 --- a/lib/udp.c +++ b/lib/udp.c @@ -6,6 +6,8 @@ #endif #include +#include +#include #include #include #include @@ -15,7 +17,7 @@ #include "udp.h" /* Send one packet, the first in queue. */ -static void +static int _send (struct rs_connection *conn, int fd) { ssize_t r = 0; @@ -30,12 +32,12 @@ _send (struct rs_connection *conn, int fd) { int sockerr = evutil_socket_geterror (pkt->conn->fd); if (sockerr != EAGAIN) - rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, - "%d: send: %d (%s)", fd, sockerr, - evutil_socket_error_to_string (sockerr)); - return; /* Don't unlink packet. */ + return rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, + "%d: send: %d (%s)", fd, sockerr, + evutil_socket_error_to_string (sockerr)); } + assert (r == pkt->rpkt->data_len); /* Unlink the packet. */ conn->out_queue = pkt->next; @@ -44,38 +46,83 @@ _send (struct rs_connection *conn, int fd) { r = event_add (pkt->conn->wev, NULL); if (r < 0) - { - rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, - "event_add: %s", evutil_gai_strerror (r)); - return; - } + return rs_err_conn_push_fl (pkt->conn, RSE_EVENT, __FILE__, __LINE__, + "event_add: %s", evutil_gai_strerror (r)); + rs_debug (("%s: re-adding the write event\n", __func__)); } + + return RSE_OK; } -/* Callback for conn->wev and conn->rev. FIXME: Rename. */ +/* Callback for conn->wev and conn->rev. FIXME: Rename. + + USER_DATA contains connection for EV_READ and a packet for + EV_WRITE. This is because we don't have a connect/establish entry + point at the user level -- send implies connect so when we're + connected we need the packet to send. */ static void _evcb (evutil_socket_t fd, short what, void *user_data) { - //rs_debug (("%s: fd=%d what=0x%x\n", __func__, fd, what)); - if (what & EV_TIMEOUT) - { - struct rs_connection *conn = (struct rs_connection *) user_data; - assert (conn); - conn->is_connecting = 0; - rs_debug (("%s: UDP timeout NYI", __func__)); - } - else if (what & EV_READ) + rs_debug (("%s: fd=%d what =", __func__, fd)); + if (what & EV_TIMEOUT) rs_debug ((" TIMEOUT")); + if (what & EV_READ) rs_debug ((" READ")); + if (what & EV_WRITE) rs_debug ((" WRITE")); + rs_debug (("\n")); + + if (what & EV_READ) { - struct rs_connection *conn = (struct rs_connection *) user_data; - assert (conn); - /* read a single UDP packet and stick it in a new struct - rs_packet */ + /* Read a single UDP packet and stick it in USER_DATA. */ + /* TODO: Verify that unsolicited packets are dropped. */ + struct rs_packet *pkt = (struct rs_packet *) user_data; + ssize_t r = 0; - /* TODO: Verify that reception of an unsolicited response packet - results in connection being closed. */ - rs_debug (("%s: UDP read NYI", __func__)); + assert (pkt); + assert (pkt->conn); - /* TODO: delete retransmit timer */ + pkt->rpkt->data = rs_malloc (pkt->conn->ctx, 4096); + if (pkt->rpkt->data == NULL) + { + rs_err_conn_push_fl (pkt->conn, RSE_NOMEM, __FILE__, __LINE__, NULL); + return; + } + r = compat_recv (fd, pkt->rpkt->data, 4096, MSG_TRUNC); + if (r == -1) + { + int sockerr = evutil_socket_geterror (pkt->conn->fd); + if (sockerr == EAGAIN) + { + /* FIXME: Really shouldn't happen since we've been told + that fd is readable! */ + rs_debug (("%s: EAGAIN reading UDP packet -- wot?")); + return; + } + + /* Hard error. */ + rs_err_conn_push_fl (pkt->conn, RSE_SOCKERR, __FILE__, __LINE__, + "%d: recv: %d (%s)", fd, sockerr, + evutil_socket_error_to_string (sockerr)); + event_del (pkt->conn->tev); + return; + } + event_del (pkt->conn->tev); + if (r < 20 || r > 4096) /* Short or long packet. */ + { + rs_err_conn_push (pkt->conn, RSE_INVALID_PKT, + "invalid packet length: %d", + pkt->rpkt->data_len); + return; + } + pkt->rpkt->data_len = (pkt->rpkt->data[2] << 8) + pkt->rpkt->data[3]; + if (!rad_packet_ok (pkt->rpkt, 0)) + { + rs_err_conn_push_fl (pkt->conn, RSE_FR, __FILE__, __LINE__, + "invalid packet: %s", fr_strerror ()); + return; + } + /* Hand over message to user. This changes ownership of pkt. + Don't touch it afterwards -- it might have been freed. */ + if (pkt->conn->callbacks.received_cb) + pkt->conn->callbacks.received_cb (pkt, pkt->conn->user_data); } else if (what & EV_WRITE) { @@ -86,8 +133,15 @@ _evcb (evutil_socket_t fd, short what, void *user_data) event_on_connect (pkt->conn, pkt); if (pkt->conn->out_queue) - _send (pkt->conn, fd); + if (_send (pkt->conn, fd) == RSE_OK) + if (pkt->conn->callbacks.sent_cb) + pkt->conn->callbacks.sent_cb (pkt->conn->user_data); } + +#if defined (DEBUG) + if (what & EV_TIMEOUT) + rs_debug (("%s: timeout on UDP event, shouldn't happen\n", __func__)); +#endif } int @@ -95,7 +149,7 @@ udp_init (struct rs_connection *conn, struct rs_packet *pkt) { assert (!conn->bev); - conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, conn); + conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, NULL); conn->wev = event_new (conn->evb, conn->fd, EV_WRITE, _evcb, pkt); if (!conn->rev || !conn->wev) { -- cgit v1.1 From 1a1e09bd5def4fae2a499294535b37805f79fde8 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Thu, 10 Mar 2011 08:08:32 +0100 Subject: [UDP] Don't crash on second packet. [UDP] Set the user_data member for the write callback in rs_packet_send() -- the one from udp_init() doesn't do much good at this point. --- lib/udp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'lib/udp.c') diff --git a/lib/udp.c b/lib/udp.c index 19968b3..911616d 100644 --- a/lib/udp.c +++ b/lib/udp.c @@ -128,6 +128,7 @@ _evcb (evutil_socket_t fd, short what, void *user_data) { struct rs_packet *pkt = (struct rs_packet *) user_data; assert (pkt); + assert (pkt->conn); if (!pkt->conn->is_connected) event_on_connect (pkt->conn, pkt); @@ -150,7 +151,7 @@ udp_init (struct rs_connection *conn, struct rs_packet *pkt) assert (!conn->bev); conn->rev = event_new (conn->evb, conn->fd, EV_READ|EV_PERSIST, _evcb, NULL); - conn->wev = event_new (conn->evb, conn->fd, EV_WRITE, _evcb, pkt); + conn->wev = event_new (conn->evb, conn->fd, EV_WRITE, _evcb, NULL); if (!conn->rev || !conn->wev) { if (conn->rev) -- cgit v1.1