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