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#include <stdio.h> 20#include <stdlib.h> 21#include <unistd.h> 22#include <errno.h> 23#include <string.h> 24 25#include "libbridge.h" 26#include "libbridge_private.h" 27 28int br_socket_fd = -1; 29struct sysfs_class *br_class_net; 30 31int br_init(void) 32{ 33 if ((br_socket_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 34 return errno; 35 36 br_class_net = sysfs_open_class("net"); 37 return 0; 38} 39 40int br_refresh(void) 41{ 42 if (br_class_net) { 43 sysfs_close_class(br_class_net); 44 br_class_net = sysfs_open_class("net"); 45 } 46 47 return 0; 48} 49 50void br_shutdown(void) 51{ 52 sysfs_close_class(br_class_net); 53 br_class_net = NULL; 54 close(br_socket_fd); 55 br_socket_fd = -1; 56} 57 58#ifdef HAVE_LIBSYSFS 59static int isbridge(const struct sysfs_class_device *dev) 60{ 61 char path[SYSFS_PATH_MAX]; 62 63 snprintf(path, sizeof(path), "%s/bridge", dev->path); 64 return !sysfs_path_is_dir(path); 65} 66 67/* 68 * New interface uses sysfs to find bridges 69 */ 70static int new_foreach_bridge(int (*iterator)(const char *name, void *), 71 void *arg) 72{ 73 struct sysfs_class_device *dev; 74 struct dlist *devlist; 75 int count = 0; 76 77 if (!br_class_net) { 78 dprintf("no class /sys/class/net\n"); 79 return -EOPNOTSUPP; 80 } 81 82 devlist = sysfs_get_class_devices(br_class_net); 83 if (!devlist) { 84 dprintf("Can't read devices from sysfs\n"); 85 return -errno; 86 } 87 88 dlist_for_each_data(devlist, dev, struct sysfs_class_device) { 89 if (isbridge(dev)) { 90 ++count; 91 if (iterator(dev->name, arg)) 92 break; 93 } 94 } 95 96 return count; 97} 98#endif 99 100/* 101 * Old interface uses ioctl 102 */ 103static int old_foreach_bridge(int (*iterator)(const char *, void *), 104 void *iarg) 105{ 106 int i, ret=0, num; 107 char ifname[IFNAMSIZ]; 108 int ifindices[MAX_BRIDGES]; 109 unsigned long args[3] = { BRCTL_GET_BRIDGES, 110 (unsigned long)ifindices, MAX_BRIDGES }; 111 112 num = ioctl(br_socket_fd, SIOCGIFBR, args); 113 if (num < 0) { 114 dprintf("Get bridge indices failed: %s\n", 115 strerror(errno)); 116 return -errno; 117 } 118 119 for (i = 0; i < num; i++) { 120 if (!if_indextoname(ifindices[i], ifname)) { 121 dprintf("get find name for ifindex %d\n", 122 ifindices[i]); 123 return -errno; 124 } 125 126 ++ret; 127 if(iterator(ifname, iarg)) 128 break; 129 130 } 131 132 return ret; 133 134} 135 136/* 137 * Go over all bridges and call iterator function. 138 * if iterator returns non-zero then stop. 139 */ 140int br_foreach_bridge(int (*iterator)(const char *, void *), 141 void *arg) 142{ 143 int ret; 144#ifdef HAVE_LIBSYSFS 145 146 ret = new_foreach_bridge(iterator, arg); 147 if (ret <= 0) 148#endif 149 ret = old_foreach_bridge(iterator, arg); 150 151 return ret; 152} 153 154/* 155 * Only used if sysfs is not available. 156 */ 157static int old_foreach_port(const char *brname, 158 int (*iterator)(const char *br, const char *port, 159 void *arg), 160 void *arg) 161{ 162 int i, err, count; 163 struct ifreq ifr; 164 char ifname[IFNAMSIZ]; 165 int ifindices[MAX_PORTS]; 166 unsigned long args[4] = { BRCTL_GET_PORT_LIST, 167 (unsigned long)ifindices, MAX_PORTS, 0 }; 168 169 memset(ifindices, 0, sizeof(ifindices)); 170 strncpy(ifr.ifr_name, brname, IFNAMSIZ); 171 ifr.ifr_data = (char *) &args; 172 173 err = ioctl(br_socket_fd, SIOCDEVPRIVATE, &ifr); 174 if (err < 0) { 175 dprintf("list ports for bridge:'%s' failed: %s\n", 176 brname, strerror(errno)); 177 return -errno; 178 } 179 180 count = 0; 181 for (i = 0; i < MAX_PORTS; i++) { 182 if (!ifindices[i]) 183 continue; 184 185 if (!if_indextoname(ifindices[i], ifname)) { 186 dprintf("can't find name for ifindex:%d\n", 187 ifindices[i]); 188 continue; 189 } 190 191 ++count; 192 if (iterator(brname, ifname, arg)) 193 break; 194 } 195 196 return count; 197} 198 199/* 200 * Iterate over all ports in bridge (using sysfs). 201 */ 202int br_foreach_port(const char *brname, 203 int (*iterator)(const char *br, const char *port, void *arg), 204 void *arg) 205{ 206#ifdef HAVE_LIBSYSFS 207 struct sysfs_class_device *dev; 208 struct sysfs_directory *dir; 209 struct sysfs_link *plink; 210 struct dlist *links; 211 int err = 0; 212 char path[SYSFS_PATH_MAX]; 213 214 if (!br_class_net || 215 !(dev = sysfs_get_class_device(br_class_net, (char *) brname))) 216 goto old; 217 218 snprintf(path, sizeof(path), "%s/%s", 219 dev->path, SYSFS_BRIDGE_PORT_SUBDIR); 220 221 dprintf("path=%s\n", path); 222 dir = sysfs_open_directory(path); 223 if (!dir) { 224 /* no /sys/class/net/ethX/brif subdirectory 225 * either: old kernel, or not really a bridge 226 */ 227 goto old; 228 } 229 230 links = sysfs_get_dir_links(dir); 231 if (!links) { 232 err = -ENOSYS; 233 goto out; 234 } 235 236 err = 0; 237 dlist_for_each_data(links, plink, struct sysfs_link) { 238 ++err; 239 if (iterator(brname, plink->name, arg)) 240 break; 241 } 242 out: 243 sysfs_close_directory(dir); 244 return err; 245 246 old: 247#endif 248 return old_foreach_port(brname, iterator, arg); 249 250} 251