Browse Source

Merged UDPbcastInterface into UDPInterface and made it beacon on a different port

Caleb James DeLisle 5 years ago
parent
commit
3fe55c4187

+ 0 - 2
admin/angel/Core.c

@@ -35,7 +35,6 @@
 #include "interface/tuntap/TUNInterface.h"
 #include "interface/tuntap/AndroidWrapper.h"
 #include "interface/UDPInterface_admin.h"
-#include "interface/UDPBcastInterface_admin.h"
 #ifdef HAS_ETH_INTERFACE
 #include "interface/ETHInterface_admin.h"
 #endif
@@ -283,7 +282,6 @@ void Core_init(struct Allocator* alloc,
     InterfaceController_admin_register(nc->ifController, admin, alloc);
     SwitchPinger_admin_register(nc->sp, admin, alloc);
     UDPInterface_admin_register(eventBase, alloc, logger, admin, nc->ifController, fakeNet);
-    UDPBcastInterface_admin_register(eventBase, alloc, logger, admin, nc->ifController);
 #ifdef HAS_ETH_INTERFACE
     ETHInterface_admin_register(eventBase, alloc, logger, admin, nc->ifController);
 #endif

+ 40 - 66
client/Configurator.c

@@ -175,6 +175,38 @@ static void authorizedPasswords(List* list, struct Context* ctx)
     }
 }
 
+static void udpInterfaceSetBeacon(
+    Dict* udp, int beacon, uint16_t beaconPort, int ifNum, struct Context* ctx)
+{
+    if (!beacon) { return; }
+    if (beacon > 2 || beacon < 0) {
+        Log_error(ctx->logger, "interfaces.UDPInterface.beacon may only be 0, 1,or 2");
+        return;
+    }
+    if (!beaconPort) {
+        Log_error(ctx->logger, "interfaces.UDPInterface.beacon requires beaconPort");
+        return;
+    }
+    List* devices = Dict_getListC(udp, "beaconDevices");
+    if (!devices) {
+        Log_error(ctx->logger, "interfaces.UDPInterface.beacon requires beaconDevices");
+        return;
+    }
+
+    // We can cast beacon to an int here because we know it's small enough
+    Log_info(ctx->logger, "Setting beacon mode UDPInterface to [%d].", (int) beacon);
+
+    Dict* d = Dict_new(ctx->alloc);
+    Dict_putIntC(d, "state", beacon, ctx->alloc);
+    Dict_putIntC(d, "interfaceNumber", ifNum, ctx->alloc);
+    rpcCall(String_CONST("UDPInterface_beacon"), d, ctx, ctx->alloc);
+
+    d = Dict_new(ctx->alloc);
+    Dict_putListC(d, "devices", devices, ctx->alloc);
+    Dict_putIntC(d, "interfaceNumber", ifNum, ctx->alloc);
+    rpcCall(String_CONST("UDPInterface_setBroadcastDevices"), d, ctx, ctx->alloc);
+}
+
 static void udpInterface(Dict* config, struct Context* ctx)
 {
     List* ifaces = Dict_getListC(config, "UDPInterface");
@@ -199,10 +231,17 @@ static void udpInterface(Dict* config, struct Context* ctx)
         if (dscp) {
             Dict_putIntC(d, "dscp", *dscp, ctx->alloc);
         }
+        int64_t* beaconPort_p = Dict_getIntC(udp, "beaconPort");
+        uint16_t beaconPort = (beaconPort_p) ? *beaconPort_p : 0;
+        int64_t* beaconP = Dict_getIntC(udp, "beacon");
+        int64_t beacon = (beaconP) ? *beaconP : 0;
+        if (beacon && beaconPort) { Dict_putIntC(d, "beaconPort", beaconPort, ctx->alloc); }
         Dict* resp = NULL;
         rpcCall0(String_CONST("UDPInterface_new"), d, ctx, ctx->alloc, &resp, true);
         int ifNum = *(Dict_getIntC(resp, "interfaceNumber"));
 
+        udpInterfaceSetBeacon(udp, beacon, beaconPort, ifNum, ctx);
+
         // Make the connections.
         Dict* connectTo = Dict_getDictC(udp, "connectTo");
         if (connectTo) {
@@ -381,7 +420,7 @@ static void ethInterfaceSetBeacon(int ifNum, Dict* eth, struct Context* ctx)
     int64_t* beaconP = Dict_getIntC(eth, "beacon");
     if (beaconP) {
         int64_t beacon = *beaconP;
-        if (beacon > 3 || beacon < 0) {
+        if (beacon > 2 || beacon < 0) {
             Log_error(ctx->logger, "interfaces.ETHInterface.beacon may only be 0, 1,or 2");
         } else {
             // We can cast beacon to an int here because we know it's small enough
@@ -484,69 +523,6 @@ static void ethInterface(Dict* config, struct Context* ctx)
     }
 }
 
-static void udpBcastInterfaceSetBeacon(Dict* udp, struct Context* ctx)
-{
-    int64_t* beaconP = Dict_getIntC(udp, "beacon");
-    if (beaconP) {
-        int64_t beacon = *beaconP;
-        if (beacon > 3 || beacon < 0) {
-            Log_error(ctx->logger, "interfaces.UDPBcastInterface.beacon may only be 0, 1,or 2");
-        } else {
-            // We can cast beacon to an int here because we know it's small enough
-            Log_info(ctx->logger, "Setting beacon mode UDPBcastInterface to [%d].", (int) beacon);
-            Dict d = Dict_CONST(String_CONST("state"), Int_OBJ(beacon), NULL);
-            rpcCall(String_CONST("UDPBcastInterface_beacon"), &d, ctx, ctx->alloc);
-        }
-    }
-}
-
-static void udpBcastInterface(Dict* config, struct Context* ctx)
-{
-    Dict* iface = Dict_getDictC(config, "UDPBcastInterface");
-    if (iface) {
-        List* devices = Dict_getListC(iface, "bindDevices");
-        String* addrStr = Dict_getStringC(iface, "bindAddress");
-        if (devices && addrStr) {
-            Log_info(ctx->logger, "Setting up UDPBcastInterface.");
-            Dict* d = Dict_new(ctx->alloc);
-            Dict* res = NULL;
-            if (rpcCall0(String_CONST("UDPBcastInterface_list"), d, ctx, ctx->alloc, &res, false)) {
-                Log_info(ctx->logger, "Getting device list failed");
-                return;
-            }
-            List* devs = Dict_getListC(res, "devices");
-            List* allDevs = List_new(ctx->alloc);
-            uint32_t devCount = List_size(devices);
-            uint32_t availableDevCount = List_size(devs);
-            for (uint32_t j = 0; j < devCount; j++) {
-                String* deviceName = List_getString(devices, j);
-                for (uint32_t i = 0; i < availableDevCount; i++) {
-                    String* avDeviceName = List_getString(devs, i);
-                    if (String_equals(String_CONST("all"), deviceName) ||
-                        String_equals(avDeviceName, deviceName)) {
-                        List_addString(allDevs, avDeviceName, ctx->alloc);
-                        Log_info(ctx->logger, "Using %s as UDPBcastInterface", avDeviceName->bytes);
-                    }
-                }
-            }
-
-            if (List_size(allDevs) > 0) {
-                Log_info(ctx->logger, "Creating new UDPBcastInterface");
-                Dict* rd = Dict_new(ctx->alloc);
-                Dict* resp;
-                Dict_putListC(rd, "bindDevices", allDevs, ctx->alloc);
-                Dict_putStringC(rd, "bindAddress", addrStr, ctx->alloc);
-                if (rpcCall0(String_CONST("UDPBcastInterface_new"),
-                            rd, ctx, ctx->alloc, &resp, false)) {
-                    Log_warn(ctx->logger, "Create UDPBcastInterface failed.");
-                    return;
-                }
-                udpBcastInterfaceSetBeacon(iface, ctx);
-            }
-        }
-    }
-}
-
 static void security(struct Allocator* tempAlloc, List* conf, struct Log* log, struct Context* ctx)
 {
     int seccomp = 1;
@@ -739,8 +715,6 @@ void Configurator_config(Dict* config,
         ethInterface(ifaces, &ctx);
     }
 
-    udpBcastInterface(ifaces, &ctx);
-
     Dict* routerConf = Dict_getDictC(config, "router");
     routerConfig(routerConf, tempAlloc, &ctx);
 

+ 17 - 1
client/cjdroute2.c

@@ -163,7 +163,23 @@ static int genconf(struct Random* rand, bool eth)
            "                // Bind to this port.\n"
            "                \"bind\": \"0.0.0.0:%u\",\n", port);
     printf("                // Set the DSCP value for Qos. Default is 0.\n"
-           "                // \"dscp\": 46,\n");
+           "                // \"dscp\": 46,\n"
+           "\n"
+           "                // Automatically connect to other nodes on the same LAN\n"
+           "                // This works by binding a second port and sending beacons\n"
+           "                // containing the main data port.\n"
+           "                // beacon is a number between 0 and 2:\n"
+           "                //   0 -> do not beacon nor connect to other nodes who beacon\n"
+           "                //   1 -> quiet mode, accept beacons from other nodes only\n"
+           "                //   2 -> send and accept beacons\n"
+           "                // beaconDevices is a list which can contain names of devices such\n"
+           "                // as eth0, as well as broadcast addresses to send to, such as\n"
+           "                // 192.168.101.255, or the pseudo-name \"all\".\n"
+           "                // in order to auto-peer, all cjdns nodes must use the same\n"
+           "                // beaconPort.\n"
+           "                \"beacon\": 2,\n"
+           "                \"beaconDevices\": [ \"all\" ],\n"
+           "                \"beaconPort\": 64512,\n");
     printf("\n"
            "                // Nodes to connect to (IPv4 only).\n"
            "                \"connectTo\":\n"

+ 22 - 29
doc/configure.md

@@ -105,6 +105,22 @@ This specifies the settings for the connection interfaces to your node. Right no
                 // Bind to this port.
                 "bind": "0.0.0.0:33808",
 
+                // Automatically connect to other nodes on the same LAN
+                // This works by binding a second port and sending beacons
+                // containing the main data port.
+                // beacon is a number between 0 and 2:
+                //   0 -> do not beacon nor connect to other nodes who beacon
+                //   1 -> quiet mode, accept beacons from other nodes only
+                //   2 -> send and accept beacons
+                // beaconDevices is a list which can contain names of devices such
+                // as eth0, as well as broadcast addresses to send to, such as
+                // 192.168.101.255, or the pseudo-name "all".
+                // in order to auto-peer, all cjdns nodes must use the same
+                // beaconPort.
+                "beacon": 2,
+                "beaconDevices": [ "all" ],
+                "beaconPort": 64512,
+
                 // Nodes to connect to.
                 "connectTo":
                 {
@@ -147,35 +163,17 @@ This specifies the settings for the connection interfaces to your node. Right no
             }
         ]
         */
-        /*
-        "UDPBcastInterface":
-        {
-            "bindAddress": "0.0.0.0:54200",
-            // Bind to this device (interface name or 'all', not MAC etc.)
-            "bindDevices": ["eth0"],
-
-            // Auto-connect to other cjdns nodes on the same network.
-            // Options:
-            //
-            // 0 -- Disabled.
-            //
-            // 1 -- Accept beacons, this will cause cjdns to accept incoming
-            //      beacon messages and try connecting to the sender.
-            //
-            // 2 -- Accept and send beacons, this will cause cjdns to broadcast
-            //      messages on the local network which contain a randomly
-            //      generated per-session password, other nodes which have this
-            //      set to 1 or 2 will hear the beacon messages and connect
-            //      automatically.
-            //
-            "beacon": 2,
-        }
-        */
     },
 ````
 
 - `UDPInterface`:
     - `bind`: This tells cjdns what IP and port to use for listening to connections.
+    - `beacon`: his controls peer auto-discovery. Set to 0 to disable auto-peering, 1 to use broadcast
+    auto-peering passwords contained in "beacon" messages from other nodes, and 2 to both broadcast and accept beacons.
+    - `beaconDevices`: A list of broadcast devices, including device names such as "eth0", address names and the
+    pseudo-name "all" which means it will use broadcast addresses of all interfaces. For example:
+    `[ "eth0", "wlan0", "192.168.300.255" ]`
+    - `beaconPort`: The UDP port for sending beacon messages on, all nodes must have the same beaconPort to auto-peer.
     - `connectTo`: This is where you put the connection details for peers that you want to connect to. The format for this generally looks like this,
         "12.34.56.78:12345":
         {
@@ -195,11 +193,6 @@ This specifies the settings for the connection interfaces to your node. Right no
     - `beacon`: This controls peer auto-discovery. Set to 0 to disable auto-peering, 1 to use broadcast auto-peering passwords contained in "beacon" messages from other nodes, and 2 to both broadcast and accept beacons.
     - In earlier versions of cjdns, it was necessary to uncomment the ETHInterface if you want to use it, however, now it is uncommented by default.
 
-- `UDPBcastInterface`:
-    - `bindAddress`: This tells cjdns which ipv4 address the UDPBcastInterface should bind to. This may be different depending on your setup. If you have already enable iptables rules please remember open the udp port for udp broadcast send/receive.
-    - `bindDevices`: This tells cjdns which device the UDPBcastInterface should bind to. This may be different depending on your setup.
-    - `beacon`: This controls peer auto-discovery. Set to 0 to disable auto-peering, 1 to use broadcast auto-peering passwords contained in "beacon" messages from other nodes, and 2 to both broadcast and accept beacons.
-
 Router
 ------
 

+ 0 - 184
interface/UDPBcastInterface_admin.c

@@ -1,184 +0,0 @@
-/* vim: set expandtab ts=4 sw=4: */
-/*
- * You may redistribute this program and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-#include "interface/UDPBcastInterface_admin.h"
-#include "benc/Int.h"
-#include "admin/Admin.h"
-#include "exception/Jmp.h"
-#include "memory/Allocator.h"
-#include "net/InterfaceController.h"
-#include "util/AddrTools.h"
-#include "util/Identity.h"
-#include "util/events/UDPBcastIface.h"
-
-struct Context
-{
-    struct EventBase* eventBase;
-    struct Allocator* alloc;
-    struct Log* logger;
-    struct Admin* admin;
-    struct InterfaceController* ic;
-    struct UDPBcastIface* udpIf;
-    int ifNum;
-    Identity
-};
-
-static void newInterface(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
-{
-    struct Context* const ctx = Identity_check((struct Context*) vcontext);
-    List* const bindDevices = Dict_getListC(args, "bindDevices");
-    String* bindAddress = Dict_getStringC(args, "bindAddress");
-    struct Allocator* const alloc = Allocator_child(ctx->alloc);
-
-    if (ctx->ifNum > 0) {
-        Dict out = Dict_CONST(String_CONST("error"),
-                    String_OBJ(String_CONST("UDPBcastInterface already exist")), NULL
-        );
-        Admin_sendMessage(&out, txid, ctx->admin);
-        return;
-    }
-
-    struct Sockaddr_storage ss;
-    if (Sockaddr_parse(bindAddress->bytes, &ss)) {
-        Dict out = Dict_CONST(
-            String_CONST("error"), String_OBJ(String_CONST("Failed to parse address")), NULL
-        );
-        Admin_sendMessage(&out, txid, ctx->admin);
-        return;
-    }
-
-    if (Sockaddr_getFamily(&ss.addr) != Sockaddr_AF_INET ||
-        !Sockaddr_getPort(&ss.addr)) {
-        Dict out = Dict_CONST(String_CONST("error"),
-                String_OBJ(String_CONST("Invalid UDPBcastInterface bind address")), NULL
-        );
-        Admin_sendMessage(&out, txid, ctx->admin);
-        return;
-    }
-
-    struct Jmp jmp;
-    Jmp_try(jmp) {
-        ctx->udpIf = UDPBcastIface_new(
-            ctx->eventBase, &ss.addr, bindDevices, alloc, &jmp.handler, ctx->logger);
-    } Jmp_catch {
-        Dict* out = Dict_new(requestAlloc);
-        Dict_putStringCC(out, "error", jmp.message, requestAlloc);
-        Admin_sendMessage(out, txid, ctx->admin);
-        Allocator_free(alloc);
-        return;
-    }
-
-    String* ifname = String_printf(requestAlloc, "UDPBcast");
-
-    struct InterfaceController_Iface* ici = InterfaceController_newIface(ctx->ic, ifname, alloc);
-    Iface_plumb(&ici->addrIf, &ctx->udpIf->generic.iface);
-
-    Dict* out = Dict_new(requestAlloc);
-    Dict_putStringCC(out, "error", "none", requestAlloc);
-    Dict_putIntC(out, "interfaceNumber", ici->ifNum, requestAlloc);
-    ctx->ifNum = ici->ifNum;
-
-    Admin_sendMessage(out, txid, ctx->admin);
-}
-
-static void beacon(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
-{
-    int64_t* stateP = Dict_getIntC(args, "state");
-    uint32_t state = (stateP) ? ((uint32_t) *stateP) : 0xffffffff;
-    struct Context* ctx = Identity_check((struct Context*) vcontext);
-
-    char* error = NULL;
-    int ret = InterfaceController_beaconState(ctx->ic, ctx->ifNum, state);
-    if (ret == InterfaceController_beaconState_NO_SUCH_IFACE) {
-        error = "invalid interfaceNumber";
-    } else if (ret == InterfaceController_beaconState_INVALID_STATE) {
-        error = "invalid state";
-    } else if (ret) {
-        error = "internal";
-    }
-
-    if (error) {
-        Dict* out = Dict_new(requestAlloc);
-        Dict_putStringCC(out, "error", error, requestAlloc);
-        Admin_sendMessage(out, txid, ctx->admin);
-        return;
-    }
-
-    char* stateStr = "disabled";
-    if (state == InterfaceController_beaconState_newState_ACCEPT) {
-        stateStr = "accepting";
-    } else if (state == InterfaceController_beaconState_newState_SEND) {
-        stateStr = "sending and accepting";
-    }
-
-    if (ctx->udpIf) {
-        UDPBcastIface_setBroadcast(ctx->udpIf, state ? true : false);
-    }
-
-    Dict out = Dict_CONST(
-        String_CONST("error"), String_OBJ(String_CONST("none")), Dict_CONST(
-        String_CONST("state"), Int_OBJ(state), Dict_CONST(
-        String_CONST("stateName"), String_OBJ(String_CONST(stateStr)), NULL
-    )));
-    Admin_sendMessage(&out, txid, ctx->admin);
-}
-
-static void listDevices(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
-{
-    struct Context* ctx = Identity_check((struct Context*) vcontext);
-    List* devices = NULL;
-    struct Jmp jmp;
-    Jmp_try(jmp) {
-        devices = UDPBcastIface_listDevices(requestAlloc, &jmp.handler);
-    } Jmp_catch {
-        Dict* out = Dict_new(requestAlloc);
-        Dict_putStringCC(out, "error", jmp.message, requestAlloc);
-        Admin_sendMessage(out, txid, ctx->admin);
-        return;
-    }
-
-    Dict* out = Dict_new(requestAlloc);
-    Dict_putStringCC(out, "error", "none", requestAlloc);
-    Dict_putListC(out, "devices", devices, requestAlloc);
-    Admin_sendMessage(out, txid, ctx->admin);
-}
-
-void UDPBcastInterface_admin_register(struct EventBase* base,
-                                      struct Allocator* alloc,
-                                      struct Log* logger,
-                                      struct Admin* admin,
-                                      struct InterfaceController* ic)
-{
-    struct Context* ctx = Allocator_clone(alloc, (&(struct Context) {
-        .eventBase = base,
-        .alloc = alloc,
-        .logger = logger,
-        .admin = admin,
-        .ic = ic
-    }));
-    Identity_set(ctx);
-
-    Admin_registerFunction("UDPBcastInterface_new", newInterface, ctx, true,
-        ((struct Admin_FunctionArg[]) {
-            { .name = "bindAddress", .required = 1, .type = "String" },
-            { .name = "bindDevices", .required = 1, .type = "List" }
-        }), admin);
-
-    Admin_registerFunction("UDPBcastInterface_beacon", beacon, ctx, true,
-        ((struct Admin_FunctionArg[]) {
-            { .name = "state", .required = 0, .type = "Int" }
-        }), admin);
-
-    Admin_registerFunction("UDPBcastInterface_list", listDevices, ctx, true, NULL, admin);
-}

+ 0 - 32
interface/UDPBcastInterface_admin.h

@@ -1,32 +0,0 @@
-/* vim: set expandtab ts=4 sw=4: */
-/*
- * You may redistribute this program and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-#ifndef UDPBcastInterface_admin_H
-#define UDPBcastInterface_admin_H
-
-#include "admin/Admin.h"
-#include "memory/Allocator.h"
-#include "net/InterfaceController.h"
-#include "util/log/Log.h"
-#include "util/events/EventBase.h"
-#include "util/Linker.h"
-Linker_require("interface/UDPBcastInterface_admin.c");
-
-void UDPBcastInterface_admin_register(struct EventBase* base,
-                                      struct Allocator* alloc,
-                                      struct Log* logger,
-                                      struct Admin* admin,
-                                      struct InterfaceController* ic);
-
-#endif

+ 309 - 0
interface/UDPInterface.c

@@ -0,0 +1,309 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#include "util/events/libuv/UvWrapper.h"
+#include "interface/UDPInterface.h"
+#include "wire/Message.h"
+#include "util/events/UDPAddrIface.h"
+
+#define ArrayList_TYPE struct Sockaddr
+#define ArrayList_NAME Sockaddr
+#include "util/ArrayList.h"
+
+#define ArrayList_TYPE String
+#define ArrayList_NAME String
+#include "util/ArrayList.h"
+
+struct UDPInterface_pvt
+{
+    struct UDPInterface pub;
+    struct Log* log;
+    struct Allocator* allocator;
+
+    struct Allocator* bcastAddrAlloc;
+    struct ArrayList_Sockaddr* bcastAddrs;
+
+    struct Allocator* bcastIfaceAlloc;
+    struct ArrayList_String* bcastIfaces;
+
+    struct UDPAddrIface* commIf;
+    struct UDPAddrIface* bcastIf;
+
+    struct Iface commSock;
+    struct Iface bcastSock;
+    uint16_t beaconPort_be;
+    uint16_t commPort_be;
+
+    Identity
+};
+
+static struct Sockaddr* mkBcastAddr(
+    uint16_t beaconPort_be,
+    uv_interface_address_t* iface,
+    struct Allocator* alloc)
+{
+    struct sockaddr_in bcast4 = {
+        .sin_family = AF_INET,
+        .sin_port = beaconPort_be,
+        .sin_addr = {
+            .s_addr =
+                (
+                    iface->address.address4.sin_addr.s_addr &
+                    iface->netmask.netmask4.sin_addr.s_addr
+                ) | ~iface->netmask.netmask4.sin_addr.s_addr
+        }
+    };
+    return Sockaddr_fromNative(&bcast4, sizeof(struct sockaddr_in), alloc);
+}
+
+static void updateBcastAddrs(struct UDPInterface_pvt* ctx)
+{
+    bool all = false;
+    for (int i = 0; ctx->bcastIfaces && i < ctx->bcastIfaces->length; i++) {
+        String* iface = ArrayList_String_get(ctx->bcastIfaces, i);
+        if (String_equals(iface, String_CONST("all"))) { all = true; }
+    }
+    uv_interface_address_t* interfaces;
+    int count;
+    int res = uv_interface_addresses(&interfaces, &count);
+    if (res) {
+        Log_info(ctx->log, "uv_interface_addresses failed [%s]", uv_strerror(-res));
+        return;
+    }
+
+    if (ctx->bcastAddrAlloc) { Allocator_free(ctx->bcastAddrAlloc); }
+    struct Allocator* alloc = ctx->bcastAddrAlloc = Allocator_child(ctx->allocator);
+    ctx->bcastAddrs = ArrayList_Sockaddr_new(alloc);
+
+    for (int i = 0; i < count; i++) {
+        if (interfaces[i].is_internal) { continue; }
+        if (interfaces[i].address.address4.sin_family != AF_INET) { continue; }
+        struct Sockaddr* addr = mkBcastAddr(ctx->beaconPort_be, &interfaces[i], alloc);
+        if (!all) {
+            String* addrStr = String_new(Sockaddr_print(addr, alloc), alloc);
+            bool found = false;
+            for (int j = 0; ctx->bcastIfaces && j < ctx->bcastIfaces->length; j++) {
+                String* iface = ArrayList_String_get(ctx->bcastIfaces, j);
+                if (String_equals(iface, addrStr)) { found = true; }
+                if (String_equals(iface, String_CONST(interfaces[i].name))) { found = true; }
+            }
+            if (!found) { continue; }
+        }
+        ArrayList_Sockaddr_add(ctx->bcastAddrs, addr);
+    }
+    uv_free_interface_addresses(interfaces, count);
+}
+
+static Iface_DEFUN sendPacket(struct Message* m, struct Iface* iface)
+{
+    struct UDPInterface_pvt* ctx =
+        Identity_containerOf(iface, struct UDPInterface_pvt, pub.generic.iface);
+
+    Assert_true(m->length > Sockaddr_OVERHEAD);
+    struct Sockaddr* sa = (struct Sockaddr*) m->bytes;
+    Assert_true(m->length > sa->addrLen);
+
+    // Regular traffic
+    if (!(sa->flags & Sockaddr_flags_BCAST)) { return Iface_next(&ctx->commSock, m); }
+
+    updateBcastAddrs(ctx);
+
+    // bcast
+    struct UDPInterface_BroadcastHeader hdr = {
+        .fffffffc_be = Endian_bigEndianToHost32(0xfffffffc),
+        .version = UDPInterface_CURRENT_VERSION,
+        .zero = 0,
+        .commPort_be = ctx->commPort_be
+    };
+    Message_shift(m, -sa->addrLen, NULL);
+    Message_push(m, &hdr, UDPInterface_BroadcastHeader_SIZE, NULL);
+
+    for (int i = 0; i < ctx->bcastAddrs->length; i++) {
+        struct Allocator* tmpAlloc = Allocator_child(ctx->allocator);
+        struct Message* mm = Message_clone(m, tmpAlloc);
+        struct Sockaddr* addr = ArrayList_Sockaddr_get(ctx->bcastAddrs, i);
+        Message_push(mm, addr, addr->addrLen, NULL);
+        Iface_send(&ctx->bcastSock, mm);
+        Allocator_free(tmpAlloc);
+    }
+
+    return NULL;
+}
+
+static Iface_DEFUN fromCommSock(struct Message* m, struct Iface* iface)
+{
+    struct UDPInterface_pvt* ctx =
+        Identity_containerOf(iface, struct UDPInterface_pvt, commSock);
+    return Iface_next(&ctx->pub.generic.iface, m);
+}
+
+static Iface_DEFUN fromBcastSock(struct Message* m, struct Iface* iface)
+{
+    struct UDPInterface_pvt* ctx =
+        Identity_containerOf(iface, struct UDPInterface_pvt, bcastSock);
+    if (m->length < UDPInterface_BroadcastHeader_SIZE + Sockaddr_OVERHEAD) {
+        Log_debug(ctx->log, "DROP runt bcast");
+        return NULL;
+    }
+
+    struct UDPInterface_BroadcastHeader hdr;
+    Message_pop(m, &hdr, UDPInterface_BroadcastHeader_SIZE, NULL);
+
+    if (hdr.fffffffc_be != Endian_hostToBigEndian32(0xfffffffc)) {
+        Log_debug(ctx->log, "DROP bcast bad magic");
+        return NULL;
+    }
+
+    if (hdr.version != UDPInterface_CURRENT_VERSION) {
+        Log_debug(ctx->log, "DROP bcast bad version [%u]", hdr.version);
+        return NULL;
+    }
+
+    if (hdr.zero) {
+        Log_debug(ctx->log, "DROP bcast malformed (zero not zero)");
+        return NULL;
+    }
+
+    uint16_t commPort = Endian_bigEndianToHost16(hdr.commPort_be);
+
+    struct Sockaddr* lladdrInmsg = (struct Sockaddr*) m->bytes;
+    if (m->length < lladdrInmsg->addrLen) {
+        Log_debug(ctx->log, "DROP runt bcast");
+        return NULL;
+    }
+
+    // Fake that it came from the communication port
+    Sockaddr_setPort(lladdrInmsg, commPort);
+    lladdrInmsg->flags |= Sockaddr_flags_BCAST;
+
+    return Iface_next(&ctx->pub.generic.iface, m);
+}
+
+/**
+ * Create a new UDPInterface
+ *
+ * @param eventBase the event context
+ * @param bindAddr the address and port to bind the socket to
+ * @param beaconPort (optional) if specifed, another socket will be created for beacon messages
+ *                  if zero then no other socket will be created.
+ * @param alloc allocator which will be used to create the interface
+ * @param exHandler in case setup fails
+ * @param logger
+ */
+struct UDPInterface* UDPInterface_new(struct EventBase* eventBase,
+                                      struct Sockaddr* bindAddr,
+                                      uint16_t beaconPort,
+                                      struct Allocator* alloc,
+                                      struct Except* exHandler,
+                                      struct Log* logger)
+{
+    if (beaconPort && Sockaddr_getFamily(bindAddr) != Sockaddr_AF_INET) {
+        Except_throw(exHandler, "UDP broadcast only supported by ipv4.");
+    }
+    if (beaconPort && Sockaddr_getPort(bindAddr) == beaconPort) {
+        Except_throw(exHandler, "UDP broadcast port must be different from communication port.");
+    }
+
+    struct UDPAddrIface* uai = UDPAddrIface_new(eventBase, bindAddr, alloc, exHandler, logger);
+
+    uint16_t commPort = Sockaddr_getPort(uai->generic.addr);
+
+    struct UDPInterface_pvt* context =
+        Allocator_clone(alloc, (&(struct UDPInterface_pvt) {
+            .log = logger,
+            .allocator = alloc,
+            .beaconPort_be = Endian_hostToBigEndian16(beaconPort),
+            .commPort_be = Endian_hostToBigEndian16(commPort)
+        }));
+    Identity_set(context);
+    context->pub.generic.addr = uai->generic.addr;
+    context->pub.generic.alloc = alloc;
+    context->pub.generic.iface.send = sendPacket;
+    context->commSock.send = fromCommSock;
+    context->bcastSock.send = fromBcastSock;
+    context->commIf = uai;
+    Iface_plumb(&uai->generic.iface, &context->commSock);
+
+    if (beaconPort) {
+        struct Sockaddr* bcastAddr = Sockaddr_clone(bindAddr, alloc);
+        Sockaddr_setPort(bcastAddr, beaconPort);
+        struct UDPAddrIface* bcast =
+            UDPAddrIface_new(eventBase, bcastAddr, alloc, exHandler, logger);
+        Iface_plumb(&bcast->generic.iface, &context->bcastSock);
+        context->bcastIf = bcast;
+    }
+
+    return &context->pub;
+}
+
+List* UDPInterface_listDevices(struct Allocator* alloc, struct Except* eh)
+{
+    List* out = List_new(alloc);
+    uv_interface_address_t* interfaces;
+    int count;
+    int res = uv_interface_addresses(&interfaces, &count);
+    if (res) { Except_throw(eh, "uv_interface_addresses failed [%s]", uv_strerror(-res)); }
+
+    for (int i = 0; i < count; i++) {
+        if (interfaces[i].is_internal) { continue; }
+        if (interfaces[i].address.address4.sin_family != AF_INET) { continue; }
+        List_addString(out, String_new(interfaces[i].name, alloc), alloc);
+    }
+    uv_free_interface_addresses(interfaces, count);
+    return out;
+}
+
+void UDPInterface_setBroadcastDevices(struct UDPInterface* udpif, List* devices)
+{
+    struct UDPInterface_pvt* ctx = Identity_check((struct UDPInterface_pvt*) udpif);
+    if (ctx->bcastIfaceAlloc) { Allocator_free(ctx->bcastIfaceAlloc); }
+    struct Allocator* alloc = ctx->bcastIfaceAlloc = Allocator_child(ctx->allocator);
+    struct ArrayList_String* bcastIfaces = ctx->bcastIfaces = ArrayList_String_new(alloc);
+    int len = List_size(devices);
+    for (uint32_t i = 0; i < (unsigned) len; i++) {
+        String* dev = List_getString(devices, i);
+        ArrayList_String_add(bcastIfaces, String_clone(dev, alloc));
+    }
+}
+
+List* UDPInterface_getBroadcastDevices(struct UDPInterface* udpif, struct Allocator* alloc)
+{
+    struct UDPInterface_pvt* ctx = Identity_check((struct UDPInterface_pvt*) udpif);
+    List* out = List_new(alloc);
+    for (int i = 0; ctx->bcastIfaces && i < ctx->bcastIfaces->length; i++) {
+        List_addString(out, ArrayList_String_get(ctx->bcastIfaces, i), alloc);
+    }
+    return out;
+}
+
+List* UDPInterface_getBroadcastAddrs(struct UDPInterface* udpif, struct Allocator* alloc)
+{
+    struct UDPInterface_pvt* ctx = Identity_check((struct UDPInterface_pvt*) udpif);
+    updateBcastAddrs(ctx);
+    List* out = List_new(alloc);
+    for (int i = 0; i < ctx->bcastAddrs->length; i++) {
+        char* addr = Sockaddr_print(ArrayList_Sockaddr_get(ctx->bcastAddrs, i), alloc);
+        List_addStringC(out, addr, alloc);
+    }
+    return out;
+}
+
+int UDPInterface_setDSCP(struct UDPInterface* udpif, uint8_t dscp)
+{
+    struct UDPInterface_pvt* ctx = Identity_check((struct UDPInterface_pvt*) udpif);
+    int res = UDPAddrIface_setDSCP(ctx->commIf, dscp);
+    if (res) { return res; }
+    if (ctx->bcastIf) { return UDPAddrIface_setDSCP(ctx->bcastIf, dscp); }
+    return 0;
+}

+ 114 - 0
interface/UDPInterface.h

@@ -0,0 +1,114 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ */
+#ifndef UDPInterface_H
+#define UDPInterface_H
+
+#include "interface/addressable/AddrIface.h"
+#include "benc/List.h"
+#include "util/events/EventBase.h"
+#include "net/InterfaceController.h"
+#include "util/Gcc.h"
+#include "util/Assert.h"
+#include "util/log/Log.h"
+#include "memory/Allocator.h"
+#include "util/events/UDPAddrIface.h"
+#include "util/Linker.h"
+Linker_require("interface/UDPInterface.c");
+
+struct UDPInterface_BroadcastHeader
+{
+    /* Magic (0xfffffffc) */
+    uint32_t fffffffc_be;
+
+    /** UDPInterface_CURRENT_VERSION, no communication is possible with different versions. */
+    uint8_t version;
+
+    /** padding and for future use. */
+    uint8_t zero;
+
+    /** Port used for data communication. */
+    uint16_t commPort_be;
+};
+#define UDPInterface_BroadcastHeader_SIZE 8
+Assert_compileTime(
+    sizeof(struct UDPInterface_BroadcastHeader) == UDPInterface_BroadcastHeader_SIZE
+);
+
+#define UDPInterface_CURRENT_VERSION 0
+
+struct UDPInterface
+{
+    struct AddrIface generic;
+};
+
+/**
+ * Create a new UDPInterface
+ *
+ * @param eventBase the event context
+ * @param bindAddr the address and port to bind the socket to
+ * @param bcastPort (optional) if specifed, another socket will be created for beacon messages
+ *                  if zero then no other socket will be created.
+ * @param alloc allocator which will be used to create the interface
+ * @param exHandler in case setup fails
+ * @param logger
+ */
+struct UDPInterface* UDPInterface_new(struct EventBase* eventBase,
+                                      struct Sockaddr* bindAddr,
+                                      uint16_t bcastPort,
+                                      struct Allocator* alloc,
+                                      struct Except* exHandler,
+                                      struct Log* logger);
+
+/**
+ * List all devices which can be broadcasted to, this will provide the name of the devices.
+ *
+ * @param alloc
+ * @param eh exception in case libuv is unable to get the device list
+ */
+List* UDPInterface_listDevices(struct Allocator* alloc, struct Except* eh);
+
+/**
+ * Specify broadcast devices, this function accepts device names, address names and
+ * the pseudo-name "all" which means it will use broadcast addresses of all interfaces.
+ * To broacdast to a different network, you can simply specify the broadcast address
+ * as if it were a device.
+ * For example: [ "eth0", "wlan0", "192.168.300.255" ]
+ *
+ * @param udpif
+ * @param devices the list of devices to assign
+ */
+void UDPInterface_setBroadcastDevices(struct UDPInterface* udpif, List* devices);
+
+/**
+ * Get the list of broadcast devices which is set using UDPInterface_setBroadcastDevices().
+ * This will return exactly the same list that was set, for the broadcast addresses which
+ * were computed to send to, use UDPInterface_getBroadcastAddrs().
+ */
+List* UDPInterface_getBroadcastDevices(struct UDPInterface* udpif, struct Allocator* alloc);
+
+/**
+ * Get the list of broadcast addresses which will be used for beconing, this is computed
+ * from the list specified by UDPInterface_setBroadcastDevices().
+ */
+List* UDPInterface_getBroadcastAddrs(struct UDPInterface* udpif, struct Allocator* alloc);
+
+/**
+ * Configure the underlying UDP socket(s) to set DSCP on the traffic they send so that a
+ * firewall can recognize them and treat them accordingly. This will set DSCP on both the
+ * data socket and the beacon socket.
+ */
+int UDPInterface_setDSCP(struct UDPInterface* udpif, uint8_t dscp);
+
+#endif

+ 188 - 51
interface/UDPInterface_admin.c

@@ -17,12 +17,17 @@
 #include "exception/Jmp.h"
 #include "memory/Allocator.h"
 #include "net/InterfaceController.h"
-#include "util/events/UDPAddrIface.h"
 #include "util/events/EventBase.h"
 #include "util/events/FakeNetwork.h"
 #include "util/platform/Sockaddr.h"
 #include "crypto/Key.h"
 #include "interface/UDPInterface_admin.h"
+#include "interface/UDPInterface.h"
+#include "util/Identity.h"
+
+#define ArrayList_TYPE struct UDPInterface
+#define ArrayList_NAME UDPInterface
+#include "util/ArrayList.h"
 
 struct Context
 {
@@ -30,43 +35,61 @@ struct Context
     struct Allocator* alloc;
     struct Log* logger;
     struct Admin* admin;
-    struct AddrIface* udpIf;
+    struct ArrayList_UDPInterface* ifaces;
     struct InterfaceController* ic;
     struct FakeNetwork* fakeNet;
+    Identity
 };
 
+static struct UDPInterface* getIface(struct Context* ctx,
+                                     Dict* args,
+                                     String* txid,
+                                     struct Allocator* requestAlloc,
+                                     uint32_t* ifNumP)
+{
+    int64_t* interfaceNumber = Dict_getIntC(args, "interfaceNumber");
+    uint32_t ifNum = (interfaceNumber) ? ((uint32_t) *interfaceNumber) : 0;
+    if (ifNumP) { *ifNumP = ifNum; }
+    struct UDPInterface* udpif = ArrayList_UDPInterface_get(ctx->ifaces, ifNum);
+    if (!udpif) {
+        Dict* out = Dict_new(requestAlloc);
+        Dict_putStringCC(out, "error", "no such interface for interfaceNumber", requestAlloc);
+        Admin_sendMessage(out, txid, ctx->admin);
+    }
+    return udpif;
+}
+
 static void beginConnection(Dict* args,
                             void* vcontext,
                             String* txid,
                             struct Allocator* requestAlloc)
 {
-    struct Context* ctx = vcontext;
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
+
+    uint32_t ifNum = 0;
+    struct UDPInterface* udpIf = getIface(ctx, args, txid, requestAlloc, &ifNum);
+    if (!udpIf) { return; }
 
     String* password = Dict_getStringC(args, "password");
     String* login = Dict_getStringC(args, "login");
     String* publicKey = Dict_getStringC(args, "publicKey");
     String* address = Dict_getStringC(args, "address");
-    int64_t* interfaceNumber = Dict_getIntC(args, "interfaceNumber");
-    uint32_t ifNum = (interfaceNumber) ? ((uint32_t) *interfaceNumber) : 0;
     String* peerName = Dict_getStringC(args, "peerName");
-    String* error = NULL;
+    char* error = NULL;
 
     Log_debug(ctx->logger, "Peering with [%s]", publicKey->bytes);
 
     struct Sockaddr_storage ss;
     uint8_t pkBytes[32];
     int ret;
-    if (interfaceNumber && *interfaceNumber < 0) {
-        error = String_CONST("negative interfaceNumber");
-
-    } else if ((ret = Key_parse(publicKey, pkBytes, NULL))) {
-        error = String_CONST(Key_parse_strerror(ret));
+    if ((ret = Key_parse(publicKey, pkBytes, NULL))) {
+        error = Key_parse_strerror(ret);
 
     } else if (Sockaddr_parse(address->bytes, &ss)) {
-        error = String_CONST("unable to parse ip address and port.");
+        error = "unable to parse ip address and port.";
 
-    } else if (Sockaddr_getFamily(&ss.addr) != Sockaddr_getFamily(ctx->udpIf->addr)) {
-        error = String_CONST("different address type than this socket is bound to.");
+    } else if (Sockaddr_getFamily(&ss.addr) != Sockaddr_getFamily(udpIf->generic.addr)) {
+        error = "different address type than this socket is bound to.";
 
     } else {
 
@@ -95,86 +118,79 @@ static void beginConnection(Dict* args,
         if (ret) {
             switch(ret) {
                 case InterfaceController_bootstrapPeer_BAD_IFNUM:
-                    error = String_CONST("no such interface for interfaceNumber");
+                    // Should never happen, should be caught in getIface()
+                    error = "interface deregistered";
                     break;
 
                 case InterfaceController_bootstrapPeer_BAD_KEY:
-                    error = String_CONST("invalid cjdns public key.");
+                    error = "invalid cjdns public key.";
                     break;
 
                 case InterfaceController_bootstrapPeer_OUT_OF_SPACE:
-                    error = String_CONST("no more space to register with the switch.");
+                    error = "no more space to register with the switch.";
                     break;
 
                 default:
-                    error = String_CONST("unknown error");
+                    error = "unknown error";
                     break;
             }
         } else {
-            error = String_CONST("none");
+            error = "none";
         }
     }
 
-    Dict out = Dict_CONST(String_CONST("error"), String_OBJ(error), NULL);
-    Admin_sendMessage(&out, txid, ctx->admin);
+    Dict* out = Dict_new(requestAlloc);
+    Dict_putStringCC(out, "error", error, requestAlloc);
+    Admin_sendMessage(out, txid, ctx->admin);
 }
 
-static struct AddrIface* setupLibuvUDP(struct Context* ctx,
+static struct UDPInterface* setupLibuvUDP(struct Context* ctx,
                                        struct Sockaddr* addr,
+                                       uint16_t beaconPort,
                                        uint8_t dscp,
                                        String* txid,
                                        struct Allocator* alloc)
 {
-    struct UDPAddrIface* udpIf = NULL;
+    struct UDPInterface* udpIf = NULL;
     struct Jmp jmp;
     Jmp_try(jmp) {
-        udpIf = UDPAddrIface_new(ctx->eventBase, addr, alloc, &jmp.handler, ctx->logger);
+        udpIf = UDPInterface_new(
+            ctx->eventBase, addr, beaconPort, alloc, &jmp.handler, ctx->logger);
         if (dscp) {
-            if (UDPAddrIface_setDSCP(udpIf, dscp)) {
+            if (UDPInterface_setDSCP(udpIf, dscp)) {
                 Log_warn(ctx->logger, "Set DSCP failed");
             }
         }
     } Jmp_catch {
-        String* errStr = String_CONST(jmp.message);
-        Dict out = Dict_CONST(String_CONST("error"), String_OBJ(errStr), NULL);
-        Admin_sendMessage(&out, txid, ctx->admin);
+        Dict* out = Dict_new(alloc);
+        Dict_putStringCC(out, "error", jmp.message, alloc);
+        Admin_sendMessage(out, txid, ctx->admin);
         Allocator_free(alloc);
         return NULL;
     }
-    return &udpIf->generic;
-}
-
-static struct AddrIface* setupFakeUDP(struct FakeNetwork* fakeNet,
-                                      struct Sockaddr* addr,
-                                      struct Allocator* alloc)
-{
-    struct FakeNetwork_UDPIface* fni = FakeNetwork_iface(fakeNet, addr, alloc);
-    return &fni->generic;
+    return udpIf;
 }
 
 static void newInterface2(struct Context* ctx,
                           struct Sockaddr* addr,
                           uint8_t dscp,
                           String* txid,
-                          struct Allocator* requestAlloc)
+                          struct Allocator* requestAlloc,
+                          uint16_t beaconPort)
 {
     struct Allocator* const alloc = Allocator_child(ctx->alloc);
-    struct AddrIface* ai;
-    if (ctx->fakeNet) {
-        ai = setupFakeUDP(ctx->fakeNet, addr, alloc);
-    } else {
-        ai = setupLibuvUDP(ctx, addr, dscp, txid, alloc);
-    }
-    if (!ai) { return; }
-    ctx->udpIf = ai;
+    struct UDPInterface* udpif = setupLibuvUDP(ctx, addr, beaconPort, dscp, txid, alloc);
+    if (!udpif) { return; }
+
     struct InterfaceController_Iface* ici =
         InterfaceController_newIface(ctx->ic, String_CONST("UDP"), alloc);
-    Iface_plumb(&ici->addrIf, &ai->iface);
+    Iface_plumb(&ici->addrIf, &udpif->generic.iface);
+    ArrayList_UDPInterface_put(ctx->ifaces, ici->ifNum, udpif);
 
     Dict* out = Dict_new(requestAlloc);
     Dict_putStringCC(out, "error", "none", requestAlloc);
     Dict_putIntC(out, "interfaceNumber", ici->ifNum, requestAlloc);
-    char* printedAddr = Sockaddr_print(ai->addr, requestAlloc);
+    char* printedAddr = Sockaddr_print(udpif->generic.addr, requestAlloc);
     Dict_putStringCC(out,
                    "bindAddress",
                    printedAddr,
@@ -185,10 +201,12 @@ static void newInterface2(struct Context* ctx,
 
 static void newInterface(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
 {
-    struct Context* ctx = vcontext;
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
     String* bindAddress = Dict_getStringC(args, "bindAddress");
     int64_t* dscpValue = Dict_getIntC(args, "dscp");
     uint8_t dscp = dscpValue ? ((uint8_t) *dscpValue) : 0;
+    int64_t* beaconPort_p = Dict_getIntC(args, "beaconPort");
+    uint16_t beaconPort = beaconPort_p ? ((uint16_t) *beaconPort_p) : 0;
     struct Sockaddr_storage addr;
     if (Sockaddr_parse((bindAddress) ? bindAddress->bytes : "0.0.0.0", &addr)) {
         Dict out = Dict_CONST(
@@ -197,7 +215,99 @@ static void newInterface(Dict* args, void* vcontext, String* txid, struct Alloca
         Admin_sendMessage(&out, txid, ctx->admin);
         return;
     }
-    newInterface2(ctx, &addr.addr, dscp, txid, requestAlloc);
+    newInterface2(ctx, &addr.addr, dscp, txid, requestAlloc, beaconPort);
+}
+
+static void listDevices(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
+{
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
+    Dict* out = Dict_new(requestAlloc);
+    struct Jmp jmp;
+    Jmp_try(jmp) {
+        List* list = UDPInterface_listDevices(requestAlloc, &jmp.handler);
+        Dict_putListC(out, "ret", list, requestAlloc);
+    } Jmp_catch {
+        Dict_putStringCC(out, "error", jmp.message, requestAlloc);
+    }
+    Admin_sendMessage(out, txid, ctx->admin);
+}
+
+static void setBroadcastDevices(
+    Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
+{
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
+    struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
+    if (!udpif) { return; }
+    UDPInterface_setBroadcastDevices(udpif, Dict_getListC(args, "devices"));
+    Dict* out = Dict_new(requestAlloc);
+    Dict_putStringCC(out, "error", "none", requestAlloc);
+    Admin_sendMessage(out, txid, ctx->admin);
+}
+
+static void getBroadcastDevices(
+    Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
+{
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
+    struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
+    if (!udpif) { return; }
+    Dict* out = Dict_new(requestAlloc);
+    Dict_putStringCC(out, "error", "none", requestAlloc);
+    List* devices = UDPInterface_getBroadcastDevices(udpif, requestAlloc);
+    Dict_putListC(out, "devices", devices, requestAlloc);
+    Admin_sendMessage(out, txid, ctx->admin);
+}
+
+static void getBroadcastAddrs(
+    Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
+{
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
+    struct UDPInterface* udpif = getIface(ctx, args, txid, requestAlloc, NULL);
+    if (!udpif) { return; }
+    Dict* out = Dict_new(requestAlloc);
+    Dict_putStringCC(out, "error", "none", requestAlloc);
+    List* addrs = UDPInterface_getBroadcastAddrs(udpif, requestAlloc);
+    Dict_putListC(out, "addrs", addrs, requestAlloc);
+    Admin_sendMessage(out, txid, ctx->admin);
+}
+
+static void beacon(Dict* args, void* vcontext, String* txid, struct Allocator* requestAlloc)
+{
+    int64_t* stateP = Dict_getIntC(args, "state");
+    int64_t* ifNumP = Dict_getIntC(args, "interfaceNumber");
+    uint32_t ifNum = (ifNumP) ? ((uint32_t) *ifNumP) : 0;
+    uint32_t state = (stateP) ? ((uint32_t) *stateP) : 0xffffffff;
+    struct Context* ctx = Identity_check((struct Context*) vcontext);
+
+    char* error = NULL;
+    int ret = InterfaceController_beaconState(ctx->ic, ifNum, state);
+    if (ret == InterfaceController_beaconState_NO_SUCH_IFACE) {
+        error = "invalid interfaceNumber";
+    } else if (ret == InterfaceController_beaconState_INVALID_STATE) {
+        error = "invalid state";
+    } else if (ret) {
+        error = "internal";
+    }
+
+    if (error) {
+        Dict* out = Dict_new(requestAlloc);
+        Dict_putStringCC(out, "error", error, requestAlloc);
+        Admin_sendMessage(out, txid, ctx->admin);
+        return;
+    }
+
+    char* stateStr = "disabled";
+    if (state == InterfaceController_beaconState_newState_ACCEPT) {
+        stateStr = "accepting";
+    } else if (state == InterfaceController_beaconState_newState_SEND) {
+        stateStr = "sending and accepting";
+    }
+
+    Dict out = Dict_CONST(
+        String_CONST("error"), String_OBJ(String_CONST("none")), Dict_CONST(
+        String_CONST("state"), Int_OBJ(state), Dict_CONST(
+        String_CONST("stateName"), String_OBJ(String_CONST(stateStr)), NULL
+    )));
+    Admin_sendMessage(&out, txid, ctx->admin);
 }
 
 void UDPInterface_admin_register(struct EventBase* base,
@@ -215,11 +325,14 @@ void UDPInterface_admin_register(struct EventBase* base,
         .ic = ic,
         .fakeNet = fakeNet
     }));
+    Identity_set(ctx);
+    ctx->ifaces = ArrayList_UDPInterface_new(alloc);
 
     Admin_registerFunction("UDPInterface_new", newInterface, ctx, true,
         ((struct Admin_FunctionArg[]) {
             { .name = "bindAddress", .required = 0, .type = "String" },
-            { .name = "dscp", .required = 0, .type = "Int" }
+            { .name = "dscp", .required = 0, .type = "Int" },
+            { .name = "beaconPort", .required = 0, .type = "Int" }
         }), admin);
 
     Admin_registerFunction("UDPInterface_beginConnection", beginConnection, ctx, true,
@@ -230,4 +343,28 @@ void UDPInterface_admin_register(struct EventBase* base,
             { .name = "address", .required = 1, .type = "String" },
             { .name = "login", .required = 0, .type = "String" }
         }), admin);
+
+    Admin_registerFunction("UDPInterface_listDevices", listDevices, ctx, true, NULL, admin);
+
+    Admin_registerFunction("UDPInterface_setBroadcastDevices", setBroadcastDevices, ctx, true,
+        ((struct Admin_FunctionArg[]) {
+            { .name = "interfaceNumber", .required = 0, .type = "Int" },
+            { .name = "devices", .required = 1, .type = "List" }
+        }), admin);
+
+    Admin_registerFunction("UDPInterface_getBroadcastDevices", getBroadcastDevices, ctx, true,
+        ((struct Admin_FunctionArg[]) {
+            { .name = "interfaceNumber", .required = 0, .type = "Int" }
+        }), admin);
+
+    Admin_registerFunction("UDPInterface_getBroadcastAddrs", getBroadcastAddrs, ctx, true,
+        ((struct Admin_FunctionArg[]) {
+            { .name = "interfaceNumber", .required = 0, .type = "Int" }
+        }), admin);
+
+    Admin_registerFunction("UDPInterface_beacon", beacon, ctx, true,
+        ((struct Admin_FunctionArg[]) {
+            { .name = "interfaceNumber", .required = 0, .type = "Int" },
+            { .name = "state", .required = 0, .type = "Int" }
+        }), admin);
 }

+ 6 - 1
net/InterfaceController.c

@@ -532,13 +532,18 @@ static Iface_DEFUN handleBeacon(struct Message* msg, struct InterfaceController_
         return NULL;
     }
 
-    if (msg->length < Headers_Beacon_SIZE) {
+    if (msg->length < Sockaddr_OVERHEAD) {
         Log_debug(ic->logger, "[%s] Dropping runt beacon", ici->name->bytes);
         return NULL;
     }
 
     struct Sockaddr* lladdrInmsg = (struct Sockaddr*) msg->bytes;
 
+    if (msg->length < lladdrInmsg->addrLen + Headers_Beacon_SIZE) {
+        Log_debug(ic->logger, "[%s] Dropping runt beacon", ici->name->bytes);
+        return NULL;
+    }
+
     // clear the bcast flag
     lladdrInmsg->flags = 0;
 

+ 7 - 3
util/Defined.h

@@ -23,8 +23,12 @@
  *     doLinuxSpecificStuff();
  * }
  */
-#define Defined_Q <?js return String.fromCharCode(34); ?>
-#define Defined(macro) \
-    <?js return ( Defined_Q macro Defined_Q === ' ' + #macro + ' ' ) ? '0' : '1'; ?>
+#ifdef __INTELLISENSE__
+    #define Defined(macro) 1
+#else
+    #define Defined_Q <?js return String.fromCharCode(34); ?>
+    #define Defined(macro) \
+        <?js return ( Defined_Q macro Defined_Q === ' ' + #macro + ' ' ) ? '0' : '1'; ?>
+#endif
 
 #endif

+ 6 - 1
util/Gcc.h

@@ -84,7 +84,12 @@
     #define Gcc_USE_RET
 #endif
 
-#define Gcc_SHORT_FILE <?js return '"'+__FILE__.substring(__FILE__.lastIndexOf('/')+1)+'"'; ?>
+#ifdef __INTELLISENSE__
+    #define Gcc_SHORT_FILE __FILE__
+#else
+    #define Gcc_SHORT_FILE <?js return '"'+__FILE__.substring(__FILE__.lastIndexOf('/')+1)+'"'; ?>
+#endif
+
 #define Gcc_FILE Gcc_SHORT_FILE
 #define Gcc_LINE __LINE__
 

+ 3 - 3
util/Identity.h

@@ -18,11 +18,11 @@
 #include "util/Assert.h"
 #include "util/Constant.h"
 
-<?js file.Identity_hash = "0x" + Constant_randHexString(16) + "ull"; ?>
+#if defined(Identity_CHECK)
 
-#define Identity_MAGIC ((unsigned long) <?js return file.Identity_hash ?>)
+    <?js file.Identity_hash = "0x" + Constant_randHexString(16) + "ull"; ?>
 
-#if defined(Identity_CHECK)
+    #define Identity_MAGIC ((unsigned long) <?js return file.Identity_hash ?>)
 
     /** This goes in each structure which will be checked. */
     #define Identity \

+ 4 - 1
util/Linker.h

@@ -16,8 +16,11 @@
 #define Linker_H
 
 // Trailing anonymous struct swollows the semicolon.
+#ifdef __INTELLISENSE__
+#define Linker_require(req)
+#else
 #define Linker_require(req) \
     struct Linker_x<?js file.links.push(req); \
         return Math.random(16).toString().replace(/[^0-9]/g,'') ?>
-
+#endif
 #endif

+ 5 - 1
util/UniqueName.h

@@ -15,6 +15,10 @@
 #ifndef UniqueName_H
 #define UniqueName_H
 
+#ifdef __INTELLISENSE__
+#define UniqueName_get()
+#define UniqueName_last()
+#else
 <?js file.Constant_JS = file.Constant_JS || require('util/Constant.js'); ?>
 
 #define UniqueName_get() <?js return \
@@ -23,5 +27,5 @@
 ?>
 
 #define UniqueName_last() <?js return file.UniqueName_last; ?>
-
+#endif
 #endif

+ 5 - 0
util/events/UDPAddrIface.h

@@ -24,6 +24,8 @@
 #include "util/Linker.h"
 Linker_require("util/events/libuv/UDPAddrIface.c");
 
+#include <stdbool.h>
+
 #define UDPAddrIface_PADDING_AMOUNT 512
 #define UDPAddrIface_BUFFER_CAP 3496
 
@@ -50,4 +52,7 @@ struct UDPAddrIface* UDPAddrIface_new(struct EventBase* base,
                                       struct Log* logger);
 
 int UDPAddrIface_setDSCP(struct UDPAddrIface* iface, uint8_t dscp);
+
+int UDPAddrIface_setBroadcast(struct UDPAddrIface* iface, bool enable);
+
 #endif

+ 0 - 83
util/events/UDPBcastIface.h

@@ -1,83 +0,0 @@
-/* vim: set expandtab ts=4 sw=4: */
-/*
- * You may redistribute this program and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-#ifndef UDPBcastIface_H
-#define UDPBcastIface_H
-
-#include "benc/List.h"
-#include "exception/Except.h"
-#include "interface/Iface.h"
-#include "interface/addressable/AddrIface.h"
-#include "memory/Allocator.h"
-#include "util/events/EventBase.h"
-#include "util/Gcc.h"
-#include "util/log/Log.h"
-#include "util/Linker.h"
-
-#include <stdbool.h>
-Linker_require("util/events/libuv/UDPBcastIface.c");
-
-#define UDPBcastIface_CURRENT_VERSION 0
-#define UDPBcastIface_PADDING_AMOUNT 512
-#define UDPBcastIface_BUFFER_CAP 3496
-
-/** Maximum number of bytes to hold in queue before dropping packets. */
-#define UDPBcastIface_MAX_QUEUE 16384
-
-#define UDPBcastIface_CURRENT_VERSION 0
-Gcc_PACKED
-struct UDPBcastIface_Header
-{
-    /** UDPBcastIface_CURRENT_VERSION, no communication is possible with different versions. */
-    uint8_t version;
-
-    /** padding and for future use. */
-    uint8_t zero;
-
-    /** Length of the content (excluding header) */
-    uint16_t length_be;
-
-    uint8_t bcast;
-    uint8_t reversed;
-
-    /** Pad to align boundry, also magic. */
-    uint16_t magic_be;
-};
-#define UDPBcastIface_Header_SIZE 8
-Assert_compileTime(sizeof(struct UDPBcastIface_Header) == UDPBcastIface_Header_SIZE);
-
-struct UDPBcastIface
-{
-    struct AddrIface generic;
-};
-
-/**
- * @param base the event loop context.
- * @param bindAddr the address/port to bind to.
- * @param allocator the memory allocator for this message.
- * @param exHandler the handler to deal with whatever exception arises.
- * @param logger
- * @return a new UDPBcastInterfaceBase.
- */
-struct UDPBcastIface* UDPBcastIface_new(struct EventBase* base,
-                                        struct Sockaddr* bindAddr,
-                                        const List* bindDevices,
-                                        struct Allocator* allocator,
-                                        struct Except* exHandler,
-                                        struct Log* logger);
-
-int UDPBcastIface_setBroadcast(struct UDPBcastIface* iface, bool enable);
-List* UDPBcastIface_listDevices(struct Allocator* alloc, struct Except* eh);
-
-#endif

+ 6 - 0
util/events/libuv/UDPAddrIface.c

@@ -252,6 +252,12 @@ int UDPAddrIface_setDSCP(struct UDPAddrIface* iface, uint8_t dscp)
     return res;
 }
 
+int UDPAddrIface_setBroadcast(struct UDPAddrIface* iface, bool enable)
+{
+    struct UDPAddrIface_pvt* context = Identity_check((struct UDPAddrIface_pvt*) iface);
+    return uv_udp_set_broadcast(&context->uvHandle, enable ? 1 : 0);
+}
+
 struct UDPAddrIface* UDPAddrIface_new(struct EventBase* eventBase,
                                       struct Sockaddr* addr,
                                       struct Allocator* alloc,

+ 0 - 447
util/events/libuv/UDPBcastIface.c

@@ -1,447 +0,0 @@
-/* vim: set expandtab ts=4 sw=4: */
-/*
- * You may redistribute this program and/or modify it under the terms of
- * the GNU General Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>.
- */
-#include "util/events/libuv/UvWrapper.h"
-#include "exception/Except.h"
-#include "interface/Iface.h"
-#include "util/events/UDPBcastIface.h"
-#include "memory/Allocator.h"
-#include "util/events/libuv/EventBase_pvt.h"
-#include "util/platform/Sockaddr.h"
-#include "util/Assert.h"
-#include "util/Identity.h"
-#include "wire/Message.h"
-#include "wire/Error.h"
-#include "util/Hex.h"
-#include "benc/String.h"
-
-struct UDPBcastIface_pvt
-{
-    struct UDPBcastIface pub;
-
-    struct Allocator* allocator;
-
-    struct Log* logger;
-
-    /** Job to close the handle when the allocator is freed */
-    struct Allocator_OnFreeJob* closeHandleOnFree;
-
-    /** Job which blocks the freeing until the callback completes */
-    struct Allocator_OnFreeJob* blockFreeInsideCallback;
-
-    uv_udp_t uvHandle;
-    int queueLen;
-    bool bcast;
-    List* devices;
-
-    /** true if we are inside of the callback, used by blockFreeInsideCallback */
-    int inCallback;
-
-    Identity
-};
-
-struct UDPBcastIface_WriteRequest_pvt {
-    uv_udp_send_t uvReq;
-    int32_t length;
-    struct UDPBcastIface_pvt* udp;
-    struct Message* msg;
-    struct Allocator* alloc;
-    Identity
-};
-
-static struct UDPBcastIface_pvt* ifaceForHandle(uv_udp_t* handle)
-{
-    char* hp = ((char*)handle) - offsetof(struct UDPBcastIface_pvt, uvHandle);
-    return Identity_check((struct UDPBcastIface_pvt*) hp);
-}
-
-static void sendComplete(uv_udp_send_t* uvReq, int error)
-{
-    struct UDPBcastIface_WriteRequest_pvt* req =
-        Identity_check((struct UDPBcastIface_WriteRequest_pvt*) uvReq);
-    if (error) {
-        Log_debug(req->udp->logger, "DROP Failed to write to UDPBcastIface [%s]",
-                  uv_strerror(error) );
-    }
-    Assert_true(req->msg->length == req->length);
-    req->udp->queueLen -= req->msg->length;
-    Assert_true(req->udp->queueLen >= 0);
-    Allocator_free(req->alloc);
-}
-
-static void sendPacket(struct Message* m, struct sockaddr* addr, struct Iface* iface)
-{
-    struct Message* msg;
-    struct UDPBcastIface_pvt* context = Identity_check((struct UDPBcastIface_pvt*) iface);
-
-    // This allocator will hold the message allocator in existance after it is freed.
-    struct Allocator* reqAlloc = Allocator_child(context->allocator);
-    msg = Message_clone(m, reqAlloc);
-
-    struct UDPBcastIface_WriteRequest_pvt* req =
-        Allocator_clone(reqAlloc, (&(struct UDPBcastIface_WriteRequest_pvt) {
-            .udp = context,
-            .msg = msg,
-            .alloc = reqAlloc
-        }));
-    Identity_set(req);
-
-    req->length = msg->length;
-
-    uv_buf_t buffers[] = {
-        { .base = (char*)msg->bytes, .len = msg->length }
-    };
-
-    int ret = uv_udp_send(&req->uvReq, &context->uvHandle, buffers, 1,
-                          addr, (uv_udp_send_cb)&sendComplete);
-
-    if (ret) {
-        Log_info(context->logger, "DROP Failed writing to UDPBcastIface [%s]",
-                 uv_strerror(ret));
-        Allocator_free(req->alloc);
-        return;
-    }
-    context->queueLen += msg->length;
-}
-
-static Iface_DEFUN incomingFromIface(struct Message* m, struct Iface* iface)
-{
-    struct UDPBcastIface_pvt* context = Identity_check((struct UDPBcastIface_pvt*) iface);
-
-    Assert_true(m->length >= Sockaddr_OVERHEAD);
-
-    if ((((struct Sockaddr*)m->bytes)->flags & Sockaddr_flags_BCAST) && !context->bcast) {
-        Log_debug(context->logger, "Attempted bcast with bcast disabled");
-        return NULL;
-    }
-
-    if (context->queueLen > UDPBcastIface_MAX_QUEUE) {
-        Log_warn(context->logger, "DROP Maximum queue length reached");
-        return NULL;
-    }
-
-    struct Sockaddr* sa = (struct Sockaddr*) m->bytes;
-    struct Sockaddr_storage ss;
-    Message_pop(m, &ss, sa->addrLen, NULL);
-
-    struct UDPBcastIface_Header hdr = {
-        .version = UDPBcastIface_CURRENT_VERSION,
-        .zero = 0,
-        .length_be = Endian_hostToBigEndian16(m->length + UDPBcastIface_Header_SIZE),
-        .bcast = 0,
-        .reversed = 0,
-        .magic_be= Endian_hostToBigEndian16(0xfc00),
-    };
-
-    if (sa->flags & Sockaddr_flags_BCAST) {
-        // We will send the message to bcast addr of all selected interfaces
-        uv_interface_address_t* interfaces;
-        int i, j, count;
-        int res = uv_interface_addresses(&interfaces, &count);
-        if (res) {
-            Log_warn(context->logger, "DROP message for none interface available");
-            return NULL;
-        }
-
-        struct Allocator* tmpAlloc = Allocator_child(context->allocator);
-        int32_t bcastCount = List_size(context->devices);
-
-        hdr.bcast = 1;
-        Message_push(m, &hdr, UDPBcastIface_Header_SIZE, NULL);
-
-        for (i = 0; i < count; i++) {
-            if (interfaces[i].is_internal) { continue; }
-            if (interfaces[i].address.address4.sin_family != AF_INET) { continue; }
-
-            for (j = 0; j < bcastCount; j++) {
-                String* device = List_getString(context->devices, j);
-                if (!CString_strcmp(interfaces[i].name, device->bytes)) { break; }
-            }
-
-            if (j == bcastCount) { continue; }
-
-            // calculate the broadcast address
-            struct sockaddr_in bcast4 = {
-                .sin_family = AF_INET,
-                .sin_port = htons(Sockaddr_getPort(context->pub.generic.addr)),
-                .sin_addr = { .s_addr =
-                    (interfaces[i].address.address4.sin_addr.s_addr &
-                     interfaces[i].netmask.netmask4.sin_addr.s_addr) |
-                        ~interfaces[i].netmask.netmask4.sin_addr.s_addr}
-            };
-
-            sendPacket(m, (struct sockaddr*)&bcast4, iface);
-        }
-        uv_free_interface_addresses(interfaces, count);
-        Allocator_free(tmpAlloc);
-    } else {
-        Message_push(m, &hdr, UDPBcastIface_Header_SIZE, NULL);
-        sendPacket(m, (struct sockaddr*)ss.nativeAddr, iface);
-    }
-
-    return NULL;
-}
-
-#if UDPBcastIface_PADDING_AMOUNT < 8
-    #error
-#endif
-#define ALLOC(buff) (((struct Allocator**) &(buff[-(8 + (((uintptr_t)buff) % 8))]))[0])
-
-static void incoming(uv_udp_t* handle,
-                     ssize_t nread,
-                     const uv_buf_t* buf,
-                     const struct sockaddr* addr,
-                     unsigned flags)
-{
-    struct UDPBcastIface_pvt* context = ifaceForHandle(handle);
-
-    context->inCallback = 1;
-
-    // Grab out the allocator which was placed there by allocate()
-    struct Allocator* alloc = buf->base ? ALLOC(buf->base) : NULL;
-
-    // if nread < 0, we used to log uv_last_error, which doesn't exist anymore.
-    if (nread == 0) {
-        // Happens constantly
-        //Log_debug(context->logger, "0 length read");
-
-    } else if (nread < UDPBcastIface_Header_SIZE) {
-        Log_debug(context->logger, "Failed to receive udp bcast frame");
-    } else {
-        do {
-            struct Message* m = Allocator_calloc(alloc, sizeof(struct Message), 1);
-            m->length = nread;
-            m->padding = UDPBcastIface_PADDING_AMOUNT + context->pub.generic.addr->addrLen;
-            m->capacity = buf->len;
-            m->bytes = (uint8_t*)buf->base;
-            m->alloc = alloc;
-
-
-            struct UDPBcastIface_Header hdr;
-            Message_pop(m, &hdr, UDPBcastIface_Header_SIZE, NULL);
-
-            if (hdr.version != UDPBcastIface_CURRENT_VERSION) {
-                Log_debug(context->logger, "DROP unknown version");
-                break;
-            }
-
-            uint16_t reportedLength = Endian_bigEndianToHost16(hdr.length_be);
-            reportedLength -= UDPBcastIface_Header_SIZE;
-            if (m->length != reportedLength) {
-                if (m->length < reportedLength) {
-                    Log_debug(context->logger, "DROP size field is larger than frame");
-                    break;
-                }
-                m->length = reportedLength;
-            }
-
-            if (hdr.magic_be != Endian_hostToBigEndian16(0xfc00)) {
-                Log_debug(context->logger, "DROP bad magic");
-                break;
-            }
-
-            if (!context->bcast) {
-                Log_debug(context->logger, "Drop packet with bcast disabled");
-                break;
-            }
-
-            struct Sockaddr laddr;
-            Bits_memcpy(&laddr, context->pub.generic.addr, Sockaddr_OVERHEAD);
-            if (hdr.bcast) {
-                laddr.flags |= Sockaddr_flags_BCAST;
-            }
-
-            Message_push(m, addr, context->pub.generic.addr->addrLen - Sockaddr_OVERHEAD, NULL);
-
-            // make sure the sockaddr doesn't have crap in it which will
-            // prevent it from being used as a lookup key
-            Sockaddr_normalizeNative((struct sockaddr*) m->bytes);
-
-            Message_push(m, &laddr, Sockaddr_OVERHEAD, NULL);
-
-            Iface_send(&context->pub.generic.iface, m);
-        } while (0);
-    }
-
-    if (alloc) {
-        Allocator_free(alloc);
-    }
-
-    context->inCallback = 0;
-    if (context->blockFreeInsideCallback) {
-        Allocator_onFreeComplete((struct Allocator_OnFreeJob*) context->blockFreeInsideCallback);
-    }
-}
-
-static void allocate(uv_handle_t* handle, size_t size, uv_buf_t* buf)
-{
-    struct UDPBcastIface_pvt* context = ifaceForHandle((uv_udp_t*)handle);
-
-    size = UDPBcastIface_BUFFER_CAP;
-    size_t fullSize = size + UDPBcastIface_PADDING_AMOUNT + context->pub.generic.addr->addrLen;
-
-    struct Allocator* child = Allocator_child(context->allocator);
-    char* buff = Allocator_malloc(child, fullSize);
-    buff += UDPBcastIface_PADDING_AMOUNT + context->pub.generic.addr->addrLen;
-
-    ALLOC(buff) = child;
-
-    buf->base = buff;
-    buf->len = size;
-}
-
-static void onClosed(uv_handle_t* wasClosed)
-{
-    struct UDPBcastIface_pvt* context =
-        Identity_check((struct UDPBcastIface_pvt*) wasClosed->data);
-    Allocator_onFreeComplete((struct Allocator_OnFreeJob*) context->closeHandleOnFree);
-}
-
-static int closeHandleOnFree(struct Allocator_OnFreeJob* job)
-{
-    struct UDPBcastIface_pvt* context =
-        Identity_check((struct UDPBcastIface_pvt*) job->userData);
-    context->closeHandleOnFree = job;
-    uv_close((uv_handle_t*)&context->uvHandle, onClosed);
-    return Allocator_ONFREE_ASYNC;
-}
-
-static int blockFreeInsideCallback(struct Allocator_OnFreeJob* job)
-{
-    struct UDPBcastIface_pvt* context =
-        Identity_check((struct UDPBcastIface_pvt*) job->userData);
-    if (!context->inCallback) {
-        return 0;
-    }
-    context->blockFreeInsideCallback = job;
-    return Allocator_ONFREE_ASYNC;
-}
-
-List* UDPBcastIface_listDevices(struct Allocator* alloc, struct Except* eh)
-{
-    List* out = List_new(alloc);
-    uv_interface_address_t* interfaces;
-    int i, count;
-    int res = uv_interface_addresses(&interfaces, &count);
-    if (res) {
-        return out;
-    }
-
-    for (i = 0; i < count; i++) {
-        if (interfaces[i].is_internal) { continue; }
-        if (interfaces[i].address.address4.sin_family != AF_INET) { continue; }
-
-        List_addString(out, String_new(interfaces[i].name, alloc), alloc);
-    }
-    uv_free_interface_addresses(interfaces, count);
-    return out;
-}
-
-
-int UDPBcastIface_setBroadcast(struct UDPBcastIface* iface, bool enable)
-{
-    struct UDPBcastIface_pvt* context = Identity_check((struct UDPBcastIface_pvt*) iface);
-    int res = uv_udp_set_broadcast(&context->uvHandle, enable ? 1 : 0);
-    if (!res) {
-        context->bcast = enable;
-    }
-    return res;
-}
-
-struct UDPBcastIface* UDPBcastIface_new(struct EventBase* eventBase,
-                                        struct Sockaddr* addr,
-                                        const List* devices,
-                                        struct Allocator* alloc,
-                                        struct Except* exHandler,
-                                        struct Log* logger)
-{
-    struct EventBase_pvt* base = EventBase_privatize(eventBase);
-
-    struct UDPBcastIface_pvt* context =
-        Allocator_clone(alloc, (&(struct UDPBcastIface_pvt) {
-            .logger = logger,
-            .bcast = true,
-            .allocator = alloc
-        }));
-    context->pub.generic.alloc = alloc;
-    context->pub.generic.iface.send = incomingFromIface;
-    Identity_set(context);
-
-    if (!addr) {
-        Except_throw(exHandler, "Must assign the bcast address.");
-    }
-    Log_debug(logger, "Binding to address [%s]", Sockaddr_print(addr, alloc));
-
-    if (!Sockaddr_getPort(addr)) {
-        Except_throw(exHandler, "Must assign the bcast port.");
-    }
-
-    if (Sockaddr_getFamily(addr) != Sockaddr_AF_INET) {
-        Except_throw(exHandler, "UDP broadcast only supported by ipv4.");
-    }
-
-    uv_udp_init(base->loop, &context->uvHandle);
-    context->uvHandle.data = context;
-
-    void* native = Sockaddr_asNative(addr);
-    int ret = uv_udp_bind(&context->uvHandle, (const struct sockaddr*)native, 0);
-
-    if (ret) {
-        Except_throw(exHandler, "call to uv_udp_bind() failed [%s]",
-                     uv_strerror(ret));
-    }
-
-    ret = uv_udp_set_broadcast(&context->uvHandle, 1);
-
-    if (ret) {
-        Except_throw(exHandler, "call to uv_udp_set_broadcast() failed [%s]",
-                     uv_strerror(ret));
-    }
-
-    ret = uv_udp_recv_start(&context->uvHandle, allocate, incoming);
-    if (ret) {
-        const char* err = uv_strerror(ret);
-        uv_close((uv_handle_t*) &context->uvHandle, NULL);
-        Except_throw(exHandler, "uv_udp_recv_start() failed [%s]", err);
-    }
-
-    int nameLen = sizeof(struct Sockaddr_storage);
-    struct Sockaddr_storage ss;
-    Bits_memset(&ss, 0, sizeof(struct Sockaddr_storage));
-    ret = uv_udp_getsockname(&context->uvHandle, (void*)ss.nativeAddr, &nameLen);
-    if (ret) {
-        const char* err = uv_strerror(ret);
-        uv_close((uv_handle_t*) &context->uvHandle, NULL);
-        Except_throw(exHandler, "uv_udp_getsockname() failed [%s]", err);
-    }
-    ss.addr.addrLen = nameLen + 8;
-
-    context->pub.generic.addr = Sockaddr_clone(&ss.addr, alloc);
-    Log_debug(logger, "Bound to address [%s]", Sockaddr_print(context->pub.generic.addr, alloc));
-
-    context->devices = List_new(alloc);
-
-    if (devices) {
-        int32_t count = List_size(devices);
-        for (int32_t i = 0; i < count; i++) {
-            String* device = List_getString(devices, i);
-            List_addStringC(context->devices, device->bytes, alloc);
-        }
-    }
-
-    Allocator_onFree(alloc, closeHandleOnFree, context);
-    Allocator_onFree(alloc, blockFreeInsideCallback, context);
-
-    return &context->pub;
-}