Removed dependency on RTPproxy from rtpproxy.org. The reasons for this are: drop-rtpproxy
authorRick van Rein <vanrein@hwdev.(none)>
Thu, 25 Nov 2010 08:55:23 +0000 (08:55 +0000)
committerRick van Rein <vanrein@hwdev.(none)>
Thu, 25 Nov 2010 08:55:23 +0000 (08:55 +0000)
 * RTPproxy is more complex than needed for simple 6/4 translation
 * RTPproxy is hardly documentated, making it an unreliable basis for SIPproxy64
 * RTPproxy is intended for very large setups; our scaling to 12500 conversations is enough?

There is a lot of work left to be done, most notably cleanup of the code now that it
works and closing sockets.  But that can be done as part of a major restructuring
operation in the master branch.

README
proxy.c
sipproxy64.man

diff --git a/README b/README
index c9a3707..d970e55 100644 (file)
--- a/README
+++ b/README
@@ -3,8 +3,8 @@ README for SIP proxy between IPv4 and IPv6
 ==========================================
 
 This software acts as a proxy between SIP phone networks on IPv4 and IPv6.
-It will hardly do a thing but translate addresses and cause RTPproxy to care
-for the media session in a corresponding fashion.
+It will hardly do a thing but translate addresses and cause its built-in
+RTPproxy64 to care for the media session in a corresponding fashion.
 
 
 Running the proxy
@@ -25,15 +25,20 @@ Building requirements
 Running requirements
 ====================
 
-* POSIX with IPv6
+* a POSIX operating system (such as Linux)
+* direct-routable IPv6 (not Teredo / Miredo)
 * libosip2, libosipparser2
-* rtpproxy
 
-Make sure that the socket of rtpproxy is accessible for sipproxy64.  If
-rtpproxy runs as a user and group "rtpproxy", it is usually enough to add
-the user running sipproxy64 to that group::
+The SIPproxy64 is a stand-alone application, with the exception of a few
+simple library dependencies.  It includes its own RTPproxy64 to make
+RTP traffic cross between IPv4 and IPv6.
 
-       adduser sipproxy64 -G rtpproxy
+You need IPv6 connectivity to be able to do this.  Note that Teredo and
+its Linux-implementation Miredo are not suitable, as these only work for
+client/server connections.  If you used that for SIP connectivity over
+IPv6, you would end up in NAT traversal problems in your IPv6 tunnel,
+very much like you do with SIP over IPv4!  Teredo and Miredo use a
+special IPv6 prefix 2001:0::/32 and so are easy to recognise.
 
 
 Mapping addresses
diff --git a/proxy.c b/proxy.c
index 8b11ec0..b718d6e 100644 (file)
--- a/proxy.c
+++ b/proxy.c
@@ -6,6 +6,13 @@
  * It was written for the 0cpm.org project in the belief that it solves problems in going
  * from an IPv4-based SIP world to a much better IPv6-based SIP world.
  *
+ * This software includes RTPproxy64, a simple proxy that relays RTP traffic between IPv6
+ * and IPv4.  This is designed to just do the simple protocol translation, and not become
+ * a playground for functions such as wiretapping or encryption.  Given a fast processor
+ * and 1 Gbps network connections on the IPv4 as well as IPv6 side, it should be feasible
+ * to run 12500 RTPproxy64 sessions in parallel (although the design may have to be
+ * improved to actually get that far).
+ *
  * This software is provided as-is under the GNU Public License version 3.  If you need
  * a more relaxed license, please contact the copyright holder; at the very least, we will
  * listen to your requests and balance them.  Our general intention is to improve freedom
@@ -26,6 +33,7 @@
 #include <string.h>
 #include <errno.h>
 #include <fcntl.h>
+#include <time.h>
 
 #include <sys/types.h>
 #include <sys/time.h>
@@ -56,9 +64,24 @@ struct v4v6map {
 #define MAP_V4BOUND 0x01
 #define MAP_V6BOUND 0x02
 
+#define IDSZ 64
+
+struct v4v6rtpmap {
+       struct v4v6rtpmap *next;
+       time_t timeout;
+       int v4sox, v6sox;
+       struct sockaddr_in  v4remote;
+       struct sockaddr_in6 v6remote;
+       unsigned short int v4port, v6port;
+       char call_id [IDSZ+2];
+       char from_tag [IDSZ+2];
+       char to_tag [IDSZ+2];
+};
+
 
 /* Global variables.
  */
+
 char *program;
 
 struct in_addr  v4local, v4uplink;
@@ -77,16 +100,19 @@ osip_t *osip;
 
 struct sockaddr_un rtppath;
 
+unsigned short int rtp_firstport = 14000, rtp_nextport = 14000, rtp_lastport = 26499;
+
+struct v4v6rtpmap *v4v6rtpmap;
+
 
 /* Commandline arguments.  See manpage for details.
  */
-const char *short_opts = "hl:L:u:U:c:";
+const char *short_opts = "hl:L:u:U:";
 struct option long_opts [] = {
        { "v4local", 1, NULL, 'l' },
        { "v6local", 1, NULL, 'L' },
        { "v4uplink",  1, NULL, 'u' },
        { "v6uplink",  1, NULL, 'U' },
-       { "rtpctl",   1, NULL, 'c' },
        { "help",     0, NULL, 'h' },
        { NULL,       0, NULL,  0  },
 };
@@ -148,102 +174,157 @@ int map_6to4_sox (const struct in6_addr *in) {
 }
 
 
-/* Interact with the RTPproxy over its command channel.  Return the response in the buffer,
- * terminated with a 0.  Return 1 on success or 0 on failure.
- */
-int rtpctl_io (char buf [201]) {
-       //
-       // Connect to the RTPproxy through a UNIX socket socket
-       int rtpctl = socket (PF_UNIX, SOCK_STREAM, 0);
-       if (rtpctl == -1) {
-               fprintf (stderr, "Failed to access RTPproxy: %s\n", strerror (errno));
-               return 0;
-       } else if (connect (rtpctl, (struct sockaddr *) &rtppath, sizeof (rtppath)) == 1) {
-               fprintf (stderr, "Failed to bind to RTPproxy: %s\n", strerror (errno));
-               close (rtpctl);
-               return 0;
-       }
-       //
-       // Send the command to the RTPproxy socket
-       buf [200] = 0;
-       printf ("RTPproxy > %s", buf);
-       size_t buflen = strlen (buf);
-       errno = 0;
-       if (write (rtpctl, buf, buflen) < buflen) {
-               printf ("Failure writing command to RTPproxy: %s\n", strerror (errno));
-               // At least the '\n' has not been written, so the next write
-               // will complete the line (leading to garbish output); but
-               // at least there is no output from this write alone.
-               buf [0] = 0;
-               close (rtpctl);
-               return 0;
-       }
-       //
-       // Read back the reply from the RTPproxy socket and check for success
-       buflen = read (rtpctl, buf, sizeof (buf)-1);
-       close (rtpctl);
-       if (buflen == -1) {
-               printf ("Failure reading response from RTPproxy: %s\n", strerror (errno));
-               buf [0] = 0;
-               return 0;
-       }
-       buf [buflen] = 0;
-       printf ("RTPproxy < %s\n", buf);
-       return 1;
-}
-
-
-/* Update the RTPproxy over its command channel, sending it information about a connection
+/* Update the built-in RTPproxy64, by setting up data structures for a connection
  * to establish.  The address provided may be an IPv4 or IPv6 address, for values AF_INET
  * and AF_INET6 in addrfam, respectively.  The port is in host by order.  The To: tag is
  * only known in reply traffic, so it is optional (meaning, it may be set to NULL).
  * The return value is a nonzero string with the port number opened, or NULL on failure.
  */
-char *rtpctl_update (int addrfam, void *addr, char *port,
+unsigned short int rtpctl_update (int addrfam, void *addr, char *port,
                                char *call_id, char *from_tag, char *opt_to_tag) {
-       static char buf [200+1];
+       struct v4v6rtpmap *map = v4v6rtpmap;
        //
-       // Create an Update session command, with Asymmetric flag and optional 6 flag
-       snprintf (buf, 200, "UA%sL%s %s %s %s %s%s%s\n",
-                       (addrfam == AF_INET6)? "EI6": "IE",
-                       (addrfam == AF_INET6)? v6local_str: v4local_str,
-                       call_id,
-                       addr,
-                       port,
-                       from_tag,
-                       opt_to_tag? " "       : "",
-                       opt_to_tag? opt_to_tag: "");
+       // Find the entry tha describes the string-described RTP connection
+       int searching = 1;
+       while (map) {
+               if (strncmp (map->call_id, call_id, IDSZ)
+                               || strncmp (map->from_tag, from_tag, IDSZ)
+                               || ( opt_to_tag && *map->to_tag
+                                       && strncmp (map->to_tag, opt_to_tag, IDSZ))) {
+                       map = map->next;
+               } else {
+                       break;
+               }
+       }
        //
-       // Actuall pass the command through the RTPproxy
-       if (!rtpctl_io (buf)) {
-               return NULL;
+       // If not found, create a new entry in the RTPmap
+       if (!map) {
+               map = malloc (sizeof (struct v4v6rtpmap));
+               if (!map) {
+                       return 0;
+               }
+               memset (map, 0, sizeof (struct v4v6rtpmap));
+               map->v4sox = map->v6sox = -1;
+               map->next = v4v6rtpmap;
+               v4v6rtpmap = map;
+               strncpy (map->call_id, call_id, IDSZ+1);
+               strncpy (map->from_tag, from_tag, IDSZ+1);
+               printf ("New RTP map entry: %s, %s, %s\n", call_id, from_tag, opt_to_tag? opt_to_tag: "*NULL*");
+       }
+       if (opt_to_tag && !*map->to_tag) {
+               strncpy (map->to_tag, opt_to_tag, IDSZ+1);
        }
-       char *space = strchr (buf, ' ');
-       if (space) {
-               *space = 0;
-               return buf;
+       //
+       // Update the RTPmap expiration timer
+       map->timeout = time (NULL) + 60;
+       //
+       // Update the remote socket address for this RTPmap
+       // TODO: Perhaps protect by needing the address family set to 0 (for unset)
+       if (addrfam == AF_INET) {
+               if (! map->v4remote.sin_family ) {
+                       map->v4remote.sin_family  = addrfam;
+                       map->v4remote.sin_port  = htons (atoi (port));
+                       if (!inet_pton (addrfam, addr, &map->v4remote.sin_addr )) {
+                               printf ("Error parsing remote IPv4 address %s for RTP\n", addr);
+                       } else {
+                               printf ("Correctly parsed remote IPv4 address %s for RTP\n", addr); //DEBUG
+                       }
+               }
        } else {
-               return NULL;
+               if (! map->v6remote.sin6_family) {
+                       map->v6remote.sin6_family = addrfam;
+                       map->v6remote.sin6_port = htons (atoi (port));
+                       if (!inet_pton (addrfam, addr, &map->v6remote.sin6_addr)) {
+                               printf ("Error parsing remote IPv6 address %s for RTP\n", addr);
+                       } else {
+                               printf ("Correctly parsed remote IPv6 address %s for RTP\n", addr); //DEBUG
+                       }
+               }
+       }
+       //
+       // Update the local socket addresses for this RTPmap
+       if (addrfam == AF_INET) {
+               if (map->v4port) {
+                       return map->v4port;
+               }
+               map->v4sox = socket (PF_INET,  SOCK_DGRAM, 0);
+               if (map->v4sox == -1) {
+                       return 0;
+               }
+               struct sockaddr_in v4sa;
+               memset (&v4sa, 0, sizeof (v4sa));
+               v4sa.sin_family = AF_INET;
+               v4sa.sin_port = htons (rtp_nextport);
+               memcpy (&v4sa.sin_addr, &v4local, sizeof (v4sa.sin_addr));
+               // TODO: Also listen to the next port for RTCP
+               while (bind (map->v4sox, (struct sockaddr *) &v4sa, sizeof (v4sa)) == -1) {
+                       printf ("Failed to bind socket to IPv4 port %d\n", rtp_nextport);
+                       //NOTE: Could in theory get stuck
+                       if (rtp_nextport == rtp_lastport) {
+                               rtp_nextport = rtp_firstport;
+                       } else {
+                               rtp_nextport++;
+                       }
+                       v4sa.sin_port = htons (rtp_nextport);
+               }
+               printf ("Succesfully bound socket %d to IPv4 address/port %s/%d\n", map->v4sox, v4local_str, rtp_nextport);
+               map->v4port = rtp_nextport;
+               return rtp_nextport;
+       } else {
+               if (map->v6port) {
+                       return map->v6port;
+               }
+               map->v6sox = socket (PF_INET6, SOCK_DGRAM, 0);
+               if (map->v6sox == -1) {
+                       return 0;
+               }
+               struct sockaddr_in6 v6sa;
+               memset (&v6sa, 0, sizeof (v6sa));
+               v6sa.sin6_family = AF_INET6;
+               v6sa.sin6_port = htons (rtp_nextport);
+               memcpy (&v6sa.sin6_addr, &v6local, sizeof (v6sa.sin6_addr));
+               // TODO: Also listen to the next port for RTCP
+               while (bind (map->v6sox, (struct sockaddr *) &v6sa, sizeof (v6sa)) == -1) {
+                       printf ("Failed to bind socket to IPv6 port %d\n", rtp_nextport);
+                       //NOTE: Could in theory get stuck
+                       if (rtp_nextport == rtp_lastport) {
+                               rtp_nextport = rtp_firstport;
+                       } else {
+                               rtp_nextport++;
+                       }
+                       v6sa.sin6_port = htons (rtp_nextport);
+               }
+               printf ("Succesfully bound socket %d to IPv6 address/port %s/%d\n", map->v6sox, v6local_str, rtp_nextport);
+               map->v6port = rtp_nextport;
+               return rtp_nextport;
        }
 }
 
 
-/* Delete a connection from the RTPproxy over its command channel.  The connection is
- * identified in the same way as when updating the RTPproxy.  The SIP traffic usually
- * takes care of duplicting that information.
+/* Delete a media stream from the RTPproxy64 over its command channel.  The connection is
+ * identified in the same way as when updating the RTPproxy64, although the from_tag and
+ * to_tag may now be reversed by the initiator of a BYE request.  This means you should
+ * invoke this function a second time with exchanged from_tag and to_tag.  This function
+ * supports optional tags for both arguments in support of that.  The SIP traffic usually
+ * takes care of duplicting that information, but this function will not fail if you tried
+ * to delete a non-existing connection.
  */
-void rtpctl_delete (char *call_id, char *from_tag, char *opt_to_tag) {
-       char buf [200+1];
+void rtpctl_delete (char *call_id, char *opt_from_tag, char *opt_to_tag) {
+       struct v4v6rtpmap *map = v4v6rtpmap;
        //
-       // Create a Delete session command and send an emtpy to_tag if needed
-       snprintf (buf, 200, "D %s %s %s\n",
-                       call_id,
-                       from_tag,
-                       opt_to_tag? opt_to_tag: "");
-       //
-       // Actuall pass the command through the RTPproxy
-       if (!rtpctl_io (buf)) {
-               printf ("RTPproxy deletion of session failed.  Inactivity will solve this.\n");
+       // Find the entry tha describes the string-described RTP connection
+       while (map) {
+               if (strncmp (map->call_id, call_id, IDSZ)
+                               || ( opt_from_tag && strncmp (map->from_tag, opt_from_tag, IDSZ))
+                               || ( opt_to_tag && *map->to_tag
+                                       && strncmp (map->to_tag, opt_to_tag, IDSZ))) {
+                       map = map->next;
+               } else {
+                       //
+                       // When found, set the entry to expire immediately
+                       map->timeout = time (NULL);
+                       return;
+               }
        }
 }
 
@@ -414,8 +495,9 @@ int map_uri_host_6to4 (osip_uri_t *uri) {
 
 /* Handle an incoming SIP message.  Read it from the socket of the given map,
  * perform sanity checks, rewrite addresses, add a Via header and pass it on.
- * On the fly, if need be, initiate or terminate RTPproxy.  Also, where
- * applicable, send notifications back to the originator.
+ * On the fly, if need be, initiate or terminate media forwarding through the
+ * built-in RTPproxy64.  Also, where applicable, send notifications back to
+ * the originator.
  * TODO: Parameterise all AF_INET / AF_INET6 differences away, thus avoiding double code.
  */
 void handle_sip (struct v4v6map *recvmap) {
@@ -598,8 +680,8 @@ void handle_sip (struct v4v6map *recvmap) {
        int cnxpos;
        if ((!disturbed) && sdp) {
                //
-               // TODO: Proxy SDP address/port pairs through RTPproxy
-               // TODO: Assuming a single c= and a single m= as RTPproxy may be incapable...
+               // TODO: Proxy SDP address/port pairs through RTPproxy64
+               // TODO: Assuming a single c= and a single m= as RTPproxy64 may be incapable...
                // pos_media==-1 refers to a c= prefixing the first m=; 0.. are subsequent m= and c=
                cnxpos = -1;
                cnx = sdp_message_connection_get (sdp, cnxpos, 0);
@@ -658,6 +740,8 @@ void handle_sip (struct v4v6map *recvmap) {
                                disturbed = 1;
                        }
                }
+       }
+       if (!disturbed) {
                //
                // Collect the information for call identification: id_code, from_tag, [to_tag]
                osip_uri_param_t *from_tag;
@@ -668,16 +752,18 @@ void handle_sip (struct v4v6map *recvmap) {
                char *call_id;
                disturbed = disturbed || osip_call_id_to_str (call_id_h, &call_id);
                //
-               // Create, update or delete a mapping in RTPproxy
+               // Create, update or delete a mapping in RTPproxy64
                if (call_id && from_tag && !disturbed) {
                        if (!resp) {
                                //
                                // Handle initiations
-                               if (strcmp (method, "INVITE") == 0 && sdp) {
-                                       char *rtpport = rtpctl_update (c_af_inetX, c_addr, m_port, call_id, from_tag->gvalue, NULL);
+                               if (MSG_IS_INVITE (sip) && sdp) {
+                                       unsigned short int rtpport = rtpctl_update (c_af_inetX, c_addr, m_port, call_id, from_tag->gvalue, NULL);
                                        if (rtpport) {
-                                               printf ("Expecting media on port %s over %s\n", rtpport, (c_af_inetX==AF_INET)? "IPv6": "IPv4");
-                                               sdp_message_m_port_set (sdp, 0, rtpport);
+                                               char portnr [11];
+                                               snprintf (portnr, 10, "%d", rtpport);
+                                               printf ("Expecting media on port %s over %s\n", portnr, (c_af_inetX==AF_INET)? "IPv6": "IPv4");
+                                               sdp_message_m_port_set (sdp, 0, portnr);
                                                cnx->c_addrtype [2] ^= '4' ^ '6';
                                                cnx->c_addr = (c_af_inetX==AF_INET)? v6local_str: v4local_str;
 #if 0
@@ -688,26 +774,27 @@ void handle_sip (struct v4v6map *recvmap) {
                                                        NULL, NULL);
 #endif
                                        }
-                                       char ibuf [200+1]; ibuf [0] = 'I'; ibuf [1] = '\n' ; ibuf [2] = 0; rtpctl_io (ibuf); //DEBUG
                                }
-                               if (strcmp (method, "BYE") == 0) {
-                                       // TODO: Make sure that "BYE" drops RTP connection
-                                       rtpctl_delete (call_id, from_tag->gvalue, to_tag->gvalue);
-                                       char ibuf [200+1]; ibuf [0] = 'I'; ibuf [1] = '\n' ; ibuf [2] = 0; rtpctl_io (ibuf); //DEBUG
+                               //
+                               // Handle terminations of connections
+                               if (MSG_IS_BYE (sip)) {
+                                       rtpctl_delete (call_id, from_tag? from_tag->gvalue: NULL, to_tag? to_tag->gvalue: NULL);
+                                       rtpctl_delete (call_id, to_tag? to_tag->gvalue: NULL, from_tag? from_tag->gvalue: NULL);
                                }
-                               if (strcmp (method, "CANCEL") == 0) {
-                                       // TODO: Make sure that "CANCEL" drops RTP connection (or...?)
-                                       rtpctl_delete (call_id, from_tag->gvalue, to_tag->gvalue);
-                                       char ibuf [200+1]; ibuf [0] = 'I'; ibuf [1] = '\n' ; ibuf [2] = 0; rtpctl_io (ibuf); //DEBUG
+                               if (MSG_IS_CANCEL (sip)) {
+                                       rtpctl_delete (call_id, from_tag? from_tag->gvalue: NULL, to_tag? to_tag->gvalue: NULL);
+                                       rtpctl_delete (call_id, to_tag? to_tag->gvalue: NULL, from_tag? from_tag->gvalue: NULL);
                                }
                        } else {
                                //
                                // Handle responses
                                if (reason == 200 && sdp) {             // 200 OK
-                                       char *rtpport = rtpctl_update (c_af_inetX, c_addr, m_port, call_id, from_tag->gvalue, to_tag->gvalue);
+                                       unsigned short int rtpport = rtpctl_update (c_af_inetX, c_addr, m_port, call_id, from_tag->gvalue, to_tag->gvalue);
                                        if (rtpport) {
-                                               printf ("Expecting media on port %s over %s\n", rtpport, (c_af_inetX==AF_INET)? "IPv6": "IPv4");
-                                               sdp_message_m_port_set (sdp, 0, rtpport);
+                                               char portnr [11];
+                                               snprintf (portnr, 10, "%d", rtpport);
+                                               printf ("Expecting media on port %s over %s\n", portnr, (c_af_inetX==AF_INET)? "IPv6": "IPv4");
+                                               sdp_message_m_port_set (sdp, 0, portnr);
                                                cnx->c_addrtype [2] ^= '4' ^ '6';
                                                cnx->c_addr = (c_af_inetX==AF_INET)? v6local_str: v4local_str;
 #if 0
@@ -718,15 +805,16 @@ void handle_sip (struct v4v6map *recvmap) {
                                                        NULL, NULL);
 #endif
                                        }
-                                       char ibuf [200+1]; ibuf [0] = 'I'; ibuf [1] = '\n' ; ibuf [2] = 0; rtpctl_io (ibuf); //DEBUG
                                }
                        }
                }
                char *sdp_newstr;
-               disturbed = disturbed || sdp_message_to_str (sdp, &sdp_newstr);
-               if (!disturbed) {
-                       osip_list_init (&(sip)->bodies);
-                       osip_message_set_body (sip, sdp_newstr, strlen (sdp_newstr));
+               if (sdp) {
+                       disturbed = disturbed || sdp_message_to_str (sdp, &sdp_newstr);
+                       if (!disturbed) {
+                               osip_list_init (&(sip)->bodies);
+                               osip_message_set_body (sip, sdp_newstr, strlen (sdp_newstr));
+                       }
                }
        }
        if (!disturbed) {
@@ -849,17 +937,74 @@ void handle_sip (struct v4v6map *recvmap) {
 }
 
 
+/* Handle RTP traffic going from IPv4 to IPv6, according to the given map.
+ * There is a separate function for the opposite direction, for efficiency's sake.
+ * TODO: Check one RTP link every time, and close/cleanup when expired.
+ */
+void handle_rtp_4to6 (struct v4v6rtpmap *rtpmap) {
+       //
+       // Read an RTP packet, but only from the accepted sender
+       char buf [1002];
+       struct sockaddr_in sender;
+       socklen_t senderlen = sizeof (sender);
+       ssize_t buflen = recvfrom (rtpmap->v4sox, buf, 1000, MSG_DONTWAIT,
+                       (struct sockaddr *) &sender, &senderlen);
+       if (buflen > 0 && senderlen == sizeof (rtpmap->v4remote) &&
+                                !memcmp (&rtpmap->v4remote, &sender, senderlen)) {
+               ssize_t sent = sendto (rtpmap->v6sox, buf, buflen, MSG_EOR,
+                       (struct sockaddr *) &rtpmap->v6remote, sizeof (rtpmap->v6remote));
+               if (sent < buflen) {
+                       printf ("Passed only %d out of %d RTP bytes from IPv4 to IPv6: %s\n", sent, buflen, strerror (errno));
+               }
+       } else {
+               printf ("RTP not mapped from IPv4 to IPv6 due to sender mismatch\n");
+       }
+}
+
+
+/* Handle RTP traffic going from IPv6 to IPv4, according to the given map.
+ * There is a separate function for the opposite direction, for efficiency's sake.
+ */
+void handle_rtp_6to4 (struct v4v6rtpmap *rtpmap) {
+       //
+       // Read an RTP packet, but only from the accepted sender
+       char buf [1002];
+       struct sockaddr_in6 sender;
+       socklen_t senderlen = sizeof (sender);
+       ssize_t buflen = recvfrom (rtpmap->v6sox, buf, 1000, MSG_DONTWAIT,
+                       (struct sockaddr *) &sender, &senderlen);
+       if (buflen > 0 && senderlen == sizeof (rtpmap->v6remote) &&
+                                !memcmp (&rtpmap->v6remote, &sender, senderlen)) {
+               ssize_t sent = sendto (rtpmap->v4sox, buf, buflen, MSG_EOR,
+                       (struct sockaddr *) &rtpmap->v4remote, sizeof (rtpmap->v4remote));
+               if (sent < buflen) {
+                       printf ("Passed only %d out of %d RTP bytes from IPv6 to IPv4: %s\n", sent, buflen, strerror (errno));
+               }
+       } else {
+               printf ("RTP not mapped from IPv6 to IPv4 due to sender mismatch\n");
+       }
+}
+
+
 /* Run the proxy daemon.  Listen to all the sockets that have been opened, and
  * upon arrival of a SIP message, handle it.
+ *
+ * The scheduling algorithm handles both SIP and RTP, in a straightforward fashion:
+ *  - a single loop scans for traffic on SIP and RTP ports
+ *  - in each loop, _all_ RTP traffic is handled first to make it faster than SIP
+ *  - in each loop, _one_ SIP message is handled last, to make it yield to    RTP
  */
 void run_proxy (void) {
        int nfds = 0;
-       struct v4v6map *map = v4v6maplist;
-       while (map) {
-               if (map->sox > nfds) {
-                       nfds = map->sox + 1;
+       struct v4v6map    *sipmap = v4v6maplist;
+       struct v4v6rtpmap *rtpmap = NULL;
+       //
+       // No RTP map initialisation needed; it is initially empty
+       while (sipmap) {
+               if (sipmap->sox >= nfds) {
+                       nfds = sipmap->sox + 1;
                }
-               map = map->next;
+               sipmap = sipmap->next;
        }
        //
        // Be a good daemon.  Loop forever.  Build & Handle <--> Wait & Listen.
@@ -867,16 +1012,69 @@ void run_proxy (void) {
        FD_ZERO (&fdset);
        while (1) {
                //
-               // Build an fd_set of sockets to listen to
-               map = v4v6maplist;
-               while (map) {
-                       if (FD_ISSET (map->sox, &fdset)) {
-                               handle_sip (map);
-                               fflush (stdout);
+               // Build an fd_set of RTP sockets to listen to, handling all
+               rtpmap = v4v6rtpmap;
+               while (rtpmap) {
+                       if (rtpmap->v4sox != -1) {
+                               if (FD_ISSET (rtpmap->v4sox, &fdset)) {
+                                       handle_rtp_4to6 (rtpmap);
+                               } else {
+                                       FD_SET (rtpmap->v4sox, &fdset);
+                                       if (rtpmap->v4sox >= nfds) {
+                                               nfds = rtpmap->v4sox + 1;
+                                       }
+                               }
+                       }
+                       if (rtpmap->v6sox != -1) {
+                               if (FD_ISSET (rtpmap->v6sox, &fdset)) {
+                                       handle_rtp_6to4 (rtpmap);
+                               } else {
+                                       FD_SET (rtpmap->v6sox, &fdset);
+                                       if (rtpmap->v6sox >= nfds) {
+                                               nfds = rtpmap->v6sox + 1;
+                                       }
+                               }
+                       }
+                       rtpmap = rtpmap->next;
+               }
+               rtpmap = v4v6rtpmap;
+               //
+               // Build an fd_set of SIP sockets to listen to, possibly handling one
+               sipmap = v4v6maplist;
+               while (sipmap) {
+                       if (FD_ISSET (sipmap->sox, &fdset)) {
+                               handle_sip (sipmap);
+                               fflush (stdout); //DEBUG
+                               //
+                               // One SIP message done, now yield to RTP
+                               while (sipmap) {
+                                       FD_SET (sipmap->sox, &fdset);
+                                       sipmap = sipmap->next;
+                               }
+                               //
+                               // If SIP has changed the RTP map, update the fd_set
+                               if (rtpmap != v4v6rtpmap) {
+                                       rtpmap = v4v6rtpmap;
+                                       while (rtpmap) {
+                                               if (rtpmap->v4sox != -1) {
+                                                       FD_SET (rtpmap->v4sox, &fdset);
+                                                       if (rtpmap->v4sox >= nfds) {
+                                                               nfds = rtpmap->v4sox + 1;
+                                                       }
+                                               }
+                                               if (rtpmap->v6sox != -1) {
+                                                       FD_SET (rtpmap->v6sox, &fdset);
+                                                       if (rtpmap->v6sox >= nfds) {
+                                                               nfds = rtpmap->v6sox + 1;
+                                                       }
+                                               }
+                                               rtpmap = rtpmap->next;
+                                       }
+                               }
                        } else {
-                               FD_SET (map->sox, &fdset);
+                               FD_SET (sipmap->sox, &fdset);
+                               sipmap = sipmap->next;
                        }
-                       map = map->next;
                }
                //
                // Now listen to the sockets in the fd_set and process output
@@ -979,7 +1177,7 @@ void open_cmdline (int argc, char *argv []) {
        // Process commandline arguments
        int sane = 1;
        char *v4bad = NULL, *v6bad = NULL;
-       int seen_l = 0, seen_L = 0, seen_c = 0, seen_h = 0;
+       int seen_l = 0, seen_L = 0, seen_h = 0;
        char *arg_b = NULL, *arg_B = NULL, *arg_p = NULL, *arg_P = NULL;
        int opt;
        while (opt = getopt_long (argc, argv, short_opts, long_opts, NULL), opt != -1) {
@@ -998,17 +1196,6 @@ void open_cmdline (int argc, char *argv []) {
                                v6bad = strdup (optarg);
                        }
                        break;
-               case 'c':
-                       seen_c++;
-                       if (strncmp (optarg, "unix:", 5) != 0) {
-                               fprintf (stderr, "%s: You currently must specify a unix:/socket/path to -c\n", program);
-                               sane = 0;
-                       } else {
-                               memset (&rtppath, 0, sizeof (rtppath));
-                               rtppath.sun_family = AF_UNIX;
-                               strncpy (rtppath.sun_path, optarg + 5, sizeof (rtppath.sun_path) - 1);
-                       }
-                       break;
                case 'u':
                        seen_u++;
                        if (inet_pton (AF_INET,  optarg, &v4uplink) != 1) {
@@ -1033,7 +1220,7 @@ void open_cmdline (int argc, char *argv []) {
                }
        }
        if (!sane) {
-               fprintf (stderr, "Usage: %s -l <v4local> -L <v6local> -c <rtpctl> [-u <v4uplink>] [-U <v6uplink>] [<v4phone>=<v6phone> ...]\n", program);
+               fprintf (stderr, "Usage: %s -l <v4local> -L <v6local> [-u <v4uplink>] [-U <v6uplink>] [<v4phone>=<v6phone> ...]\n", program);
                exit (!seen_h);
        }
        //
@@ -1048,10 +1235,6 @@ void open_cmdline (int argc, char *argv []) {
                fprintf (stderr, "%s: You must specify one IPv6 local address of this proxy using -L\n", program);
                sane = 0;
        }
-       if (seen_c != 1) {
-               fprintf (stderr, "%s: You must specify one control socket to access RTPproxy using -c\n", program);
-               sane = 0;
-       }
        if (seen_u > 1) {
                fprintf (stderr, "%s: You may not specify multiple IPv4 uplink addresses using -u\n", program);
                sane = 0;
index f7df846..d292af3 100644 (file)
@@ -10,16 +10,12 @@ nothing more clever, it merely exchanges IPv4 and IPv6 addresses and passes on t
 to other packets.  As a concrete example, it won't do DNS.  If anything needs to be resolved,
 you should direct the traffic to a proxy for further handling.
 
-To handle RTP traffic, SIPproxy64 interacts with RTPproxy.  It is assumed that this has been
-started to listen on IPv4 and IPv6 networks that can handle the traffic passed around with
-the SIP messages.  For instance, the RTPproxy could be started with
-
-       rtpproxy -l <v4local> -6 /<v6local> -u rtpproxy -p <rtpctl>
-
-The addresses used by RTPproxy may be the same as those used by SIPproxy64, but they
-need not be.  Whatever address/port pairs RTPproxy picks will be used in SDP descriptions
-of RTP streams.  The only task of SIPproxy64 concerning SDP is to locate addresses and
-substitute them as RTPproxy dictates.
+SIPproxy64 has its own little RTPproxy64 built in.  This means that no external
+application is required to tunnel traffic.  The added traffic for SIP usually
+does no harm to an RTPproxy64, and getting it there may be done through any form
+of SIP traffic shaping, if so desired, including even selective pickups of call
+requests by the SIPproxy64 hosts.  Note however, that 1 Gbps can carry as many
+as 12500 calls at ISDN-quality, so this is not a limit to reach quickly.
 
 The actual task of SIPproxy64 is to find IPv6 addresses in messages entering its IPv6
 side, and IPv4 address on messages entering its IPv4 side.  It then exchanges the address
@@ -68,14 +64,6 @@ OPTIONS
        the only reason to specify this option is to be able to proxy new transactions
        from IPv4 to IPv6.
 
--c <rtpctl>
---rtpctl=<rtpctl>
-       One or more of these options specify the path to an RTPproxy daemon that can
-       proxy the IPv4 and IPv6 networks.  The path is currently limited to UNIX
-       file system sockets only, so specifying multiple options is not useful.
-       If not specified, this setting reverts to the RTPproxy default, being
-       unix:/var/run/rtpproxy.sock
-
 -u <v4phone>
 --v4phone=<v4phone>
        Any number of addresses of IPv4-only phones can be specified with zero or more of
@@ -113,8 +101,7 @@ EXAMPLES
 
        Assume the home router at 192.168.2.1, and the SIPproxy64 to sit
        between 192.168.2.12 and 2001:abcd:ef::192.168.2.12.  Also assume
-       an IPv6-side smarter proxy at 2001:fe:dcba::87.  The RTPproxy is
-       silently assumed to run on its default location udp:127.0.0.1:22222
+       an IPv6-side smarter proxy at 2001:fe:dcba::87.
 
        sipproxy64 -l 192.168.2.21 -L 2001:abcd:ef::192.168.2.12 -u 192.168.2.1 -U 2001:fe:dcba::87
 
@@ -130,8 +117,7 @@ EXAMPLES
        the SIPproxy64; that all depends on the possibilities of the
        registrar.  The following example assumes no smarter proxies to be
        configured, but purely to route call traffic.  Also, it assumes to
-       run at 10.0.0.17 annex 2001:abcd:ef:10::0:0:17 and have the RTPproxy
-       at its default location
+       run at 10.0.0.17 annex 2001:abcd:ef:10::0:0:17
 
        sipproxy64 -l 10.0.0.17 -L 2001:abcd:ef::10:0:0:17 10.0.0.138=2001:abcd:ef::10:0:0:138 10.0.0.139=2001:abcd:ef::10:0:0:139
 
@@ -149,9 +135,9 @@ EXAMPLES
 ALGORITHM
 
        The SIPproxy64 is a stateless SIP proxy.  This means that it is not
-       aware of connections that are live.  RTPproxy does keep state on
-       connections, but it too will timeout if no traffic is passing through
-       it.  In other words, the system will always get back to an empty
+       aware of connections that are live.  The built-in RTPproxy64 does keep
+       state on connections, but it too will timeout if no traffic is passing
+       through it.  In other words, the system will always get back to an empty
        initial state if left alone.
 
        To achieve statelessness, SIP sends information in Route: headers
@@ -195,25 +181,12 @@ ALGORITHM
        SIPproxy64.  In other words, an INVITE to the World will be
        sent as an INVITE in the other address family.
 
-       In all the cases above, if SDP content is found attached to
-       the message, its addresses and ports are rewritten to values
-       that are suggested by RTPproxy.  The proxy will be asked to
-       setup for asynchronous RTP, meaning that it will not wait for
-       traffic in both directions before starting to forward the
-       media to the other side.  Under the assumption that both IPv4
-       and IPv6 addresses can be routed locally, this should give
-       the best possible performance.
-
 
 BUGS
        The only <phone> options supported are IPv4 phones, because there does not seem
        to be any use for proxying to IPv6 phones.  If the external site is not a phone
        but a proxy, then the -U option should be used instead.
 
-       Only one -c option is currently meaningful.  This is the result of only supporting
-       unix:/file/socket paths to an RTPproxy instance.  In complex setups, this option
-       would have to change.  Then again, complex setups may need a more complex proxy?
-
        It is possible in theory to listen promiscuously to an interface, or
        to use a mangling firewall, as a way to force traffic for a subnet
        through the SIPproxy64.  This is not currently supported, which means
@@ -224,5 +197,10 @@ BUGS
        The current implementation does not scan port numbers post-fixed to IP addresses,
        but it really should in order to simplify matters.
 
-SEE ALSO
-       rtpproxy
+       No support exists for routing requests to the same address family.
+       Although this makes the function of SIPproxy64 very clear, it could
+       be argued that an IPv4-only device would always forward to the same
+       IPv4 address, without knowing if it is actually asking for an IPv6
+       alternative.  Input on this matter is requested to decide whether it
+       should be handled in SIPproxy64 or not.
+