Home | History | Annotate | Download | only in network
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2004-2009  Marcel Holtmann <marcel (at) holtmann.org>
      6  *
      7  *
      8  *  This program is free software; you can redistribute it and/or modify
      9  *  it under the terms of the GNU General Public License as published by
     10  *  the Free Software Foundation; either version 2 of the License, or
     11  *  (at your option) any later version.
     12  *
     13  *  This program is distributed in the hope that it will be useful,
     14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     16  *  GNU General Public License for more details.
     17  *
     18  *  You should have received a copy of the GNU General Public License
     19  *  along with this program; if not, write to the Free Software
     20  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     21  *
     22  */
     23 
     24 #ifdef HAVE_CONFIG_H
     25 #include <config.h>
     26 #endif
     27 
     28 #include <stdio.h>
     29 #include <errno.h>
     30 #include <unistd.h>
     31 #include <stdlib.h>
     32 #include <sys/param.h>
     33 #include <sys/ioctl.h>
     34 #include <sys/socket.h>
     35 #include <sys/wait.h>
     36 #include <net/if.h>
     37 
     38 #include <bluetooth/bluetooth.h>
     39 #include <bluetooth/l2cap.h>
     40 #include <bluetooth/bnep.h>
     41 
     42 #include <glib.h>
     43 
     44 #include "logging.h"
     45 #include "common.h"
     46 
     47 static int ctl;
     48 static GSList *pids;
     49 
     50 static struct {
     51 	const char	*name;		/* Friendly name */
     52 	const char	*uuid128;	/* UUID 128 */
     53 	uint16_t	id;		/* Service class identifier */
     54 } __svc[] = {
     55 	{ "panu",	PANU_UUID,	BNEP_SVC_PANU	},
     56 	{ "gn",		GN_UUID,	BNEP_SVC_GN	},
     57 	{ "nap",	NAP_UUID,	BNEP_SVC_NAP	},
     58 	{ NULL }
     59 };
     60 
     61 static const char *panu = NULL;
     62 static const char *gn = NULL;
     63 static const char *nap = NULL;
     64 
     65 struct bnep_data {
     66 	char *devname;
     67 	char *script;
     68 	int pid;
     69 };
     70 
     71 static gint find_devname(gconstpointer a, gconstpointer b)
     72 {
     73 	struct bnep_data *data = (struct bnep_data *) a;
     74 	const char *devname = b;
     75 
     76 	return strcmp(data->devname, devname);
     77 }
     78 
     79 static void script_exited(GPid pid, gint status, gpointer data)
     80 {
     81 	if (WIFEXITED(status))
     82 		debug("%d exited with status %d", pid, WEXITSTATUS(status));
     83 	else
     84 		debug("%d was killed by signal %d", pid, WTERMSIG(status));
     85 
     86 	g_spawn_close_pid(pid);
     87 }
     88 
     89 uint16_t bnep_service_id(const char *svc)
     90 {
     91 	int i;
     92 	uint16_t id;
     93 
     94 	/* Friendly service name */
     95 	for (i = 0; __svc[i].name; i++)
     96 		if (!strcasecmp(svc, __svc[i].name)) {
     97 			return __svc[i].id;
     98 		}
     99 
    100 	/* UUID 128 string */
    101 	for (i = 0; __svc[i].uuid128; i++)
    102 		if (!strcasecmp(svc, __svc[i].uuid128)) {
    103 			return __svc[i].id;
    104 		}
    105 
    106 	/* Try convert to HEX */
    107 	id = strtol(svc, NULL, 16);
    108 	if ((id < BNEP_SVC_PANU) || (id > BNEP_SVC_GN))
    109 		return 0;
    110 
    111 	return id;
    112 }
    113 
    114 const char *bnep_uuid(uint16_t id)
    115 {
    116 	int i;
    117 
    118 	for (i = 0; __svc[i].uuid128; i++)
    119 		if (__svc[i].id == id)
    120 			return __svc[i].uuid128;
    121 	return NULL;
    122 }
    123 
    124 const char *bnep_name(uint16_t id)
    125 {
    126 	int i;
    127 
    128 	for (i = 0; __svc[i].name; i++)
    129 		if (__svc[i].id == id)
    130 			return __svc[i].name;
    131 	return NULL;
    132 }
    133 
    134 int bnep_init(const char *panu_script, const char *gn_script,
    135 		const char *nap_script)
    136 {
    137 	ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
    138 
    139 	if (ctl < 0) {
    140 		int err = errno;
    141 		error("Failed to open control socket: %s (%d)",
    142 						strerror(err), err);
    143 		return -err;
    144 	}
    145 
    146 	panu = panu_script;
    147 	gn = gn_script;
    148 	nap = nap_script;
    149 	return 0;
    150 }
    151 
    152 int bnep_cleanup(void)
    153 {
    154 	close(ctl);
    155 	return 0;
    156 }
    157 
    158 int bnep_kill_connection(bdaddr_t *dst)
    159 {
    160 	struct bnep_conndel_req req;
    161 
    162 	memset(&req, 0, sizeof(req));
    163 	baswap((bdaddr_t *)&req.dst, dst);
    164 	req.flags = 0;
    165 	if (ioctl(ctl, BNEPCONNDEL, &req)) {
    166 		int err = errno;
    167 		error("Failed to kill connection: %s (%d)",
    168 						strerror(err), err);
    169 		return -err;
    170 	}
    171 	return 0;
    172 }
    173 
    174 int bnep_kill_all_connections(void)
    175 {
    176 	struct bnep_connlist_req req;
    177 	struct bnep_conninfo ci[7];
    178 	unsigned int i;
    179 	int err;
    180 
    181 	memset(&req, 0, sizeof(req));
    182 	req.cnum = 7;
    183 	req.ci   = ci;
    184 	if (ioctl(ctl, BNEPGETCONNLIST, &req)) {
    185 		err = errno;
    186 		error("Failed to get connection list: %s (%d)",
    187 						strerror(err), err);
    188 		return -err;
    189 	}
    190 
    191 	for (i = 0; i < req.cnum; i++) {
    192 		struct bnep_conndel_req del;
    193 
    194 		memset(&del, 0, sizeof(del));
    195 		memcpy(del.dst, ci[i].dst, ETH_ALEN);
    196 		del.flags = 0;
    197 		ioctl(ctl, BNEPCONNDEL, &del);
    198 	}
    199 	return 0;
    200 }
    201 
    202 int bnep_connadd(int sk, uint16_t role, char *dev)
    203 {
    204 	struct bnep_connadd_req req;
    205 
    206 	memset(&req, 0, sizeof(req));
    207 	strncpy(req.device, dev, 16);
    208 	req.device[15] = '\0';
    209 	req.sock = sk;
    210 	req.role = role;
    211 	if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
    212 		int err = errno;
    213 		error("Failed to add device %s: %s(%d)",
    214 				dev, strerror(err), err);
    215 		return -err;
    216 	}
    217 
    218 	strncpy(dev, req.device, 16);
    219 	return 0;
    220 }
    221 
    222 static void bnep_setup(gpointer data)
    223 {
    224 }
    225 
    226 static int bnep_exec(const char **argv)
    227 {
    228 	int pid;
    229 	GSpawnFlags flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
    230 
    231 	if (!g_spawn_async(NULL, (char **) argv, NULL, flags, bnep_setup, NULL,
    232 				&pid, NULL)) {
    233 		error("Unable to execute %s %s", argv[0], argv[1]);
    234 		return -EINVAL;
    235 	}
    236 
    237 	return pid;
    238 }
    239 
    240 int bnep_if_up(const char *devname, uint16_t id)
    241 {
    242 	int sd, err;
    243 	struct ifreq ifr;
    244 	const char *argv[5];
    245 	struct bnep_data *bnep = NULL;
    246 	GSList *l;
    247 
    248 	/* Check if a script is running */
    249 	l = g_slist_find_custom(pids, devname, find_devname);
    250 	if (l) {
    251 		bnep = l->data;
    252 
    253 		if (bnep->script && !strcmp(bnep->script, "avahi-autoipd")) {
    254 			argv[0] = bnep->script;
    255 			argv[1] = devname;
    256 			argv[2] = "--refresh";
    257 			argv[3] = NULL;
    258 
    259 			bnep->pid = bnep_exec(argv);
    260 		}
    261 	}
    262 
    263 	sd = socket(AF_INET, SOCK_DGRAM, 0);
    264 	memset(&ifr, 0, sizeof(ifr));
    265 	strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
    266 
    267 	ifr.ifr_flags |= IFF_UP;
    268 	ifr.ifr_flags |= IFF_MULTICAST;
    269 
    270 	if ((ioctl(sd, SIOCSIFFLAGS, (caddr_t) &ifr)) < 0) {
    271 		err = errno;
    272 		error("Could not bring up %s. %s(%d)", devname, strerror(err),
    273 			err);
    274 		return -err;
    275 	}
    276 
    277 	if (bnep)
    278 		return bnep->pid;
    279 
    280 	bnep = g_new0(struct bnep_data, 1);
    281 	bnep->devname = g_strdup(devname);
    282 
    283 	if (!id)
    284 		goto done;
    285 
    286 	if (id == BNEP_SVC_PANU)
    287 		bnep->script = g_strdup(panu);
    288 	else if (id == BNEP_SVC_GN)
    289 		bnep->script = g_strdup(gn);
    290 	else
    291 		bnep->script = g_strdup(nap);
    292 
    293 	if (!bnep->script)
    294 		goto done;
    295 
    296 	argv[0] = bnep->script;
    297 	argv[1] = devname;
    298 
    299 	if (!strcmp(bnep->script, "avahi-autoipd")) {
    300 		argv[2] = "--no-drop-root";
    301 		argv[3] = "--no-chroot";
    302 		argv[4] = NULL;
    303 	} else
    304 		argv[2] = NULL;
    305 
    306 	bnep->pid = bnep_exec(argv);
    307 	g_child_watch_add(bnep->pid, script_exited, bnep);
    308 
    309 done:
    310 	pids = g_slist_append(pids, bnep);
    311 
    312 	return bnep->pid;
    313 }
    314 
    315 int bnep_if_down(const char *devname)
    316 {
    317 	int sd, err, pid;
    318 	struct ifreq ifr;
    319 	struct bnep_data *bnep;
    320 	GSList *l;
    321 	GSpawnFlags flags;
    322 	const char *argv[4];
    323 
    324 	l = g_slist_find_custom(pids, devname, find_devname);
    325 	if (!l)
    326 		return 0;
    327 
    328 	bnep = l->data;
    329 
    330 	if (!bnep->pid)
    331 		goto done;
    332 
    333 	if (bnep->script && !strcmp(bnep->script, "avahi-autoipd")) {
    334 		argv[0] = bnep->script;
    335 		argv[1] = devname;
    336 		argv[2] = "--kill";
    337 		argv[3] = NULL;
    338 
    339 		flags = G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH;
    340 		g_spawn_async(NULL, (char **) argv, NULL, flags, bnep_setup,
    341 				(gpointer) devname, &pid, NULL);
    342 
    343 		goto done;
    344 	}
    345 
    346 	/* Kill script */
    347 	err = kill(bnep->pid, SIGTERM);
    348 	if (err < 0)
    349 		error("kill(%d, SIGTERM): %s (%d)", bnep->pid,
    350 			strerror(errno), errno);
    351 
    352 done:
    353 	sd = socket(AF_INET, SOCK_DGRAM, 0);
    354 	memset(&ifr, 0, sizeof(ifr));
    355 	strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
    356 
    357 	ifr.ifr_flags &= ~IFF_UP;
    358 
    359 	/* Bring down the interface */
    360 	ioctl(sd, SIOCSIFFLAGS, (caddr_t) &ifr);
    361 
    362 	pids = g_slist_remove(pids, bnep);
    363 
    364 	if (bnep->devname)
    365 		g_free(bnep->devname);
    366 
    367 	if (bnep->script)
    368 		g_free(bnep->script);
    369 
    370 	g_free(bnep);
    371 
    372 	return 0;
    373 }
    374