summaryrefslogtreecommitdiffstats
path: root/network/common.c
diff options
context:
space:
mode:
Diffstat (limited to 'network/common.c')
-rw-r--r--network/common.c396
1 files changed, 396 insertions, 0 deletions
diff --git a/network/common.c b/network/common.c
new file mode 100644
index 00000000..f1dbe1c5
--- /dev/null
+++ b/network/common.c
@@ -0,0 +1,396 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <net/if.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/l2cap.h>
+#include <bluetooth/bnep.h>
+
+#include <glib.h>
+
+#include "logging.h"
+#include "common.h"
+#include "textfile.h"
+
+static int ctl;
+static GSList *pids;
+
+#define PANU_UUID "00001115-0000-1000-8000-00805f9b34fb"
+#define NAP_UUID "00001116-0000-1000-8000-00805f9b34fb"
+#define GN_UUID "00001117-0000-1000-8000-00805f9b34fb"
+
+static struct {
+ const char *name; /* Friendly name */
+ const char *uuid128; /* UUID 128 */
+ uint16_t id; /* Service class identifier */
+} __svc[] = {
+ { "panu", PANU_UUID, BNEP_SVC_PANU },
+ { "gn", GN_UUID, BNEP_SVC_GN },
+ { "nap", NAP_UUID, BNEP_SVC_NAP },
+ { NULL }
+};
+
+static const char *panu = NULL;
+static const char *gn = NULL;
+static const char *nap = NULL;
+
+struct bnep_data {
+ char *devname;
+ char *script;
+ int pid;
+};
+
+static gint find_devname(gconstpointer a, gconstpointer b)
+{
+ struct bnep_data *data = (struct bnep_data *) a;
+ const char *devname = b;
+
+ return strcmp(data->devname, devname);
+}
+
+static void script_exited(GPid pid, gint status, gpointer data)
+{
+ if (WIFEXITED(status))
+ debug("%d exited with status %d", pid, WEXITSTATUS(status));
+ else
+ debug("%d was killed by signal %d", pid, WTERMSIG(status));
+
+ g_spawn_close_pid(pid);
+}
+
+uint16_t bnep_service_id(const char *svc)
+{
+ int i;
+ uint16_t id;
+
+ /* Friendly service name */
+ for (i = 0; __svc[i].name; i++)
+ if (!strcasecmp(svc, __svc[i].name)) {
+ return __svc[i].id;
+ }
+
+ /* UUID 128 string */
+ for (i = 0; __svc[i].uuid128; i++)
+ if (!strcasecmp(svc, __svc[i].uuid128)) {
+ return __svc[i].id;
+ }
+
+ /* Try convert to HEX */
+ id = strtol(svc, NULL, 16);
+ if ((id < BNEP_SVC_PANU) || (id > BNEP_SVC_GN))
+ return 0;
+
+ return id;
+}
+
+const char *bnep_uuid(uint16_t id)
+{
+ int i;
+
+ for (i = 0; __svc[i].uuid128; i++)
+ if (__svc[i].id == id)
+ return __svc[i].uuid128;
+ return NULL;
+}
+
+const char *bnep_name(uint16_t id)
+{
+ int i;
+
+ for (i = 0; __svc[i].name; i++)
+ if (__svc[i].id == id)
+ return __svc[i].name;
+ return NULL;
+}
+
+int bnep_init(const char *panu_script, const char *gn_script,
+ const char *nap_script)
+{
+ ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
+
+ if (ctl < 0) {
+ int err = errno;
+ error("Failed to open control socket: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+
+ panu = panu_script;
+ gn = gn_script;
+ nap = nap_script;
+ return 0;
+}
+
+int bnep_cleanup(void)
+{
+ close(ctl);
+ return 0;
+}
+
+int bnep_kill_connection(bdaddr_t *dst)
+{
+ struct bnep_conndel_req req;
+
+ memset(&req, 0, sizeof(req));
+ baswap((bdaddr_t *)&req.dst, dst);
+ req.flags = 0;
+ if (ioctl(ctl, BNEPCONNDEL, &req)) {
+ int err = errno;
+ error("Failed to kill connection: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+ return 0;
+}
+
+int bnep_kill_all_connections(void)
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[7];
+ int i, err;
+
+ memset(&req, 0, sizeof(req));
+ req.cnum = 7;
+ req.ci = ci;
+ if (ioctl(ctl, BNEPGETCONNLIST, &req)) {
+ err = errno;
+ error("Failed to get connection list: %s (%d)",
+ strerror(err), err);
+ return -err;
+ }
+
+ for (i=0; i < req.cnum; i++) {
+ struct bnep_conndel_req del;
+
+ memset(&del, 0, sizeof(del));
+ memcpy(del.dst, ci[i].dst, ETH_ALEN);
+ del.flags = 0;
+ ioctl(ctl, BNEPCONNDEL, &del);
+ }
+ return 0;
+}
+
+int bnep_connadd(int sk, uint16_t role, char *dev)
+{
+ struct bnep_connadd_req req;
+
+ memset(&req, 0, sizeof(req));
+ strncpy(req.device, dev, 16);
+ req.device[15] = '\0';
+ req.sock = sk;
+ req.role = role;
+ if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
+ int err = errno;
+ error("Failed to add device %s: %s(%d)",
+ dev, strerror(err), err);
+ return -err;
+ }
+
+ strncpy(dev, req.device, 16);
+ return 0;
+}
+
+static void bnep_setup(gpointer data)
+{
+}
+
+static int bnep_exec(const char **argv)
+{
+ int pid;
+ GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
+
+ if (!g_spawn_async(NULL, (char **) argv, NULL, flags, bnep_setup, NULL,
+ &pid, NULL)) {
+ error("Unable to execute %s %s", *argv[0], *argv[1]);
+ return -EINVAL;
+ }
+
+ return pid;
+}
+
+int bnep_if_up(const char *devname, uint16_t id)
+{
+ int sd, err;
+ struct ifreq ifr;
+ const char *argv[5];
+ struct bnep_data *bnep = NULL;
+ GSList *l;
+
+ /* Check if a script is running */
+ l = g_slist_find_custom(pids, devname, find_devname);
+ if (l) {
+ bnep = l->data;
+
+ if (bnep->script && !strcmp(bnep->script, "avahi-autoipd")) {
+ argv[0] = bnep->script;
+ argv[1] = devname;
+ argv[2] = "--refresh";
+ argv[3] = NULL;
+
+ bnep->pid = bnep_exec(argv);
+ }
+ }
+
+ sd = socket(AF_INET6, SOCK_DGRAM, 0);
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, devname);
+
+ ifr.ifr_flags |= IFF_UP;
+ ifr.ifr_flags |= IFF_MULTICAST;
+
+ if ((ioctl(sd, SIOCSIFFLAGS, (caddr_t) &ifr)) < 0) {
+ err = errno;
+ error("Could not bring up %s. %s(%d)", devname, strerror(err),
+ err);
+ return -err;
+ }
+
+ if (bnep)
+ return bnep->pid;
+
+ bnep = g_new0(struct bnep_data, 1);
+ bnep->devname = g_strdup(devname);
+
+ if (!id)
+ goto done;
+
+ if (id == BNEP_SVC_PANU)
+ bnep->script = g_strdup(panu);
+ else if (id == BNEP_SVC_GN)
+ bnep->script = g_strdup(gn);
+ else
+ bnep->script = g_strdup(nap);
+
+ if (!bnep->script)
+ goto done;
+
+ argv[0] = bnep->script;
+ argv[1] = devname;
+
+ if (!strcmp(bnep->script, "avahi-autoipd")) {
+ argv[2] = "--no-drop-root";
+ argv[3] = "--no-chroot";
+ argv[4] = NULL;
+ } else
+ argv[2] = NULL;
+
+ bnep->pid = bnep_exec(argv);
+ g_child_watch_add(bnep->pid, script_exited, bnep);
+
+done:
+ pids = g_slist_append(pids, bnep);
+
+ return bnep->pid;
+}
+
+int bnep_if_down(const char *devname)
+{
+ int sd, err, pid;
+ struct ifreq ifr;
+ struct bnep_data *bnep;
+ GSList *l;
+ GSpawnFlags flags;
+ const char *argv[4];
+
+ l = g_slist_find_custom(pids, devname, find_devname);
+ if (!l)
+ return 0;
+
+ bnep = l->data;
+
+ if (!bnep->pid)
+ goto done;
+
+ if (bnep->script && !strcmp(bnep->script, "avahi-autoipd")) {
+ argv[0] = bnep->script;
+ argv[1] = devname;
+ argv[2] = "--kill";
+ argv[3] = NULL;
+
+ flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
+ g_spawn_async(NULL, (char **) argv, NULL, flags, bnep_setup,
+ (gpointer) devname, &pid, NULL);
+
+ goto done;
+ }
+
+ /* Kill script */
+ err = kill(bnep->pid, SIGTERM);
+ if (err < 0)
+ error("kill(%d, SIGTERM): %s (%d)", bnep->pid,
+ strerror(errno), errno);
+
+done:
+ sd = socket(AF_INET6, SOCK_DGRAM, 0);
+ memset(&ifr, 0, sizeof(ifr));
+ strcpy(ifr.ifr_name, devname);
+
+ ifr.ifr_flags &= ~IFF_UP;
+
+ /* Bring down the interface */
+ ioctl(sd, SIOCSIFFLAGS, (caddr_t) &ifr);
+
+ pids = g_slist_remove(pids, bnep);
+
+ if (bnep->devname)
+ g_free(bnep->devname);
+
+ if (bnep->script)
+ g_free(bnep->script);
+
+ g_free(bnep);
+
+ return 0;
+}
+
+int read_remote_name(bdaddr_t *src, bdaddr_t *dst, char *buf, size_t size)
+{
+ char filename[PATH_MAX + 1], addr[18], *str;
+
+ ba2str(src, addr);
+ create_name(filename, PATH_MAX, STORAGEDIR, addr, "names");
+
+ ba2str(dst, addr);
+ str = textfile_get(filename, addr);
+ if (!str)
+ return -ENOENT;
+
+ snprintf(buf, size, "%s", str);
+ free(str);
+
+ return 0;
+}