2 * Copyright (C) 2000 Lennert Buytenhek
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.
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.
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.
26 #include <sys/fcntl.h>
28 #include "libbridge.h"
29 #include "libbridge_private.h"
31 static FILE *fpopen(const char *dir
, const char *name
)
33 char path
[SYSFS_PATH_MAX
];
35 snprintf(path
, SYSFS_PATH_MAX
, "%s/%s", dir
, name
);
36 return fopen(path
, "r");
39 static void fetch_id(const char *dev
, const char *name
, struct bridge_id
*id
)
41 FILE *f
= fpopen(dev
, name
);
44 fprintf(stderr
, "%s: %s\n", dev
, strerror(errno
));
46 fscanf(f
, "%2hhx%2hhx.%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
47 &id
->prio
[0], &id
->prio
[1],
48 &id
->addr
[0], &id
->addr
[1], &id
->addr
[2],
49 &id
->addr
[3], &id
->addr
[4], &id
->addr
[5]);
54 /* Fetch an integer attribute out of sysfs. */
55 static int fetch_int(const char *dev
, const char *name
)
57 FILE *f
= fpopen(dev
, name
);
63 fscanf(f
, "%i", &value
);
68 /* Get a time value out of sysfs */
69 static void fetch_tv(const char *dev
, const char *name
,
72 __jiffies_to_tv(tv
, fetch_int(dev
, name
));
76 * Convert device name to an index in the list of ports in bridge.
78 * Old API does bridge operations as if ports were an array
79 * inside bridge structure.
81 static int get_portno(const char *brname
, const char *ifname
)
84 int ifindex
= if_nametoindex(ifname
);
85 int ifindices
[MAX_PORTS
];
86 unsigned long args
[4] = { BRCTL_GET_PORT_LIST
,
87 (unsigned long)ifindices
, MAX_PORTS
, 0 };
93 memset(ifindices
, 0, sizeof(ifindices
));
94 strncpy(ifr
.ifr_name
, brname
, IFNAMSIZ
);
95 ifr
.ifr_data
= (char *) &args
;
97 if (ioctl(br_socket_fd
, SIOCDEVPRIVATE
, &ifr
) < 0) {
98 dprintf("get_portno: get ports of %s failed: %s\n",
99 brname
, strerror(errno
));
103 for (i
= 0; i
< MAX_PORTS
; i
++) {
104 if (ifindices
[i
] == ifindex
)
108 dprintf("%s is not a in bridge %s\n", ifname
, brname
);
113 /* get information via ioctl */
114 static int old_get_bridge_info(const char *bridge
, struct bridge_info
*info
)
117 struct __bridge_info i
;
118 unsigned long args
[4] = { BRCTL_GET_BRIDGE_INFO
,
119 (unsigned long) &i
, 0, 0 };
121 memset(info
, 0, sizeof(*info
));
122 strncpy(ifr
.ifr_name
, bridge
, IFNAMSIZ
);
123 ifr
.ifr_data
= (char *) &args
;
125 if (ioctl(br_socket_fd
, SIOCDEVPRIVATE
, &ifr
) < 0) {
126 dprintf("%s: can't get info %s\n",
127 bridge
, strerror(errno
));
131 memcpy(&info
->designated_root
, &i
.designated_root
, 8);
132 memcpy(&info
->bridge_id
, &i
.bridge_id
, 8);
133 info
->root_path_cost
= i
.root_path_cost
;
134 info
->root_port
= i
.root_port
;
135 info
->topology_change
= i
.topology_change
;
136 info
->topology_change_detected
= i
.topology_change_detected
;
137 info
->stp_enabled
= i
.stp_enabled
;
138 __jiffies_to_tv(&info
->max_age
, i
.max_age
);
139 __jiffies_to_tv(&info
->hello_time
, i
.hello_time
);
140 __jiffies_to_tv(&info
->forward_delay
, i
.forward_delay
);
141 __jiffies_to_tv(&info
->bridge_max_age
, i
.bridge_max_age
);
142 __jiffies_to_tv(&info
->bridge_hello_time
, i
.bridge_hello_time
);
143 __jiffies_to_tv(&info
->bridge_forward_delay
, i
.bridge_forward_delay
);
144 __jiffies_to_tv(&info
->ageing_time
, i
.ageing_time
);
145 __jiffies_to_tv(&info
->hello_timer_value
, i
.hello_timer_value
);
146 __jiffies_to_tv(&info
->tcn_timer_value
, i
.tcn_timer_value
);
147 __jiffies_to_tv(&info
->topology_change_timer_value
,
148 i
.topology_change_timer_value
);
149 __jiffies_to_tv(&info
->gc_timer_value
, i
.gc_timer_value
);
155 * Get bridge parameters using either sysfs or old
158 int br_get_bridge_info(const char *bridge
, struct bridge_info
*info
)
161 char path
[SYSFS_PATH_MAX
];
163 snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"%s/bridge", bridge
);
166 dprintf("path '%s' is not a directory\n", path
);
170 memset(info
, 0, sizeof(*info
));
171 fetch_id(path
, "root_id", &info
->designated_root
);
172 fetch_id(path
, "bridge_id", &info
->bridge_id
);
173 info
->root_path_cost
= fetch_int(path
, "root_path_cost");
174 fetch_tv(path
, "max_age", &info
->max_age
);
175 fetch_tv(path
, "hello_time", &info
->hello_time
);
176 fetch_tv(path
, "forward_delay", &info
->forward_delay
);
177 fetch_tv(path
, "max_age", &info
->bridge_max_age
);
178 fetch_tv(path
, "hello_time", &info
->bridge_hello_time
);
179 fetch_tv(path
, "forward_delay", &info
->bridge_forward_delay
);
180 fetch_tv(path
, "ageing_time", &info
->ageing_time
);
181 fetch_tv(path
, "hello_timer", &info
->hello_timer_value
);
182 fetch_tv(path
, "tcn_timer", &info
->tcn_timer_value
);
183 fetch_tv(path
, "topology_change_timer",
184 &info
->topology_change_timer_value
);;
185 fetch_tv(path
, "gc_timer", &info
->gc_timer_value
);
187 info
->root_port
= fetch_int(path
, "root_port");
188 info
->stp_enabled
= fetch_int(path
, "stp_state");
189 info
->topology_change
= fetch_int(path
, "topology_change");
190 info
->topology_change_detected
= fetch_int(path
, "topology_change_detected");
196 return old_get_bridge_info(bridge
, info
);
199 static int old_get_port_info(const char *brname
, const char *port
,
200 struct port_info
*info
)
202 struct __port_info i
;
205 memset(info
, 0, sizeof(*info
));
207 index
= get_portno(brname
, port
);
213 unsigned long args
[4] = { BRCTL_GET_PORT_INFO
,
214 (unsigned long) &i
, index
, 0 };
216 strncpy(ifr
.ifr_name
, brname
, IFNAMSIZ
);
217 ifr
.ifr_data
= (char *) &args
;
219 if (ioctl(br_socket_fd
, SIOCDEVPRIVATE
, &ifr
) < 0) {
220 dprintf("old can't get port %s(%d) info %s\n",
221 brname
, index
, strerror(errno
));
226 info
->port_no
= index
;
227 memcpy(&info
->designated_root
, &i
.designated_root
, 8);
228 memcpy(&info
->designated_bridge
, &i
.designated_bridge
, 8);
229 info
->port_id
= i
.port_id
;
230 info
->designated_port
= i
.designated_port
;
231 info
->path_cost
= i
.path_cost
;
232 info
->designated_cost
= i
.designated_cost
;
233 info
->state
= i
.state
;
234 info
->top_change_ack
= i
.top_change_ack
;
235 info
->config_pending
= i
.config_pending
;
236 __jiffies_to_tv(&info
->message_age_timer_value
,
237 i
.message_age_timer_value
);
238 __jiffies_to_tv(&info
->forward_delay_timer_value
,
239 i
.forward_delay_timer_value
);
240 __jiffies_to_tv(&info
->hold_timer_value
, i
.hold_timer_value
);
241 info
->hairpin_mode
= 0;
246 * Get information about port on bridge.
248 int br_get_port_info(const char *brname
, const char *port
,
249 struct port_info
*info
)
252 char path
[SYSFS_PATH_MAX
];
254 snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"%s/brport", port
);
259 memset(info
, 0, sizeof(*info
));
261 fetch_id(path
, "designated_root", &info
->designated_root
);
262 fetch_id(path
, "designated_bridge", &info
->designated_bridge
);
263 info
->port_no
= fetch_int(path
, "port_no");
264 info
->port_id
= fetch_int(path
, "port_id");
265 info
->designated_port
= fetch_int(path
, "designated_port");
266 info
->path_cost
= fetch_int(path
, "path_cost");
267 info
->designated_cost
= fetch_int(path
, "designated_cost");
268 info
->state
= fetch_int(path
, "state");
269 info
->top_change_ack
= fetch_int(path
, "change_ack");
270 info
->config_pending
= fetch_int(path
, "config_pending");
271 fetch_tv(path
, "message_age_timer", &info
->message_age_timer_value
);
272 fetch_tv(path
, "forward_delay_timer", &info
->forward_delay_timer_value
);
273 fetch_tv(path
, "hold_timer", &info
->hold_timer_value
);
274 info
->hairpin_mode
= fetch_int(path
, "hairpin_mode");
280 return old_get_port_info(brname
, port
, info
);
284 static int br_set(const char *bridge
, const char *name
,
285 unsigned long value
, unsigned long oldcode
)
288 char path
[SYSFS_PATH_MAX
];
291 snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"%s/%s", bridge
, name
);
293 f
= fopen(path
, "w");
295 ret
= fprintf(f
, "%ld\n", value
);
298 /* fallback to old ioctl */
300 unsigned long args
[4] = { oldcode
, value
, 0, 0 };
302 strncpy(ifr
.ifr_name
, bridge
, IFNAMSIZ
);
303 ifr
.ifr_data
= (char *) &args
;
304 ret
= ioctl(br_socket_fd
, SIOCDEVPRIVATE
, &ifr
);
307 return ret
< 0 ? errno
: 0;
310 int br_set_bridge_forward_delay(const char *br
, struct timeval
*tv
)
312 return br_set(br
, "forward_delay", __tv_to_jiffies(tv
),
313 BRCTL_SET_BRIDGE_FORWARD_DELAY
);
316 int br_set_bridge_hello_time(const char *br
, struct timeval
*tv
)
318 return br_set(br
, "hello_time", __tv_to_jiffies(tv
),
319 BRCTL_SET_BRIDGE_HELLO_TIME
);
322 int br_set_bridge_max_age(const char *br
, struct timeval
*tv
)
324 return br_set(br
, "max_age", __tv_to_jiffies(tv
),
325 BRCTL_SET_BRIDGE_MAX_AGE
);
328 int br_set_ageing_time(const char *br
, struct timeval
*tv
)
330 return br_set(br
, "ageing_time", __tv_to_jiffies(tv
),
331 BRCTL_SET_AGEING_TIME
);
334 int br_set_stp_state(const char *br
, int stp_state
)
336 return br_set(br
, "stp_state", stp_state
, BRCTL_SET_BRIDGE_STP_STATE
);
339 int br_set_bridge_priority(const char *br
, int bridge_priority
)
341 return br_set(br
, "priority", bridge_priority
,
342 BRCTL_SET_BRIDGE_PRIORITY
);
345 static int port_set(const char *bridge
, const char *ifname
,
346 const char *name
, unsigned long value
,
347 unsigned long oldcode
)
350 char path
[SYSFS_PATH_MAX
];
353 snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"%s/brport/%s", ifname
, name
);
354 f
= fopen(path
, "w");
356 ret
= fprintf(f
, "%ld\n", value
);
359 int index
= get_portno(bridge
, ifname
);
365 unsigned long args
[4] = { oldcode
, index
, value
, 0 };
367 strncpy(ifr
.ifr_name
, bridge
, IFNAMSIZ
);
368 ifr
.ifr_data
= (char *) &args
;
369 ret
= ioctl(br_socket_fd
, SIOCDEVPRIVATE
, &ifr
);
373 return ret
< 0 ? errno
: 0;
376 int br_set_port_priority(const char *bridge
, const char *port
, int priority
)
378 return port_set(bridge
, port
, "priority", priority
, BRCTL_SET_PORT_PRIORITY
);
381 int br_set_path_cost(const char *bridge
, const char *port
, int cost
)
383 return port_set(bridge
, port
, "path_cost", cost
, BRCTL_SET_PATH_COST
);
386 int br_set_hairpin_mode(const char *bridge
, const char *port
, int hairpin_mode
)
388 return port_set(bridge
, port
, "hairpin_mode", hairpin_mode
, 0);
391 static inline void __copy_fdb(struct fdb_entry
*ent
,
392 const struct __fdb_entry
*f
)
394 memcpy(ent
->mac_addr
, f
->mac_addr
, 6);
395 ent
->port_no
= f
->port_no
;
396 ent
->is_local
= f
->is_local
;
397 __jiffies_to_tv(&ent
->ageing_timer_value
, f
->ageing_timer_value
);
400 int br_read_fdb(const char *bridge
, struct fdb_entry
*fdbs
,
401 unsigned long offset
, int num
)
405 struct __fdb_entry fe
[num
];
406 char path
[SYSFS_PATH_MAX
];
408 /* open /sys/class/net/brXXX/brforward */
409 snprintf(path
, SYSFS_PATH_MAX
, SYSFS_CLASS_NET
"%s/brforward", bridge
);
410 f
= fopen(path
, "r");
412 fseek(f
, offset
*sizeof(struct __fdb_entry
), SEEK_SET
);
413 n
= fread(fe
, sizeof(struct __fdb_entry
), num
, f
);
416 /* old kernel, use ioctl */
417 unsigned long args
[4] = { BRCTL_GET_FDB_ENTRIES
,
423 strncpy(ifr
.ifr_name
, bridge
, IFNAMSIZ
);
424 ifr
.ifr_data
= (char *) args
;
427 n
= ioctl(br_socket_fd
, SIOCDEVPRIVATE
, &ifr
);
429 /* table can change during ioctl processing */
430 if (n
< 0 && errno
== EAGAIN
&& ++retries
< 10) {
436 for (i
= 0; i
< n
; i
++)
437 __copy_fdb(fdbs
+i
, fe
+i
);