1/* 2 * Copyright (C) 2000 Lennert Buytenhek 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 20#include <stdio.h> 21#include <stdlib.h> 22#include <unistd.h> 23#include <errno.h> 24#include <string.h> 25#include <sys/fcntl.h> 26 27#include "libbridge.h" 28#include "libbridge_private.h" 29 30#ifdef HAVE_LIBSYSFS 31/* Given two two character "0a" convert it to a byte */ 32static unsigned char getoctet(const char *cp) 33{ 34 char t[3] = { cp[0], cp[1], 0 }; 35 return strtoul(t, NULL, 16); 36} 37 38static struct sysfs_directory *bridge_sysfs_directory(const char *devname, 39 const char *subname) 40{ 41 struct sysfs_directory *sdir; 42 struct sysfs_class_device *dev; 43 char path[SYSFS_PATH_MAX]; 44 45 if (!devname) 46 return NULL; 47 48 if (!br_class_net) { 49 dprintf("can't find class_net\n"); 50 return NULL; 51 } 52 53 dev = sysfs_get_class_device(br_class_net, (char *) devname); 54 if (!dev) { 55 dprintf("can't find device %s in %s\n", devname, br_class_net->path); 56 return NULL; 57 } 58 59 snprintf(path, SYSFS_PATH_MAX, "%s/%s", dev->path, subname); 60 sdir = sysfs_open_directory(path); 61 if (!sdir) 62 dprintf("can't open directory: %s\n", path); 63 return sdir; 64} 65 66static void fetch_id(struct sysfs_directory *sdir, const char *name, 67 struct bridge_id *id) 68{ 69 struct sysfs_attribute *attr; 70 71 memset(id, 0, sizeof(id)); 72 attr = sysfs_get_directory_attribute(sdir, (char *) name); 73 if (!attr) { 74 dprintf("Can't find attribute %s/%s\n", sdir->path, name); 75 return; 76 } 77 78 if (strlen(attr->value) < 17) 79 dprintf("Bad format for %s: '%s'\n", name, attr->value); 80 else { 81 const char *cp = attr->value; 82 id->prio[0] = getoctet(cp); cp += 2; 83 id->prio[1] = getoctet(cp); cp += 3; 84 id->addr[0] = getoctet(cp); cp += 2; 85 id->addr[1] = getoctet(cp); cp += 2; 86 id->addr[2] = getoctet(cp); cp += 2; 87 id->addr[3] = getoctet(cp); cp += 2; 88 id->addr[4] = getoctet(cp); cp += 2; 89 id->addr[5] = getoctet(cp); 90 } 91} 92 93/* Get a time value out of sysfs */ 94static void fetch_tv(struct sysfs_directory *sdir, const char *name, 95 struct timeval *tv) 96{ 97 struct sysfs_attribute *attr 98 = sysfs_get_directory_attribute(sdir, (char *) name); 99 100 if (!attr) { 101 dprintf("Can't find attribute %s/%s\n", sdir->path, name); 102 memset(tv, 0, sizeof(tv)); 103 return; 104 } 105 106 __jiffies_to_tv(tv, strtoul(attr->value, NULL, 0)); 107} 108 109/* Fetch an integer attribute out of sysfs. */ 110static int fetch_int(struct sysfs_directory *sdir, const char *name) 111{ 112 struct sysfs_attribute *attr 113 = sysfs_get_directory_attribute(sdir, (char *) name); 114 int val = 0; 115 116 if (!attr) 117 dprintf("Can't find attribute %s/%s\n", sdir->path, name); 118 else 119 val = strtol(attr->value, NULL, 0); 120 return val; 121} 122#endif 123 124/* 125 * Convert device name to an index in the list of ports in bridge. 126 * 127 * Old API does bridge operations as if ports were an array 128 * inside bridge structure. 129 */ 130static int get_portno(const char *brname, const char *ifname) 131{ 132 int i; 133 int ifindex = if_nametoindex(ifname); 134 int ifindices[MAX_PORTS]; 135 unsigned long args[4] = { BRCTL_GET_PORT_LIST, 136 (unsigned long)ifindices, MAX_PORTS, 0 }; 137 struct ifreq ifr; 138 139 if (ifindex <= 0) 140 goto error; 141 142 memset(ifindices, 0, sizeof(ifindices)); 143 strncpy(ifr.ifr_name, brname, IFNAMSIZ); 144 ifr.ifr_data = (char *) &args; 145 146 if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { 147 dprintf("get_portno: get ports of %s failed: %s\n", 148 brname, strerror(errno)); 149 goto error; 150 } 151 152 for (i = 0; i < MAX_PORTS; i++) { 153 if (ifindices[i] == ifindex) 154 return i; 155 } 156 157 dprintf("%s is not a in bridge %s\n", ifname, brname); 158 error: 159 return -1; 160} 161 162/* get information via ioctl */ 163static int old_get_bridge_info(const char *bridge, struct bridge_info *info) 164{ 165 struct ifreq ifr; 166 struct __bridge_info i; 167 unsigned long args[4] = { BRCTL_GET_BRIDGE_INFO, 168 (unsigned long) &i, 0, 0 }; 169 170 memset(info, 0, sizeof(*info)); 171 strncpy(ifr.ifr_name, bridge, IFNAMSIZ); 172 ifr.ifr_data = (char *) &args; 173 174 if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { 175 dprintf("%s: can't get info %s\n", 176 bridge, strerror(errno)); 177 return errno; 178 } 179 180 memcpy(&info->designated_root, &i.designated_root, 8); 181 memcpy(&info->bridge_id, &i.bridge_id, 8); 182 info->root_path_cost = i.root_path_cost; 183 info->root_port = i.root_port; 184 info->topology_change = i.topology_change; 185 info->topology_change_detected = i.topology_change_detected; 186 info->stp_enabled = i.stp_enabled; 187 __jiffies_to_tv(&info->max_age, i.max_age); 188 __jiffies_to_tv(&info->hello_time, i.hello_time); 189 __jiffies_to_tv(&info->forward_delay, i.forward_delay); 190 __jiffies_to_tv(&info->bridge_max_age, i.bridge_max_age); 191 __jiffies_to_tv(&info->bridge_hello_time, i.bridge_hello_time); 192 __jiffies_to_tv(&info->bridge_forward_delay, i.bridge_forward_delay); 193 __jiffies_to_tv(&info->ageing_time, i.ageing_time); 194 __jiffies_to_tv(&info->hello_timer_value, i.hello_timer_value); 195 __jiffies_to_tv(&info->tcn_timer_value, i.tcn_timer_value); 196 __jiffies_to_tv(&info->topology_change_timer_value, 197 i.topology_change_timer_value); 198 __jiffies_to_tv(&info->gc_timer_value, i.gc_timer_value); 199 200 return 0; 201} 202 203/* 204 * Get bridge parameters using either sysfs or old 205 * ioctl. 206 */ 207int br_get_bridge_info(const char *bridge, struct bridge_info *info) 208{ 209#ifndef HAVE_LIBSYSFS 210 return old_get_bridge_info(bridge, info); 211#else 212 struct sysfs_directory *sdir; 213 214 sdir = bridge_sysfs_directory(bridge, SYSFS_BRIDGE_ATTR); 215 if (!sdir) 216 return old_get_bridge_info(bridge,info); 217 218 memset(info, 0, sizeof(*info)); 219 fetch_id(sdir, "root_id", &info->designated_root); 220 fetch_id(sdir, "bridge_id", &info->bridge_id); 221 info->root_path_cost = fetch_int(sdir, "root_path_cost"); 222 fetch_tv(sdir, "max_age", &info->max_age); 223 fetch_tv(sdir, "hello_time", &info->hello_time); 224 fetch_tv(sdir, "forward_delay", &info->forward_delay); 225 fetch_tv(sdir, "max_age", &info->bridge_max_age); 226 fetch_tv(sdir, "hello_time", &info->bridge_hello_time); 227 fetch_tv(sdir, "forward_delay", &info->bridge_forward_delay); 228 fetch_tv(sdir, "ageing_time", &info->ageing_time); 229 fetch_tv(sdir, "hello_timer", &info->hello_timer_value); 230 fetch_tv(sdir, "tcn_timer", &info->tcn_timer_value); 231 fetch_tv(sdir, "topology_change_timer", 232 &info->topology_change_timer_value);; 233 fetch_tv(sdir, "gc_timer", &info->gc_timer_value); 234 235 info->root_port = fetch_int(sdir, "root_port"); 236 info->stp_enabled = fetch_int(sdir, "stp_state"); 237 info->topology_change = fetch_int(sdir, "topology_change"); 238 info->topology_change_detected = fetch_int(sdir, "topology_change_detected"); 239 sysfs_close_directory(sdir); 240 241 return 0; 242#endif 243} 244 245static int old_get_port_info(const char *brname, const char *port, 246 struct port_info *info) 247{ 248 struct __port_info i; 249 int index; 250 251 memset(info, 0, sizeof(*info)); 252 253 index = get_portno(brname, port); 254 if (index < 0) 255 return errno; 256 257 else { 258 struct ifreq ifr; 259 unsigned long args[4] = { BRCTL_GET_PORT_INFO, 260 (unsigned long) &i, index, 0 }; 261 262 strncpy(ifr.ifr_name, brname, IFNAMSIZ); 263 ifr.ifr_data = (char *) &args; 264 265 if (ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr) < 0) { 266 dprintf("old can't get port %s(%d) info %s\n", 267 brname, index, strerror(errno)); 268 return errno; 269 } 270 } 271 272 info->port_no = index; 273 memcpy(&info->designated_root, &i.designated_root, 8); 274 memcpy(&info->designated_bridge, &i.designated_bridge, 8); 275 info->port_id = i.port_id; 276 info->designated_port = i.designated_port; 277 info->path_cost = i.path_cost; 278 info->designated_cost = i.designated_cost; 279 info->state = i.state; 280 info->top_change_ack = i.top_change_ack; 281 info->config_pending = i.config_pending; 282 __jiffies_to_tv(&info->message_age_timer_value, 283 i.message_age_timer_value); 284 __jiffies_to_tv(&info->forward_delay_timer_value, 285 i.forward_delay_timer_value); 286 __jiffies_to_tv(&info->hold_timer_value, i.hold_timer_value); 287 return 0; 288} 289 290/* 291 * Get information about port on bridge. 292 */ 293int br_get_port_info(const char *brname, const char *port, 294 struct port_info *info) 295{ 296#ifndef HAVE_LIBSYSFS 297 return old_get_port_info(brname, port, info); 298#else 299 struct sysfs_directory *sdir 300 = bridge_sysfs_directory(port, SYSFS_BRIDGE_PORT_ATTR); 301 302 if (!sdir) 303 return old_get_port_info(brname, port, info); 304 305 memset(info, 0, sizeof(*info)); 306 fetch_id(sdir, "designated_root", &info->designated_root); 307 fetch_id(sdir, "designated_bridge", &info->designated_bridge); 308 info->port_no = fetch_int(sdir, "port_no"); 309 info->port_id = fetch_int(sdir, "port_id"); 310 info->designated_port = fetch_int(sdir, "designated_port"); 311 info->path_cost = fetch_int(sdir, "path_cost"); 312 info->designated_cost = fetch_int(sdir, "designated_cost"); 313 info->state = fetch_int(sdir, "state"); 314 info->top_change_ack = fetch_int(sdir, "change_ack"); 315 info->config_pending = fetch_int(sdir, "config_pending"); 316 fetch_tv(sdir, "message_age_timer", 317 &info->message_age_timer_value); 318 fetch_tv(sdir, "forward_delay_timer", 319 &info->forward_delay_timer_value); 320 fetch_tv(sdir, "hold_timer", 321 &info->hold_timer_value); 322 sysfs_close_directory(sdir); 323 324 return 0; 325#endif 326} 327 328 329static int br_set(const char *bridge, const char *name, 330 unsigned long value, unsigned long oldcode) 331{ 332 int ret; 333#ifdef HAVE_LIBSYSFS 334 struct sysfs_directory *sdir; 335 336 sdir = bridge_sysfs_directory(bridge, SYSFS_BRIDGE_ATTR); 337 if (sdir) { 338 struct sysfs_attribute *attr; 339 char buf[32]; 340 sprintf(buf, "%ld", value); 341 342 attr = sysfs_get_directory_attribute(sdir, (char *) name); 343 if (attr) 344 ret = sysfs_write_attribute(attr, buf, strlen(buf)); 345 else { 346 ret = -1; 347 errno = EINVAL; 348 } 349 sysfs_close_directory(sdir); 350 } else 351#endif 352 { 353 struct ifreq ifr; 354 unsigned long args[4] = { oldcode, value, 0, 0 }; 355 356 strncpy(ifr.ifr_name, bridge, IFNAMSIZ); 357 ifr.ifr_data = (char *) &args; 358 ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); 359 } 360 361 return ret < 0 ? errno : 0; 362} 363 364int br_set_bridge_forward_delay(const char *br, struct timeval *tv) 365{ 366 return br_set(br, "forward_delay", __tv_to_jiffies(tv), 367 BRCTL_SET_BRIDGE_FORWARD_DELAY); 368} 369 370int br_set_bridge_hello_time(const char *br, struct timeval *tv) 371{ 372 return br_set(br, "hello_time", __tv_to_jiffies(tv), 373 BRCTL_SET_BRIDGE_HELLO_TIME); 374} 375 376int br_set_bridge_max_age(const char *br, struct timeval *tv) 377{ 378 return br_set(br, "max_age", __tv_to_jiffies(tv), 379 BRCTL_SET_BRIDGE_MAX_AGE); 380} 381 382int br_set_ageing_time(const char *br, struct timeval *tv) 383{ 384 return br_set(br, "ageing_time", __tv_to_jiffies(tv), 385 BRCTL_SET_AGEING_TIME); 386} 387 388int br_set_stp_state(const char *br, int stp_state) 389{ 390 return br_set(br, "stp_state", stp_state, BRCTL_SET_BRIDGE_STP_STATE); 391} 392 393int br_set_bridge_priority(const char *br, int bridge_priority) 394{ 395 return br_set(br, "priority", bridge_priority, 396 BRCTL_SET_BRIDGE_PRIORITY); 397} 398 399static int port_set(const char *bridge, const char *ifname, 400 const char *name, unsigned long value, 401 unsigned long oldcode) 402{ 403 int ret, index; 404#ifdef HAVE_LIBSYSFS 405 struct sysfs_directory *sdir; 406 407 sdir = bridge_sysfs_directory(ifname, SYSFS_BRIDGE_PORT_ATTR); 408 if (sdir) { 409 struct sysfs_attribute *attr; 410 char buf[32]; 411 412 sprintf(buf, "%ld", value); 413 414 attr = sysfs_get_directory_attribute(sdir, (char *) name); 415 if (attr) 416 ret = sysfs_write_attribute(attr, buf, strlen(buf)); 417 else { 418 ret = -1; 419 errno = EINVAL; 420 } 421 sysfs_close_directory(sdir); 422 } else 423#endif 424 if ( (index = get_portno(bridge, ifname)) < 0) 425 ret = index; 426 427 else { 428 struct ifreq ifr; 429 unsigned long args[4] = { oldcode, index, value, 0 }; 430 431 strncpy(ifr.ifr_name, bridge, IFNAMSIZ); 432 ifr.ifr_data = (char *) &args; 433 ret = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); 434 } 435 436 return ret < 0 ? errno : 0; 437} 438 439int br_set_port_priority(const char *bridge, const char *port, int priority) 440{ 441 return port_set(bridge, port, "priority", priority, BRCTL_SET_PORT_PRIORITY); 442} 443 444int br_set_path_cost(const char *bridge, const char *port, int cost) 445{ 446 return port_set(bridge, port, "path_cost", cost, BRCTL_SET_PATH_COST); 447} 448 449static inline void __copy_fdb(struct fdb_entry *ent, 450 const struct __fdb_entry *f) 451{ 452 memcpy(ent->mac_addr, f->mac_addr, 6); 453 ent->port_no = f->port_no; 454 ent->is_local = f->is_local; 455 __jiffies_to_tv(&ent->ageing_timer_value, f->ageing_timer_value); 456} 457 458int br_read_fdb(const char *bridge, struct fdb_entry *fdbs, 459 unsigned long offset, int num) 460{ 461 int i, fd = -1, n; 462 struct __fdb_entry fe[num]; 463#ifdef HAVE_LIBSYSFS 464 struct sysfs_class_device *dev; 465 466 /* open /sys/class/net/brXXX/brforward */ 467 if (br_class_net && 468 (dev = sysfs_get_class_device(br_class_net, (char *) bridge))) { 469 char path[SYSFS_PATH_MAX]; 470 471 snprintf(path, SYSFS_PATH_MAX, "%s/%s", dev->path, 472 SYSFS_BRIDGE_FDB); 473 fd = open(path, O_RDONLY, 0); 474 } 475 476 if (fd != -1) { 477 /* read records from file */ 478 lseek(fd, offset*sizeof(struct __fdb_entry), SEEK_SET); 479 n = read(fd, fe, num*sizeof(struct __fdb_entry)); 480 if (n > 0) 481 n /= sizeof(struct __fdb_entry); 482 } else 483#endif 484 { 485 /* old kernel, use ioctl */ 486 unsigned long args[4] = { BRCTL_GET_FDB_ENTRIES, 487 (unsigned long) fe, 488 num, offset }; 489 struct ifreq ifr; 490 int retries = 0; 491 492 strncpy(ifr.ifr_name, bridge, IFNAMSIZ); 493 ifr.ifr_data = (char *) args; 494 495 retry: 496 n = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); 497 498 /* table can change during ioctl processing */ 499 if (n < 0 && errno == EAGAIN && ++retries < 10) { 500 sleep(0); 501 goto retry; 502 } 503 } 504 505 for (i = 0; i < n; i++) 506 __copy_fdb(fdbs+i, fe+i); 507 508 if (fd > 0) 509 close(fd); 510 511 return n; 512} 513