Browse Source

udpgw: add option for specifying local UDP port range

ambrop7 14 năm trước cách đây
mục cha
commit
955e558343
4 tập tin đã thay đổi với 78 bổ sung0 xóa
  1. 8 0
      system/BDatagram.h
  2. 12 0
      system/BDatagram_unix.c
  3. 12 0
      system/BDatagram_win.c
  4. 46 0
      udpgw/udpgw.c

+ 8 - 0
system/BDatagram.h

@@ -127,6 +127,14 @@ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *lo
 int BDatagram_GetFd (BDatagram *o);
 #endif
 
+/**
+ * Sets the SO_REUSEADDR option for the underlying socket.
+ * 
+ * @param o the object
+ * @param reuse value of the option. Must be 0 or 1.
+ */
+int BDatagram_SetReuseAddr (BDatagram *o, int reuse);
+
 /**
  * Initializes the send interface.
  * The send interface must not be initialized.

+ 12 - 0
system/BDatagram_unix.c

@@ -735,6 +735,18 @@ int BDatagram_GetFd (BDatagram *o)
     return o->fd;
 }
 
+int BDatagram_SetReuseAddr (BDatagram *o, int reuse)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(reuse == 0 || reuse == 1)
+    
+    if (setsockopt(o->fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0) {
+        return 0;
+    }
+    
+    return 1;
+}
+
 void BDatagram_SendAsync_Init (BDatagram *o, int mtu)
 {
     DebugObject_Access(&o->d_obj);

+ 12 - 0
system/BDatagram_win.c

@@ -632,6 +632,18 @@ int BDatagram_GetLastReceiveAddrs (BDatagram *o, BAddr *remote_addr, BIPAddr *lo
     return 1;
 }
 
+int BDatagram_SetReuseAddr (BDatagram *o, int reuse)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(reuse == 0 || reuse == 1)
+    
+    if (setsockopt(o->sock, SOL_SOCKET, SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) < 0) {
+        return 0;
+    }
+    
+    return 1;
+}
+
 void BDatagram_SendAsync_Init (BDatagram *o, int mtu)
 {
     DebugObject_Access(&o->d_obj);

+ 46 - 0
udpgw/udpgw.c

@@ -119,6 +119,8 @@ struct {
     int max_clients;
     int max_connections_for_client;
     int client_socket_sndbuf;
+    int local_udp_num_ports;
+    char *local_udp_addr;
 } options;
 
 // MTUs
@@ -129,6 +131,9 @@ int pp_mtu;
 BAddr listen_addrs[MAX_LISTEN_ADDRS];
 int num_listen_addrs;
 
+// local UDP port range, if options.local_udp_num_ports>=0
+BAddr local_udp_addr;
+
 // reactor
 BReactor ss;
 
@@ -326,6 +331,7 @@ void print_help (const char *name)
         "        [--max-clients <number>]\n"
         "        [--max-connections-for-client <number>]\n"
         "        [--client-socket-sndbuf <bytes / 0>]\n"
+        "        [--local-udp-addrs <addr> <num_ports>]\n"
         "Address format is a.b.c.d:port (IPv4) or [addr]:port (IPv6).\n",
         name
     );
@@ -358,6 +364,7 @@ int parse_arguments (int argc, char *argv[])
     options.max_clients = DEFAULT_MAX_CLIENTS;
     options.max_connections_for_client = DEFAULT_MAX_CONNECTIONS_FOR_CLIENT;
     options.client_socket_sndbuf = CLIENT_DEFAULT_SOCKET_SEND_BUFFER;
+    options.local_udp_num_ports = -1;
     
     int i;
     for (i = 1; i < argc; i++) {
@@ -492,6 +499,18 @@ int parse_arguments (int argc, char *argv[])
             }
             i++;
         }
+        else if (!strcmp(arg, "--local-udp-addrs")) {
+            if (2 >= argc - i) {
+                fprintf(stderr, "%s: requires two arguments\n", arg);
+                return 0;
+            }
+            options.local_udp_addr = argv[i + 1];
+            if ((options.local_udp_num_ports = atoi(argv[i + 2])) < 0) {
+                fprintf(stderr, "%s: wrong argument\n", arg);
+                return 0;
+            }
+            i += 2;
+        }
         else {
             fprintf(stderr, "unknown option: %s\n", arg);
             return 0;
@@ -517,6 +536,14 @@ int process_arguments (void)
         num_listen_addrs++;
     }
     
+    // resolve local UDP address
+    if (options.local_udp_num_ports >= 0) {
+        if (!BAddr_Parse(&local_udp_addr, options.local_udp_addr, NULL, 0)) {
+            BLog(BLOG_ERROR, "local udp addr: BAddr_Parse failed");
+            return 0;
+        }
+    }
+    
     return 1;
 }
 
@@ -822,6 +849,25 @@ void connection_init (struct client *client, uint16_t conid, BAddr addr, const u
         goto fail2;
     }
     
+    if (options.local_udp_num_ports >= 0) {
+        // try binding (without SO_REUSEADDR, else we'd always end up binding to first port)
+        for (int i = 0; i < options.local_udp_num_ports; i++) {
+            BAddr addr = local_udp_addr;
+            BAddr_SetPort(&addr, hton16(ntoh16(BAddr_GetPort(&addr)) + (uint16_t)i));
+            
+            if (BDatagram_Bind(&con->udp_dgram, addr)) {
+                // set SO_REUSEADDR (we bound without it, but we don't want to take over the address)
+                if (!BDatagram_SetReuseAddr(&con->udp_dgram, 1)) {
+                    client_log(client, BLOG_ERROR, "set SO_REUSEADDR failed");
+                }
+                goto cont;
+            }
+        }
+        
+        client_log(client, BLOG_WARNING, "failed to bind to any local address; proceeding regardless");
+    cont:;
+    }
+    
     // set UDP dgram send address
     BIPAddr ipaddr;
     BIPAddr_InitInvalid(&ipaddr);