├── .gitignore ├── scripts └── bridge-stp ├── CMakeLists.txt ├── ubus.h ├── config.h ├── worker.h ├── bridge_track.h ├── packet.h ├── driver.h ├── netif_utils.h ├── config.c ├── libnetlink.h ├── log.h ├── worker.c ├── bridge_ctl.h ├── main.c ├── hmac_md5.c ├── netif_utils.c ├── brmon.c ├── packet.c ├── ubus.c ├── libnetlink.c ├── bridge_track.c └── mstp.h /.gitignore: -------------------------------------------------------------------------------- 1 | /Makefile 2 | CMakeCache.txt 3 | CMakeFiles 4 | *.cmake 5 | install_manifest.txt 6 | /ustpd 7 | -------------------------------------------------------------------------------- /scripts/bridge-stp: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | bridge="$1" 3 | cmd="$2" 4 | 5 | export PATH='/sbin:/usr/sbin:/bin:/usr/bin' 6 | 7 | case "$cmd" in 8 | start) enabled=true ;; 9 | stop) enabled=false ;; 10 | *) 11 | echo "Usage: $0 {start|stop}" 12 | exit 1 13 | ;; 14 | esac 15 | 16 | ubus call ustp bridge_state '{ "name": "'"$bridge"'", "enabled": '$enabled' }' 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | PROJECT(ustp C) 4 | 5 | set(THREADS_PREFER_PTHREAD_FLAG ON) 6 | find_package(Threads) 7 | 8 | ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations -Wno-error=missing-declarations -I${CMAKE_SOURCE_DIR}) 9 | 10 | ADD_EXECUTABLE(ustpd bridge_track.c brmon.c hmac_md5.c libnetlink.c mstp.c netif_utils.c packet.c worker.c config.c main.c ubus.c) 11 | TARGET_LINK_LIBRARIES(ustpd ubox ubus Threads::Threads) 12 | 13 | SET(CMAKE_INSTALL_PREFIX /) 14 | 15 | INSTALL(TARGETS ustpd 16 | RUNTIME DESTINATION sbin 17 | ) 18 | INSTALL(FILES scripts/bridge-stp 19 | DESTINATION sbin 20 | ) 21 | -------------------------------------------------------------------------------- /ubus.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ustp - OpenWrt STP/RSTP/MSTP daemon 3 | * Copyright (C) 2021 Felix Fietkau 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License version 2 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | #ifndef __UBUS_H 15 | #define __UBUS_H 16 | 17 | void ustp_ubus_init(void); 18 | void ustp_ubus_exit(void); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /config.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ustp - OpenWrt STP/RSTP/MSTP daemon 3 | * Copyright (C) 2021 Felix Fietkau 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License version 2 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | #ifndef __CONFIG_H 15 | #define __CONFIG_H 16 | 17 | #include 18 | #include 19 | #include "mstp.h" 20 | 21 | extern struct avl_tree bridge_config; 22 | 23 | struct bridge_config { 24 | struct avl_node node; 25 | uint32_t timestamp; 26 | CIST_BridgeConfig config; 27 | }; 28 | 29 | struct bridge_config *bridge_config_get(const char *name, bool create); 30 | void bridge_config_expire(void); 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /worker.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ustp - OpenWrt STP/RSTP/MSTP daemon 3 | * Copyright (C) 2021 Felix Fietkau 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License version 2 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | #ifndef __WORKER_H 15 | #define __WORKER_H 16 | 17 | #include "mstp.h" 18 | 19 | enum worker_event_type { 20 | WORKER_EV_SHUTDOWN, 21 | WORKER_EV_RECV_PACKET, 22 | WORKER_EV_BRIDGE_EVENT, 23 | WORKER_EV_BRIDGE_ADD, 24 | WORKER_EV_BRIDGE_REMOVE, 25 | WORKER_EV_ONE_SECOND, 26 | }; 27 | 28 | struct worker_event { 29 | enum worker_event_type type; 30 | 31 | int bridge_idx; 32 | CIST_BridgeConfig bridge_config; 33 | }; 34 | 35 | int worker_init(void); 36 | void worker_cleanup(void); 37 | void worker_queue_event(struct worker_event *ev); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /bridge_track.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | This program is free software; you can redistribute it and/or modify it 3 | under the terms of the GNU General Public License as published by the Free 4 | Software Foundation; either version 2 of the License, or (at your option) 5 | any later version. 6 | 7 | This program is distributed in the hope that it will be useful, but WITHOUT 8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 9 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 10 | more details. 11 | 12 | You should have received a copy of the GNU General Public License along with 13 | this program; if not, write to the Free Software Foundation, Inc., 59 14 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 15 | 16 | The full GNU General Public License is included in this distribution in the 17 | file called LICENSE. 18 | 19 | ******************************************************************************/ 20 | 21 | #ifndef MSTPD_BRIDGE_TRACK_H 22 | #define MSTPD_BRIDGE_TRACK_H 23 | 24 | #include "mstp.h" 25 | 26 | int bridge_create(int bridge_idx, CIST_BridgeConfig *cfg); 27 | void bridge_delete(int bridge_idx); 28 | int bridge_track_fini(void); 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /packet.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | 4 | This program is free software; you can redistribute it and/or modify it 5 | under the terms of the GNU General Public License as published by the Free 6 | Software Foundation; either version 2 of the License, or (at your option) 7 | any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but WITHOUT 10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program; if not, write to the Free Software Foundation, Inc., 59 16 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | 18 | The full GNU General Public License is included in this distribution in the 19 | file called LICENSE. 20 | 21 | Authors: Srinivas Aji 22 | 23 | ******************************************************************************/ 24 | 25 | #ifndef PACKET_SOCK_H 26 | #define PACKET_SOCK_H 27 | 28 | #include 29 | 30 | void packet_send(int ifindex, const struct iovec *iov, int iov_count, int len); 31 | int packet_sock_init(void); 32 | void packet_rcv(); 33 | 34 | #endif /* PACKET_SOCK_H */ 35 | -------------------------------------------------------------------------------- /driver.h: -------------------------------------------------------------------------------- 1 | /* 2 | * driver.h Driver-specific code. 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 6 | * as published by the Free Software Foundation; either version 7 | * 2 of the License, or (at your option) any later version. 8 | * 9 | * Authors: Vitalii Demianets 10 | */ 11 | 12 | #ifndef _MSTP_DRIVER_H 13 | #define _MSTP_DRIVER_H 14 | 15 | #include "mstp.h" 16 | 17 | static inline int 18 | driver_set_new_state(per_tree_port_t *ptp, int new_state) 19 | { 20 | return new_state; 21 | } 22 | 23 | static inline void 24 | driver_flush_all_fids(per_tree_port_t *ptp) 25 | { 26 | MSTP_IN_all_fids_flushed(ptp); 27 | } 28 | 29 | static inline unsigned int 30 | driver_set_ageing_time(port_t *prt, unsigned int ageingTime) 31 | { 32 | return ageingTime; 33 | } 34 | 35 | static inline bool 36 | driver_create_msti(bridge_t *br, __u16 mstid) 37 | { 38 | return true; 39 | } 40 | 41 | static inline bool 42 | driver_delete_msti(bridge_t *br, __u16 mstid) 43 | { 44 | return true; 45 | } 46 | 47 | static inline bool 48 | driver_create_bridge(bridge_t *br, __u8 *macaddr) 49 | { 50 | return true; 51 | } 52 | 53 | static inline bool 54 | driver_create_port(port_t *prt, __u16 portno) 55 | { 56 | return true; 57 | } 58 | 59 | static inline void driver_delete_bridge(bridge_t *br) 60 | { 61 | } 62 | 63 | static inline void driver_delete_port(port_t *prt) 64 | { 65 | } 66 | 67 | #endif /* _MSTP_DRIVER_H */ 68 | -------------------------------------------------------------------------------- /netif_utils.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | Copyright (c) 2011 Factor-SPE 4 | 5 | This program is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License as published by the Free 7 | Software Foundation; either version 2 of the License, or (at your option) 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program; if not, write to the Free Software Foundation, Inc., 59 17 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | The full GNU General Public License is included in this distribution in the 20 | file called LICENSE. 21 | 22 | Authors: Srinivas Aji 23 | Authors: Vitalii Demianets 24 | 25 | ******************************************************************************/ 26 | 27 | #ifndef NETIF_UTILS_H 28 | #define NETIF_UTILS_H 29 | 30 | /* An inet socket for everyone to use for ifreqs. */ 31 | int netsock_init(void); 32 | 33 | int get_hwaddr(char *ifname, unsigned char *hwaddr); 34 | int get_flags(char *ifname); 35 | int if_shutdown(char *ifname); 36 | 37 | int ethtool_get_speed_duplex(char *ifname, int *speed, int *duplex); 38 | 39 | bool is_bridge(char *if_name); 40 | 41 | int get_bpdu_filter(char *if_name); 42 | int get_bridge_portno(char *if_name); 43 | 44 | #endif /* NETIF_UTILS_H */ 45 | -------------------------------------------------------------------------------- /config.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ustp - OpenWrt STP/RSTP/MSTP daemon 3 | * Copyright (C) 2021 Felix Fietkau 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License version 2 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | #include "config.h" 20 | 21 | AVL_TREE(bridge_config, avl_strcmp, false, NULL); 22 | 23 | static uint32_t bridge_config_timestamp(void) 24 | { 25 | struct timespec ts; 26 | 27 | clock_gettime(CLOCK_MONOTONIC, &ts); 28 | 29 | return ts.tv_sec; 30 | } 31 | 32 | struct bridge_config * 33 | bridge_config_get(const char *name, bool create) 34 | { 35 | struct bridge_config *cfg; 36 | char *name_buf; 37 | 38 | cfg = avl_find_element(&bridge_config, name, cfg, node); 39 | if (cfg) 40 | goto out; 41 | 42 | if (!create) 43 | return NULL; 44 | 45 | cfg = calloc_a(sizeof(*cfg), &name_buf, strlen(name) + 1); 46 | cfg->node.key = strcpy(name_buf, name); 47 | avl_insert(&bridge_config, &cfg->node); 48 | 49 | out: 50 | cfg->timestamp = bridge_config_timestamp(); 51 | 52 | return cfg; 53 | } 54 | 55 | void bridge_config_expire(void) 56 | { 57 | struct bridge_config *cfg, *tmp; 58 | uint32_t ts; 59 | 60 | ts = bridge_config_timestamp(); 61 | avl_for_each_element_safe(&bridge_config, cfg, node, tmp) { 62 | if (ts - cfg->timestamp < 60) 63 | continue; 64 | 65 | avl_delete(&bridge_config, &cfg->node); 66 | free(cfg); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /libnetlink.h: -------------------------------------------------------------------------------- 1 | #ifndef __LIBNETLINK_H__ 2 | #define __LIBNETLINK_H__ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | struct rtnl_handle 11 | { 12 | int fd; 13 | struct sockaddr_nl local; 14 | struct sockaddr_nl peer; 15 | __u32 seq; 16 | __u32 dump; 17 | }; 18 | 19 | int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions); 20 | int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, 21 | int protocol); 22 | void rtnl_close(struct rtnl_handle *rth); 23 | int rtnl_wilddump_request(struct rtnl_handle *rth, int fam, int type); 24 | int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len); 25 | 26 | typedef int (*rtnl_filter_t)(const struct sockaddr_nl *, struct nlmsghdr *n, 27 | void *); 28 | int rtnl_dump_filter(struct rtnl_handle *rth, rtnl_filter_t filter, 29 | void *arg1, rtnl_filter_t junk, void *arg2); 30 | int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, 31 | unsigned groups, struct nlmsghdr *answer, rtnl_filter_t junk, 32 | void *jarg); 33 | int rtnl_send(struct rtnl_handle *rth, const char *buf, int); 34 | 35 | int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data); 36 | int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data); 37 | int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, 38 | int alen); 39 | int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len); 40 | int rta_addattr8(struct rtattr *rta, int maxlen, int type, __u8 data); 41 | int rta_addattr16(struct rtattr *rta, int maxlen, int type, __u16 data); 42 | int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data); 43 | int rta_addattr64(struct rtattr *rta, int maxlen, int type, __u64 data); 44 | int rta_addattr_l(struct rtattr *rta, int maxlen, int type, 45 | const void *data, int alen); 46 | 47 | int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len); 48 | int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, 49 | int len); 50 | 51 | struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type); 52 | int rta_nest_end(struct rtattr *rta, struct rtattr *nest); 53 | 54 | #define RTA_TAIL(rta) \ 55 | ((struct rtattr *) (((void *) (rta)) + \ 56 | RTA_ALIGN((rta)->rta_len))) 57 | 58 | #define parse_rtattr_nested(tb, max, rta) \ 59 | (parse_rtattr((tb), (max), RTA_DATA(rta), RTA_PAYLOAD(rta))) 60 | 61 | int rtnl_listen(struct rtnl_handle *, rtnl_filter_t handler, void *jarg); 62 | int rtnl_from_file(FILE *, rtnl_filter_t handler, void *jarg); 63 | 64 | #define NLMSG_TAIL(nmsg) \ 65 | ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) 66 | 67 | #endif /* __LIBNETLINK_H__ */ 68 | -------------------------------------------------------------------------------- /log.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | Copyright (c) 2011 Factor-SPE 4 | 5 | This program is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License as published by the Free 7 | Software Foundation; either version 2 of the License, or (at your option) 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program; if not, write to the Free Software Foundation, Inc., 59 17 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | The full GNU General Public License is included in this distribution in the 20 | file called LICENSE. 21 | 22 | Authors: Srinivas Aji 23 | Authors: Vitalii Demianets 24 | 25 | ******************************************************************************/ 26 | 27 | #ifndef LOG_H 28 | #define LOG_H 29 | 30 | #include 31 | #include 32 | 33 | #define LOG_LEVEL_NONE 0 34 | #define LOG_LEVEL_ERROR 1 35 | #define LOG_LEVEL_INFO 2 36 | #define LOG_LEVEL_DEBUG 3 37 | #define LOG_LEVEL_STATE_MACHINE_TRANSITION 4 38 | 39 | #ifdef DEBUG 40 | #define LOG_LEVEL_MAX 100 41 | #else 42 | #define LOG_LEVEL_MAX LOG_LEVEL_INFO 43 | #endif 44 | 45 | #define LOG_LEVEL_DEFAULT LOG_LEVEL_ERROR 46 | 47 | extern void Dprintf(int level, const char *fmt, ...); 48 | extern int log_level; 49 | 50 | #define PRINT(_level, _fmt, _args...) \ 51 | ({ \ 52 | if ((_level) <= LOG_LEVEL_MAX) \ 53 | Dprintf(_level, _fmt, ##_args); \ 54 | }) 55 | 56 | #define TSTM(x, y, _fmt, _args...) \ 57 | do if(!(x)) \ 58 | { \ 59 | PRINT(LOG_LEVEL_ERROR, "Error in %s at %s:%d verifying %s. " _fmt, \ 60 | __PRETTY_FUNCTION__, __FILE__, __LINE__, #x, ##_args); \ 61 | return y; \ 62 | } while (0) 63 | 64 | #define TST(x, y) TSTM(x, y, "") 65 | 66 | #define LOG(_fmt, _args...) \ 67 | PRINT(LOG_LEVEL_DEBUG, "%s: " _fmt, __PRETTY_FUNCTION__, ##_args) 68 | 69 | #define INFO(_fmt, _args...) \ 70 | PRINT(LOG_LEVEL_INFO, "%s: " _fmt, __PRETTY_FUNCTION__, ##_args) 71 | 72 | #define ERROR(_fmt, _args...) \ 73 | PRINT(LOG_LEVEL_ERROR, "%s: " _fmt, __PRETTY_FUNCTION__, ##_args) 74 | 75 | static inline void dump_hex(void *b, int l) 76 | { 77 | unsigned char *buf = b; 78 | char logbuf[80]; 79 | int i, j; 80 | for (i = 0; i < l; i += 16) { 81 | for (j = 0; j < 16 && i + j < l; ++j) 82 | sprintf(logbuf + j * 3, " %02x", buf[i + j]); 83 | PRINT(LOG_LEVEL_INFO, "%s", logbuf); 84 | } 85 | PRINT(LOG_LEVEL_INFO, "\n"); 86 | } 87 | 88 | #endif /* LOG_H */ 89 | -------------------------------------------------------------------------------- /worker.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ustp - OpenWrt STP/RSTP/MSTP daemon 3 | * Copyright (C) 2021 Felix Fietkau 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License version 2 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include 20 | 21 | #include "worker.h" 22 | #include "bridge_ctl.h" 23 | #include "bridge_track.h" 24 | #include "packet.h" 25 | 26 | static pthread_t w_thread; 27 | static pthread_mutex_t w_lock = PTHREAD_MUTEX_INITIALIZER; 28 | static pthread_cond_t w_cond = PTHREAD_COND_INITIALIZER; 29 | static LIST_HEAD(w_queue); 30 | static struct uloop_timeout w_timer; 31 | 32 | struct worker_queued_event { 33 | struct list_head list; 34 | struct worker_event ev; 35 | }; 36 | 37 | static struct worker_event *worker_next_event(void) 38 | { 39 | struct worker_queued_event *ev; 40 | static struct worker_event ev_data; 41 | 42 | pthread_mutex_lock(&w_lock); 43 | while (list_empty(&w_queue)) 44 | pthread_cond_wait(&w_cond, &w_lock); 45 | 46 | ev = list_first_entry(&w_queue, struct worker_queued_event, list); 47 | list_del(&ev->list); 48 | pthread_mutex_unlock(&w_lock); 49 | 50 | memcpy(&ev_data, &ev->ev, sizeof(ev_data)); 51 | free(ev); 52 | 53 | return &ev_data; 54 | } 55 | 56 | static void 57 | handle_worker_event(struct worker_event *ev) 58 | { 59 | switch (ev->type) { 60 | case WORKER_EV_ONE_SECOND: 61 | bridge_one_second(); 62 | break; 63 | case WORKER_EV_BRIDGE_EVENT: 64 | bridge_event_handler(); 65 | break; 66 | case WORKER_EV_RECV_PACKET: 67 | packet_rcv(); 68 | break; 69 | case WORKER_EV_BRIDGE_ADD: 70 | bridge_create(ev->bridge_idx, &ev->bridge_config); 71 | break; 72 | case WORKER_EV_BRIDGE_REMOVE: 73 | bridge_delete(ev->bridge_idx); 74 | break; 75 | default: 76 | return; 77 | } 78 | } 79 | 80 | static void *worker_thread_fn(void *arg) 81 | { 82 | struct worker_event *ev; 83 | 84 | while (1) { 85 | ev = worker_next_event(); 86 | if (ev->type == WORKER_EV_SHUTDOWN) 87 | break; 88 | 89 | handle_worker_event(ev); 90 | } 91 | 92 | return NULL; 93 | } 94 | 95 | static void worker_timer_cb(struct uloop_timeout *t) 96 | { 97 | struct worker_event ev = { 98 | .type = WORKER_EV_ONE_SECOND, 99 | }; 100 | 101 | uloop_timeout_set(t, 1000); 102 | worker_queue_event(&ev); 103 | } 104 | 105 | int worker_init(void) 106 | { 107 | w_timer.cb = worker_timer_cb; 108 | uloop_timeout_set(&w_timer, 1000); 109 | 110 | return pthread_create(&w_thread, NULL, worker_thread_fn, NULL); 111 | } 112 | 113 | void worker_cleanup(void) 114 | { 115 | struct worker_event ev = { 116 | .type = WORKER_EV_SHUTDOWN, 117 | }; 118 | 119 | worker_queue_event(&ev); 120 | pthread_join(w_thread, NULL); 121 | } 122 | 123 | void worker_queue_event(struct worker_event *ev) 124 | { 125 | struct worker_queued_event *evc; 126 | 127 | evc = malloc(sizeof(*evc)); 128 | memcpy(&evc->ev, ev, sizeof(*ev)); 129 | 130 | pthread_mutex_lock(&w_lock); 131 | list_add_tail(&evc->list, &w_queue); 132 | pthread_mutex_unlock(&w_lock); 133 | 134 | pthread_cond_signal(&w_cond); 135 | } 136 | -------------------------------------------------------------------------------- /bridge_ctl.h: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | Copyright (c) 2011 Factor-SPE 4 | 5 | This program is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License as published by the Free 7 | Software Foundation; either version 2 of the License, or (at your option) 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program; if not, write to the Free Software Foundation, Inc., 59 17 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | The full GNU General Public License is included in this distribution in the 20 | file called LICENSE. 21 | 22 | Authors: Srinivas Aji 23 | Authors: Vitalii Demianets 24 | 25 | ******************************************************************************/ 26 | 27 | #ifndef BRIDGE_CTL_H 28 | #define BRIDGE_CTL_H 29 | 30 | #include 31 | #include 32 | #include 33 | 34 | typedef struct 35 | { 36 | int if_index; 37 | __u8 macaddr[ETH_ALEN]; 38 | char name[IFNAMSIZ]; 39 | 40 | bool up; 41 | } sysdep_br_data_t; 42 | 43 | typedef struct 44 | { 45 | int if_index; 46 | __u8 macaddr[ETH_ALEN]; 47 | char name[IFNAMSIZ]; 48 | 49 | bool up; 50 | int speed, duplex; 51 | } sysdep_if_data_t; 52 | 53 | #define GET_PORT_SPEED(port) ((port)->sysdeps.speed) 54 | #define GET_PORT_DUPLEX(port) ((port)->sysdeps.duplex) 55 | 56 | /* Logging macros for mstp.c - they use system dependent info */ 57 | #define ERROR_BRNAME(_br, _fmt, _args...) ERROR("%s " _fmt, \ 58 | _br->sysdeps.name, ##_args) 59 | #define INFO_BRNAME(_br, _fmt, _args...) INFO("%s " _fmt, \ 60 | _br->sysdeps.name, ##_args) 61 | #define LOG_BRNAME(_br, _fmt, _args...) LOG("%s " _fmt, \ 62 | _br->sysdeps.name, ##_args) 63 | #define ERROR_PRTNAME(_br, _prt, _fmt, _args...) ERROR("%s:%s " _fmt, \ 64 | _br->sysdeps.name, _prt->sysdeps.name, ##_args) 65 | #define INFO_PRTNAME(_br, _prt, _fmt, _args...) INFO("%s:%s " _fmt, \ 66 | _br->sysdeps.name, _prt->sysdeps.name, ##_args) 67 | #define LOG_PRTNAME(_br, _prt, _fmt, _args...) LOG("%s:%s " _fmt, \ 68 | _br->sysdeps.name, _prt->sysdeps.name, ##_args) 69 | #define ERROR_MSTINAME(_br,_prt,_ptp,_fmt,_args...) ERROR("%s:%s:%hu " _fmt, \ 70 | _br->sysdeps.name, _prt->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args) 71 | #define INFO_MSTINAME(_br,_prt,_ptp,_fmt,_args...) INFO("%s:%s:%hu " _fmt, \ 72 | _br->sysdeps.name, _prt->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args) 73 | #define LOG_MSTINAME(_br,_prt,_ptp,_fmt,_args...) LOG("%s:%s:%hu " _fmt, \ 74 | _br->sysdeps.name, _prt->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args) 75 | #define SMLOG_MSTINAME(_ptp, _fmt, _args...) \ 76 | PRINT(LOG_LEVEL_STATE_MACHINE_TRANSITION, "%s: %s:%s:%hu " _fmt, \ 77 | __PRETTY_FUNCTION__, _ptp->port->bridge->sysdeps.name, \ 78 | _ptp->port->sysdeps.name, __be16_to_cpu(ptp->MSTID), ##_args) 79 | 80 | extern struct rtnl_handle rth_state; 81 | 82 | int init_bridge_ops(void); 83 | void bridge_event_handler(void); 84 | 85 | int bridge_notify(int br_index, int if_index, bool newlink, unsigned flags); 86 | 87 | void bridge_bpdu_rcv(int ifindex, const unsigned char *data, int len); 88 | 89 | void bridge_one_second(void); 90 | 91 | #endif /* BRIDGE_CTL_H */ 92 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | Copyright (c) 2011 Factor-SPE 4 | 5 | This program is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License as published by the Free 7 | Software Foundation; either version 2 of the License, or (at your option) 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program; if not, write to the Free Software Foundation, Inc., 59 17 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | The full GNU General Public License is included in this distribution in the 20 | file called LICENSE. 21 | 22 | Authors: Srinivas Aji 23 | Authors: Vitalii Demianets 24 | 25 | ******************************************************************************/ 26 | 27 | /* #define MISC_TEST_FUNCS */ 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "bridge_ctl.h" 40 | #include "netif_utils.h" 41 | #include "packet.h" 42 | #include "log.h" 43 | #include "mstp.h" 44 | #include "driver.h" 45 | #include "bridge_track.h" 46 | #include "worker.h" 47 | #include "ubus.h" 48 | 49 | #define APP_NAME "ustpd" 50 | 51 | static int print_to_syslog = 1; 52 | int log_level = LOG_LEVEL_DEFAULT; 53 | 54 | 55 | int main(int argc, char *argv[]) 56 | { 57 | int c; 58 | 59 | while((c = getopt(argc, argv, "sv:")) != -1) { 60 | switch (c) { 61 | case 's': 62 | print_to_syslog = 0; 63 | break; 64 | case 'v': { 65 | char *end; 66 | long l; 67 | l = strtoul(optarg, &end, 0); 68 | if(*optarg == 0 || *end != 0 || l > LOG_LEVEL_MAX) { 69 | ERROR("Invalid loglevel %s", optarg); 70 | exit(1); 71 | } 72 | log_level = l; 73 | break; 74 | } 75 | default: 76 | return -1; 77 | } 78 | } 79 | 80 | if (print_to_syslog) 81 | openlog(APP_NAME, LOG_PID, LOG_DAEMON); 82 | 83 | uloop_init(); 84 | 85 | TST(worker_init() == 0, -1); 86 | TST(packet_sock_init() == 0, -1); 87 | TST(netsock_init() == 0, -1); 88 | TST(init_bridge_ops() == 0, -1); 89 | ustp_ubus_init(); 90 | 91 | uloop_run(); 92 | bridge_track_fini(); 93 | worker_cleanup(); 94 | ustp_ubus_exit(); 95 | uloop_done(); 96 | 97 | return 0; 98 | } 99 | 100 | /*********************** Logging *********************/ 101 | 102 | #include 103 | #include 104 | 105 | static void vDprintf(int level, const char *fmt, va_list ap) 106 | { 107 | if(level > log_level) 108 | return; 109 | 110 | if(!print_to_syslog) 111 | { 112 | char logbuf[256]; 113 | logbuf[255] = 0; 114 | time_t clock; 115 | struct tm *local_tm; 116 | time(&clock); 117 | local_tm = localtime(&clock); 118 | int l = strftime(logbuf, sizeof(logbuf) - 1, "%F %T ", local_tm); 119 | vsnprintf(logbuf + l, sizeof(logbuf) - l - 1, fmt, ap); 120 | printf("%s\n", logbuf); 121 | fflush(stdout); 122 | } 123 | else 124 | { 125 | vsyslog((level <= LOG_LEVEL_INFO) ? LOG_INFO : LOG_DEBUG, fmt, ap); 126 | } 127 | } 128 | 129 | void Dprintf(int level, const char *fmt, ...) 130 | { 131 | va_list ap; 132 | va_start(ap, fmt); 133 | vDprintf(level, fmt, ap); 134 | va_end(ap); 135 | } 136 | -------------------------------------------------------------------------------- /hmac_md5.c: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All 2 | rights reserved. 3 | 4 | License to copy and use this software is granted provided that it 5 | is identified as the "RSA Data Security, Inc. MD5 Message-Digest 6 | Algorithm" in all material mentioning or referencing this software 7 | or this function. 8 | 9 | License is also granted to make and use derivative works provided 10 | that such works are identified as "derived from the RSA Data 11 | Security, Inc. MD5 Message-Digest Algorithm" in all material 12 | mentioning or referencing the derived work. 13 | 14 | RSA Data Security, Inc. makes no representations concerning either 15 | the merchantability of this software or the suitability of this 16 | software for any particular purpose. It is provided "as is" 17 | without express or implied warranty of any kind. 18 | 19 | These notices must be retained in any copies of any part of this 20 | documentation and/or software. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "mstp.h" 28 | 29 | /* 30 | ** Function: hmac_md5 from RFC-2104 31 | */ 32 | void hmac_md5(text, text_len, key, key_len, digest) 33 | unsigned char* text; /* pointer to data stream */ 34 | int text_len; /* length of data stream */ 35 | unsigned char* key; /* pointer to authentication key */ 36 | int key_len; /* length of authentication key */ 37 | caddr_t digest; /* caller digest to be filled in */ 38 | { 39 | md5_ctx_t context; 40 | unsigned char k_ipad[65]; /* inner padding - 41 | * key XORd with ipad 42 | */ 43 | unsigned char k_opad[65]; /* outer padding - 44 | * key XORd with opad 45 | */ 46 | unsigned char tk[16]; 47 | int i; 48 | /* if key is longer than 64 bytes reset it to key=MD5(key) */ 49 | if(key_len > 64) 50 | { 51 | md5_ctx_t tctx; 52 | 53 | md5_begin(&tctx); 54 | md5_hash(key, key_len, &tctx); 55 | md5_end(tk, &tctx); 56 | 57 | key = tk; 58 | key_len = 16; 59 | } 60 | 61 | /* 62 | * the HMAC_MD5 transform looks like: 63 | * 64 | * MD5(K XOR opad, MD5(K XOR ipad, text)) 65 | * 66 | * where K is an n byte key 67 | * ipad is the byte 0x36 repeated 64 times 68 | * opad is the byte 0x5c repeated 64 times 69 | * and text is the data being protected 70 | */ 71 | 72 | /* start out by storing key in pads */ 73 | bzero(k_ipad, sizeof k_ipad); 74 | bzero(k_opad, sizeof k_opad); 75 | bcopy(key, k_ipad, key_len); 76 | bcopy( key, k_opad, key_len); 77 | 78 | /* XOR key with ipad and opad values */ 79 | for(i = 0; i < 64; ++i) 80 | { 81 | k_ipad[i] ^= 0x36; 82 | k_opad[i] ^= 0x5c; 83 | } 84 | /* 85 | * perform inner MD5 86 | */ 87 | md5_begin(&context); /* init context for 1st 88 | * pass */ 89 | md5_hash(k_ipad, 64, &context); /* start with inner pad */ 90 | md5_hash(text, text_len, &context); /* then text of datagram */ 91 | md5_end(digest, &context); /* finish up 1st pass */ 92 | /* 93 | * perform outer MD5 94 | */ 95 | md5_begin(&context); /* init context for 2nd 96 | * pass */ 97 | md5_hash(k_opad, 64, &context); /* start with outer pad */ 98 | md5_hash(digest, 16, &context); /* then results of 1st 99 | * hash */ 100 | md5_end(digest, &context); /* finish up 2nd pass */ 101 | } 102 | -------------------------------------------------------------------------------- /netif_utils.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | Copyright (c) 2011 Factor-SPE 4 | 5 | This program is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License as published by the Free 7 | Software Foundation; either version 2 of the License, or (at your option) 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program; if not, write to the Free Software Foundation, Inc., 59 17 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | The full GNU General Public License is included in this distribution in the 20 | file called LICENSE. 21 | 22 | Authors: Srinivas Aji 23 | Authors: Vitalii Demianets 24 | 25 | ******************************************************************************/ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "netif_utils.h" 41 | #include "log.h" 42 | 43 | #ifndef SYSFS_CLASS_NET 44 | #define SYSFS_CLASS_NET "/sys/class/net" 45 | #endif 46 | 47 | static int netsock = -1; 48 | 49 | int netsock_init(void) 50 | { 51 | netsock = socket(AF_INET, SOCK_DGRAM, 0); 52 | if(0 > netsock) 53 | { 54 | ERROR("Couldn't open inet socket for ioctls: %m\n"); 55 | return -1; 56 | } 57 | return 0; 58 | } 59 | 60 | int get_hwaddr(char *ifname, __u8 *hwaddr) 61 | { 62 | struct ifreq ifr; 63 | memset(&ifr, 0, sizeof(ifr)); 64 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); 65 | if(0 > ioctl(netsock, SIOCGIFHWADDR, &ifr)) 66 | { 67 | ERROR("%s: get hw address failed: %m", ifname); 68 | return -1; 69 | } 70 | memcpy(hwaddr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); 71 | return 0; 72 | } 73 | 74 | int get_flags(char *ifname) 75 | { 76 | struct ifreq ifr; 77 | memset(&ifr, 0, sizeof(ifr)); 78 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); 79 | if(0 > ioctl(netsock, SIOCGIFFLAGS, &ifr)) 80 | { 81 | ERROR("%s: get interface flags failed: %m", ifname); 82 | return -1; 83 | } 84 | return ifr.ifr_flags; 85 | } 86 | 87 | int if_shutdown(char *ifname) 88 | { 89 | struct ifreq ifr; 90 | 91 | memset(&ifr, 0, sizeof(ifr)); 92 | /* TODO: Let's hope -1 is not a valid flag combination */ 93 | if(-1 == (ifr.ifr_flags = get_flags(ifname))) 94 | { 95 | return -1; 96 | } 97 | ifr.ifr_flags &= ~IFF_UP; 98 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); 99 | if(0 > ioctl(netsock, SIOCSIFFLAGS, &ifr)) 100 | { 101 | ERROR("%s: set if_down flag failed: %m", ifname); 102 | return -1; 103 | } 104 | return 0; 105 | } 106 | 107 | int ethtool_get_speed_duplex(char *ifname, int *speed, int *duplex) 108 | { 109 | struct ifreq ifr; 110 | memset(&ifr, 0, sizeof(ifr)); 111 | strncpy(ifr.ifr_name, ifname, IFNAMSIZ - 1); 112 | struct ethtool_cmd ecmd; 113 | 114 | ecmd.cmd = ETHTOOL_GSET; 115 | ifr.ifr_data = (caddr_t)&ecmd; 116 | if(0 > ioctl(netsock, SIOCETHTOOL, &ifr)) 117 | { 118 | INFO("Cannot get speed/duplex for %s: %m\n", ifname); 119 | return -1; 120 | } 121 | *speed = ecmd.speed; /* Ethtool speed is in Mbps */ 122 | *duplex = ecmd.duplex; /* We have same convention as ethtool. 123 | 0 = half, 1 = full */ 124 | return 0; 125 | } 126 | 127 | /********* Sysfs based utility functions *************/ 128 | 129 | /* This sysfs stuff might break with interface renames */ 130 | bool is_bridge(char *if_name) 131 | { 132 | char path[32 + IFNAMSIZ]; 133 | sprintf(path, SYSFS_CLASS_NET "/%s/bridge", if_name); 134 | return (0 == access(path, R_OK)); 135 | } 136 | 137 | static int get_port_file(const char *if_name, const char *file) 138 | { 139 | char path[32 + IFNAMSIZ]; 140 | sprintf(path, SYSFS_CLASS_NET "/%s/brport/%s", if_name, file); 141 | char buf[128]; 142 | int fd; 143 | long res = -1; 144 | TSTM((fd = open(path, O_RDONLY)) >= 0, -1, "%m"); 145 | int l; 146 | if((l = read(fd, buf, sizeof(buf) - 1)) < 0) { 147 | ERROR("Failed to read file %s: error %m", file); 148 | return -1; 149 | } 150 | if(0 == l) 151 | { 152 | ERROR("Empty %s file", file); 153 | goto out; 154 | } 155 | else if((sizeof(buf) - 1) == l) 156 | { 157 | ERROR("%s file too long", file); 158 | goto out; 159 | } 160 | buf[l] = 0; 161 | if('\n' == buf[l - 1]) 162 | buf[l - 1] = 0; 163 | char *end; 164 | res = strtoul(buf, &end, 0); 165 | if(0 != *end || INT_MAX < res) 166 | { 167 | ERROR("Invalid %s %s", file, buf); 168 | res = -1; 169 | } 170 | out: 171 | close(fd); 172 | return res; 173 | } 174 | 175 | int get_bpdu_filter(char *if_name) 176 | { 177 | return get_port_file(if_name, "bpdu_filter"); 178 | } 179 | 180 | int get_bridge_portno(char *if_name) 181 | { 182 | return get_port_file(if_name, "port_no"); 183 | } 184 | -------------------------------------------------------------------------------- /brmon.c: -------------------------------------------------------------------------------- 1 | /* 2 | * brmon.c RTnetlink listener. 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 6 | * as published by the Free Software Foundation; either version 7 | * 2 of the License, or (at your option) any later version. 8 | * 9 | * Authors: Stephen Hemminger 10 | * Modified by Srinivas Aji 11 | * for use in RSTP daemon. - 2006-09-01 12 | * Modified by Vitalii Demianets 13 | * for use in MSTP daemon. - 2011-07-18 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "log.h" 24 | #include "libnetlink.h" 25 | #include "bridge_ctl.h" 26 | #include "netif_utils.h" 27 | #include "worker.h" 28 | 29 | /* RFC 2863 operational status */ 30 | enum 31 | { 32 | IF_OPER_UNKNOWN, 33 | IF_OPER_NOTPRESENT, 34 | IF_OPER_DOWN, 35 | IF_OPER_LOWERLAYERDOWN, 36 | IF_OPER_TESTING, 37 | IF_OPER_DORMANT, 38 | IF_OPER_UP, 39 | }; 40 | 41 | /* link modes */ 42 | enum 43 | { 44 | IF_LINK_MODE_DEFAULT, 45 | IF_LINK_MODE_DORMANT, /* limit upward transition to dormant */ 46 | }; 47 | 48 | static const char *port_states[] = 49 | { 50 | [BR_STATE_DISABLED] = "disabled", 51 | [BR_STATE_LISTENING] = "listening", 52 | [BR_STATE_LEARNING] = "learning", 53 | [BR_STATE_FORWARDING] = "forwarding", 54 | [BR_STATE_BLOCKING] = "blocking", 55 | }; 56 | 57 | static struct rtnl_handle rth; 58 | static struct uloop_fd ufd; 59 | 60 | struct rtnl_handle rth_state; 61 | 62 | static int dump_msg(const struct sockaddr_nl *who, struct nlmsghdr *n, 63 | void *arg) 64 | { 65 | struct ifinfomsg *ifi = NLMSG_DATA(n); 66 | struct rtattr * tb[IFLA_MAX + 1]; 67 | int len = n->nlmsg_len; 68 | char b1[IFNAMSIZ]; 69 | int af_family; 70 | bool newlink; 71 | int br_index; 72 | 73 | if(n->nlmsg_type == NLMSG_DONE) 74 | return 0; 75 | 76 | len -= NLMSG_LENGTH(sizeof(*ifi)); 77 | if(len < 0) 78 | { 79 | return -1; 80 | } 81 | 82 | af_family = ifi->ifi_family; 83 | 84 | if(af_family != AF_BRIDGE && af_family != AF_UNSPEC) 85 | return 0; 86 | 87 | if(n->nlmsg_type != RTM_NEWLINK && n->nlmsg_type != RTM_DELLINK) 88 | return 0; 89 | 90 | parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifi), len); 91 | 92 | /* Check if we got this from bonding */ 93 | if(tb[IFLA_MASTER] && af_family != AF_BRIDGE) 94 | return 0; 95 | 96 | if(tb[IFLA_IFNAME] == NULL) 97 | { 98 | ERROR("BUG: nil ifname\n"); 99 | return -1; 100 | } 101 | 102 | if(n->nlmsg_type == RTM_DELLINK) 103 | LOG("Deleted "); 104 | 105 | LOG("%d: %s ", ifi->ifi_index, (char*)RTA_DATA(tb[IFLA_IFNAME])); 106 | 107 | if(tb[IFLA_OPERSTATE]) 108 | { 109 | __u8 state = *(__u8*)RTA_DATA(tb[IFLA_OPERSTATE]); 110 | switch (state) 111 | { 112 | case IF_OPER_UNKNOWN: 113 | LOG("Unknown "); 114 | break; 115 | case IF_OPER_NOTPRESENT: 116 | LOG("Not Present "); 117 | break; 118 | case IF_OPER_DOWN: 119 | LOG("Down "); 120 | break; 121 | case IF_OPER_LOWERLAYERDOWN: 122 | LOG("Lowerlayerdown "); 123 | break; 124 | case IF_OPER_TESTING: 125 | LOG("Testing "); 126 | break; 127 | case IF_OPER_DORMANT: 128 | LOG("Dormant "); 129 | break; 130 | case IF_OPER_UP: 131 | LOG("Up "); 132 | break; 133 | default: 134 | LOG("State(%d) ", state); 135 | } 136 | } 137 | 138 | if(tb[IFLA_MTU]) 139 | LOG("mtu %u ", *(int*)RTA_DATA(tb[IFLA_MTU])); 140 | 141 | if(tb[IFLA_MASTER]) 142 | { 143 | LOG("master %s ", 144 | if_indextoname(*(int*)RTA_DATA(tb[IFLA_MASTER]), b1)); 145 | } 146 | 147 | if(tb[IFLA_PROTINFO]) 148 | { 149 | uint8_t state = *(uint8_t *)RTA_DATA(tb[IFLA_PROTINFO]); 150 | if(state <= BR_STATE_BLOCKING) 151 | LOG("state %s", port_states[state]); 152 | else 153 | LOG("state (%d)", state); 154 | } 155 | 156 | newlink = (n->nlmsg_type == RTM_NEWLINK); 157 | 158 | if(tb[IFLA_MASTER]) 159 | br_index = *(int*)RTA_DATA(tb[IFLA_MASTER]); 160 | else if(is_bridge((char*)RTA_DATA(tb[IFLA_IFNAME]))) 161 | br_index = ifi->ifi_index; 162 | else 163 | br_index = -1; 164 | 165 | bridge_notify(br_index, ifi->ifi_index, newlink, ifi->ifi_flags); 166 | 167 | return 0; 168 | } 169 | 170 | void bridge_event_handler(void) 171 | { 172 | if(rtnl_listen(&rth, dump_msg, stdout) < 0) 173 | { 174 | ERROR("Error on bridge monitoring socket\n"); 175 | } 176 | } 177 | 178 | static void bridge_event_cb(struct uloop_fd *fd, unsigned int events) 179 | { 180 | struct worker_event ev = { 181 | .type = WORKER_EV_BRIDGE_EVENT 182 | }; 183 | 184 | worker_queue_event(&ev); 185 | } 186 | 187 | int init_bridge_ops(void) 188 | { 189 | if(rtnl_open(&rth, RTMGRP_LINK) < 0) 190 | { 191 | ERROR("Couldn't open rtnl socket for monitoring\n"); 192 | return -1; 193 | } 194 | 195 | if(rtnl_open(&rth_state, 0) < 0) 196 | { 197 | ERROR("Couldn't open rtnl socket for setting state\n"); 198 | return -1; 199 | } 200 | 201 | if(rtnl_wilddump_request(&rth, PF_BRIDGE, RTM_GETLINK) < 0) 202 | { 203 | ERROR("Cannot send dump request: %m\n"); 204 | return -1; 205 | } 206 | 207 | if(rtnl_dump_filter(&rth, dump_msg, stdout, NULL, NULL) < 0) 208 | { 209 | ERROR("Dump terminated\n"); 210 | return -1; 211 | } 212 | 213 | if(fcntl(rth.fd, F_SETFL, O_NONBLOCK) < 0) 214 | { 215 | ERROR("Error setting O_NONBLOCK: %m\n"); 216 | return -1; 217 | } 218 | 219 | ufd.fd = rth.fd; 220 | ufd.cb = bridge_event_cb; 221 | uloop_fd_add(&ufd, ULOOP_READ | ULOOP_EDGE_TRIGGER); 222 | bridge_event_cb(&ufd, 0); 223 | 224 | return 0; 225 | } 226 | -------------------------------------------------------------------------------- /packet.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | 4 | This program is free software; you can redistribute it and/or modify it 5 | under the terms of the GNU General Public License as published by the Free 6 | Software Foundation; either version 2 of the License, or (at your option) 7 | any later version. 8 | 9 | This program is distributed in the hope that it will be useful, but WITHOUT 10 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 12 | more details. 13 | 14 | You should have received a copy of the GNU General Public License along with 15 | this program; if not, write to the Free Software Foundation, Inc., 59 16 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 | 18 | The full GNU General Public License is included in this distribution in the 19 | file called LICENSE. 20 | 21 | Authors: Srinivas Aji 22 | Authors: Stephen Hemminger 23 | 24 | ******************************************************************************/ 25 | 26 | /* #define PACKET_DEBUG */ 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "netif_utils.h" 40 | #include "bridge_ctl.h" 41 | #include "packet.h" 42 | #include "log.h" 43 | #include "worker.h" 44 | 45 | static struct uloop_fd ufd; 46 | 47 | #ifdef PACKET_DEBUG 48 | static void dump_packet(const unsigned char *buf, int cc) 49 | { 50 | int i, j; 51 | for(i = 0; i < cc; i += 16) 52 | { 53 | for(j = 0; j < 16 && i + j < cc; ++j) 54 | printf(" %02x", buf[i + j]); 55 | printf("\n"); 56 | } 57 | printf("\n"); 58 | fflush(stdout); 59 | } 60 | #endif 61 | 62 | /* 63 | * To send/receive Spanning Tree packets we use PF_PACKET because 64 | * it allows the filtering we want but gives raw data 65 | */ 66 | void packet_send(int ifindex, const struct iovec *iov, int iov_count, int len) 67 | { 68 | int l; 69 | struct sockaddr_ll sl = 70 | { 71 | .sll_family = AF_PACKET, 72 | .sll_protocol = __constant_cpu_to_be16(ETH_P_802_2), 73 | .sll_ifindex = ifindex, 74 | .sll_halen = ETH_ALEN, 75 | }; 76 | 77 | if(iov_count > 0 && iov[0].iov_len > ETH_ALEN) 78 | memcpy(&sl.sll_addr, iov[0].iov_base, ETH_ALEN); 79 | 80 | struct msghdr msg = 81 | { 82 | .msg_name = &sl, 83 | .msg_namelen = sizeof(sl), 84 | .msg_iov = (struct iovec *)iov, 85 | .msg_iovlen = iov_count, 86 | .msg_control = NULL, 87 | .msg_controllen = 0, 88 | .msg_flags = 0, 89 | }; 90 | 91 | #ifdef PACKET_DEBUG 92 | printf("Transmit Dst index %d %02x:%02x:%02x:%02x:%02x:%02x\n", 93 | sl.sll_ifindex, 94 | sl.sll_addr[0], sl.sll_addr[1], sl.sll_addr[2], 95 | sl.sll_addr[3], sl.sll_addr[4], sl.sll_addr[5]); 96 | { 97 | int i; 98 | for(i = 0; i < iov_count; ++i) 99 | dump_packet(iov[i].iov_base, iov[i].iov_len); 100 | } 101 | #endif 102 | 103 | l = sendmsg(ufd.fd, &msg, 0); 104 | 105 | if(l < 0) 106 | { 107 | if(errno != EWOULDBLOCK) 108 | ERROR("send failed: %m"); 109 | } 110 | else if(l != len) 111 | ERROR("short write in sendto: %d instead of %d", l, len); 112 | } 113 | 114 | void packet_rcv(void) 115 | { 116 | unsigned char buf[2048]; 117 | struct sockaddr_ll sl; 118 | socklen_t salen = sizeof sl; 119 | int cc; 120 | 121 | while (1) { 122 | cc = recvfrom(ufd.fd, &buf, sizeof(buf), 0, (struct sockaddr *) &sl, &salen); 123 | if (cc < 0) { 124 | switch (errno) { 125 | case EINTR: 126 | continue; 127 | case EAGAIN: 128 | return; 129 | default: 130 | cc = 0; 131 | } 132 | } 133 | 134 | if (cc == 0) { 135 | ERROR("recvfrom failed: %m"); 136 | uloop_fd_delete(&ufd); 137 | return; 138 | } 139 | 140 | #ifdef PACKET_DEBUG 141 | printf("Receive Src ifindex %d %02x:%02x:%02x:%02x:%02x:%02x\n", 142 | sl.sll_ifindex, 143 | sl.sll_addr[0], sl.sll_addr[1], sl.sll_addr[2], 144 | sl.sll_addr[3], sl.sll_addr[4], sl.sll_addr[5]); 145 | 146 | dump_packet(buf, cc); 147 | #endif 148 | 149 | bridge_bpdu_rcv(sl.sll_ifindex, buf, cc); 150 | } 151 | } 152 | 153 | /* Berkeley Packet filter code to filter out spanning tree packets. 154 | from tcpdump -s 1152 -dd stp 155 | */ 156 | static struct sock_filter stp_filter[] = { 157 | { 0x28, 0, 0, 0x0000000c }, 158 | { 0x25, 3, 0, 0x000005dc }, 159 | { 0x30, 0, 0, 0x0000000e }, 160 | { 0x15, 0, 1, 0x00000042 }, 161 | { 0x6, 0, 0, 0x00000480 }, 162 | { 0x6, 0, 0, 0x00000000 }, 163 | }; 164 | 165 | static void 166 | packet_event(struct uloop_fd *fd, unsigned int events) 167 | { 168 | struct worker_event ev = { 169 | .type = WORKER_EV_RECV_PACKET, 170 | }; 171 | 172 | worker_queue_event(&ev); 173 | } 174 | 175 | /* 176 | * Open up a raw packet socket to catch all 802.2 packets. 177 | * and install a packet filter to only see STP (SAP 42) 178 | * 179 | * Since any bridged devices are already in promiscious mode 180 | * no need to add multicast address. 181 | */ 182 | int packet_sock_init(void) 183 | { 184 | struct sock_fprog prog = 185 | { 186 | .len = sizeof(stp_filter) / sizeof(stp_filter[0]), 187 | .filter = stp_filter, 188 | }; 189 | int s; 190 | 191 | s = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_802_2)); 192 | if(s < 0) 193 | { 194 | ERROR("socket failed: %m"); 195 | return -1; 196 | } 197 | 198 | if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog)) < 0) { 199 | ERROR("setsockopt packet filter failed: %m"); 200 | goto out; 201 | } 202 | 203 | if (fcntl(s, F_SETFL, O_NONBLOCK) < 0) { 204 | ERROR("fcntl set nonblock failed: %m"); 205 | goto out; 206 | } 207 | 208 | ufd.fd = s; 209 | ufd.cb = packet_event; 210 | uloop_fd_add(&ufd, ULOOP_READ | ULOOP_EDGE_TRIGGER); 211 | 212 | return 0; 213 | 214 | out: 215 | close(s); 216 | return -1; 217 | } 218 | -------------------------------------------------------------------------------- /ubus.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ustp - OpenWrt STP/RSTP/MSTP daemon 3 | * Copyright (C) 2021 Felix Fietkau 4 | * 5 | * This program is free software; you can redistribute it and/or modify 6 | * it under the terms of the GNU General Public License version 2 7 | * as published by the Free Software Foundation 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | */ 14 | #include 15 | #include 16 | #include "config.h" 17 | #include "mstp.h" 18 | #include "worker.h" 19 | #include "ubus.h" 20 | 21 | struct blob_buf b; 22 | 23 | enum bridge_config_attr { 24 | BRIDGE_CONFIG_NAME, 25 | BRIDGE_CONFIG_PROTO, 26 | BRIDGE_CONFIG_FWD_DELAY, 27 | BRIDGE_CONFIG_HELLO_TIME, 28 | BRIDGE_CONFIG_MAX_AGE, 29 | BRIDGE_CONFIG_AGEING_TIME, 30 | __BRIDGE_CONFIG_MAX 31 | }; 32 | 33 | static const struct blobmsg_policy bridge_config_policy[__BRIDGE_CONFIG_MAX] = { 34 | [BRIDGE_CONFIG_NAME] = { "name", BLOBMSG_TYPE_STRING }, 35 | [BRIDGE_CONFIG_PROTO] = { "proto", BLOBMSG_TYPE_STRING }, 36 | [BRIDGE_CONFIG_FWD_DELAY] = { "forward_delay", BLOBMSG_TYPE_INT32 }, 37 | [BRIDGE_CONFIG_HELLO_TIME] = { "hello_time", BLOBMSG_TYPE_INT32 }, 38 | [BRIDGE_CONFIG_MAX_AGE] = { "max_age", BLOBMSG_TYPE_INT32 }, 39 | [BRIDGE_CONFIG_AGEING_TIME] = { "ageing_time", BLOBMSG_TYPE_INT32 }, 40 | }; 41 | 42 | static bool 43 | ubus_set_bridge_config(struct blob_attr *attr) 44 | { 45 | struct blob_attr *tb[__BRIDGE_CONFIG_MAX], *cur; 46 | struct bridge_config *cfg; 47 | CIST_BridgeConfig *bc; 48 | 49 | blobmsg_parse(bridge_config_policy, __BRIDGE_CONFIG_MAX, tb, 50 | blobmsg_data(attr), blobmsg_len(attr)); 51 | 52 | cur = tb[BRIDGE_CONFIG_NAME]; 53 | if (!cur) 54 | return false; 55 | 56 | cfg = bridge_config_get(blobmsg_get_string(cur), true); 57 | 58 | bc = &cfg->config; 59 | bc->protocol_version = protoRSTP; 60 | bc->set_protocol_version = true; 61 | 62 | if ((cur = tb[BRIDGE_CONFIG_PROTO]) != NULL) { 63 | const char *proto = blobmsg_get_string(cur); 64 | 65 | if (!strcmp(proto, "mstp")) 66 | bc->protocol_version = protoMSTP; 67 | else if (!strcmp(proto, "stp")) 68 | bc->protocol_version = protoSTP; 69 | } 70 | 71 | if ((cur = tb[BRIDGE_CONFIG_FWD_DELAY]) != NULL) { 72 | bc->bridge_forward_delay = blobmsg_get_u32(cur); 73 | bc->set_bridge_forward_delay = true; 74 | } 75 | 76 | if ((cur = tb[BRIDGE_CONFIG_HELLO_TIME]) != NULL) { 77 | bc->bridge_hello_time = blobmsg_get_u32(cur); 78 | bc->set_bridge_hello_time = true; 79 | } 80 | 81 | if ((cur = tb[BRIDGE_CONFIG_AGEING_TIME]) != NULL) { 82 | bc->bridge_ageing_time = blobmsg_get_u32(cur); 83 | bc->set_bridge_ageing_time = true; 84 | } 85 | 86 | if ((cur = tb[BRIDGE_CONFIG_MAX_AGE]) != NULL) { 87 | bc->bridge_max_age = blobmsg_get_u32(cur); 88 | bc->set_bridge_max_age = true; 89 | } 90 | 91 | return true; 92 | } 93 | 94 | static int 95 | ubus_add_bridge(struct ubus_context *ctx, struct ubus_object *obj, 96 | struct ubus_request_data *req, const char *method, 97 | struct blob_attr *msg) 98 | { 99 | if (!ubus_set_bridge_config(msg)) 100 | return UBUS_STATUS_INVALID_ARGUMENT; 101 | 102 | return 0; 103 | } 104 | 105 | enum bridge_state_attr { 106 | BRIDGE_STATE_NAME, 107 | BRIDGE_STATE_ENABLED, 108 | __BRIDGE_STATE_MAX 109 | }; 110 | 111 | static const struct blobmsg_policy bridge_state_policy[__BRIDGE_STATE_MAX] = { 112 | [BRIDGE_STATE_NAME] = { "name", BLOBMSG_TYPE_STRING }, 113 | [BRIDGE_STATE_ENABLED] = { "enabled", BLOBMSG_TYPE_BOOL }, 114 | }; 115 | 116 | static int 117 | ubus_bridge_state(struct ubus_context *ctx, struct ubus_object *obj, 118 | struct ubus_request_data *req, const char *method, 119 | struct blob_attr *msg) 120 | { 121 | struct blob_attr *tb[__BRIDGE_STATE_MAX]; 122 | struct bridge_config *cfg; 123 | const char *bridge_name; 124 | struct worker_event ev = {}; 125 | 126 | blobmsg_parse(bridge_state_policy, __BRIDGE_STATE_MAX, tb, 127 | blobmsg_data(msg), blobmsg_len(msg)); 128 | 129 | if (!tb[BRIDGE_STATE_NAME] || !tb[BRIDGE_STATE_ENABLED]) 130 | return UBUS_STATUS_INVALID_ARGUMENT; 131 | 132 | bridge_name = blobmsg_get_string(tb[BRIDGE_STATE_NAME]); 133 | ev.bridge_idx = if_nametoindex(bridge_name); 134 | if (!ev.bridge_idx) 135 | return UBUS_STATUS_NOT_FOUND; 136 | 137 | if (blobmsg_get_bool(tb[BRIDGE_STATE_ENABLED])) { 138 | cfg = bridge_config_get(bridge_name, false); 139 | if (!cfg) 140 | return UBUS_STATUS_NOT_FOUND; 141 | 142 | ev.type = WORKER_EV_BRIDGE_ADD; 143 | ev.bridge_config = cfg->config; 144 | } else { 145 | ev.type = WORKER_EV_BRIDGE_REMOVE; 146 | } 147 | 148 | worker_queue_event(&ev); 149 | 150 | return 0; 151 | } 152 | 153 | static const struct ubus_method ustp_methods[] = { 154 | UBUS_METHOD("add_bridge", ubus_add_bridge, bridge_config_policy), 155 | UBUS_METHOD("bridge_state", ubus_bridge_state, bridge_state_policy), 156 | }; 157 | 158 | static struct ubus_object_type ustp_object_type = 159 | UBUS_OBJECT_TYPE("ustp", ustp_methods); 160 | 161 | static struct ubus_object ustp_object = { 162 | .name = "ustp", 163 | .type = &ustp_object_type, 164 | .methods = ustp_methods, 165 | .n_methods = ARRAY_SIZE(ustp_methods), 166 | }; 167 | 168 | static int 169 | netifd_device_cb(struct ubus_context *ctx, struct ubus_object *obj, 170 | struct ubus_request_data *req, const char *method, 171 | struct blob_attr *msg) 172 | { 173 | if (strcmp(method, "stp_init") != 0) 174 | return 0; 175 | 176 | ubus_set_bridge_config(msg); 177 | 178 | return 0; 179 | } 180 | 181 | static struct ubus_auto_conn conn; 182 | static struct ubus_subscriber netifd_sub; 183 | 184 | static void netifd_sub_cb(struct uloop_timeout *t) 185 | { 186 | uint32_t id; 187 | 188 | if (ubus_lookup_id(&conn.ctx, "network.device", &id) != 0 || 189 | ubus_subscribe(&conn.ctx, &netifd_sub, id) != 0) { 190 | uloop_timeout_set(t, 1000); 191 | return; 192 | } 193 | 194 | blob_buf_init(&b, 0); 195 | ubus_invoke(&conn.ctx, id, "stp_init", b.head, NULL, NULL, 1000); 196 | } 197 | 198 | static struct uloop_timeout netifd_sub_timer = { 199 | .cb = netifd_sub_cb, 200 | }; 201 | 202 | static void 203 | netifd_device_remove_cb(struct ubus_context *ctx, 204 | struct ubus_subscriber *obj, uint32_t id) 205 | { 206 | uloop_timeout_set(&netifd_sub_timer, 1000); 207 | } 208 | 209 | static struct ubus_subscriber netifd_sub = { 210 | .cb = netifd_device_cb, 211 | .remove_cb = netifd_device_remove_cb, 212 | }; 213 | 214 | static void 215 | ubus_connect_handler(struct ubus_context *ctx) 216 | { 217 | ubus_add_object(ctx, &ustp_object); 218 | ubus_register_subscriber(ctx, &netifd_sub); 219 | uloop_timeout_set(&netifd_sub_timer, 1); 220 | } 221 | 222 | void ustp_ubus_init(void) 223 | { 224 | conn.cb = ubus_connect_handler; 225 | ubus_auto_connect(&conn); 226 | } 227 | 228 | void ustp_ubus_exit(void) 229 | { 230 | uint32_t id; 231 | 232 | ubus_remove_object(&conn.ctx, &ustp_object); 233 | ubus_unregister_subscriber(&conn.ctx, &netifd_sub); 234 | blob_buf_init(&b, 0); 235 | if (ubus_lookup_id(&conn.ctx, "network.device", &id) == 0) 236 | ubus_invoke(&conn.ctx, id, "stp_init", b.head, NULL, NULL, 1000); 237 | ubus_auto_shutdown(&conn); 238 | } 239 | -------------------------------------------------------------------------------- /libnetlink.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libnetlink.c RTnetlink service routines. 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 6 | * as published by the Free Software Foundation; either version 7 | * 2 of the License, or (at your option) any later version. 8 | * 9 | * Authors: Alexey Kuznetsov, 10 | * 11 | */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #include "log.h" 27 | #include "libnetlink.h" 28 | 29 | #ifndef DEFAULT_RTNL_BUFSIZE 30 | #define DEFAULT_RTNL_BUFSIZE 4 * 1024 * 1024 31 | #endif 32 | 33 | #ifndef RTNL_SND_BUFSIZE 34 | #define RTNL_SND_BUFSIZE DEFAULT_RTNL_BUFSIZE 35 | #endif 36 | #ifndef RTNL_RCV_BUFSIZE 37 | #define RTNL_RCV_BUFSIZE DEFAULT_RTNL_BUFSIZE 38 | #endif 39 | 40 | void rtnl_close(struct rtnl_handle *rth) 41 | { 42 | close(rth->fd); 43 | } 44 | 45 | int rtnl_open_byproto(struct rtnl_handle *rth, unsigned subscriptions, 46 | int protocol) 47 | { 48 | socklen_t addr_len; 49 | int sndbuf = RTNL_SND_BUFSIZE; 50 | int rcvbuf = RTNL_RCV_BUFSIZE; 51 | int yes = 1; 52 | 53 | memset(rth, 0, sizeof(*rth)); 54 | 55 | rth->fd = socket(AF_NETLINK, SOCK_RAW, protocol); 56 | if (rth->fd < 0) { 57 | ERROR("Cannot open netlink socket"); 58 | return -1; 59 | } 60 | 61 | if (setsockopt(rth->fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) 62 | < 0) { 63 | ERROR("SO_SNDBUF"); 64 | return -1; 65 | } 66 | 67 | if (setsockopt(rth->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) 68 | < 0) { 69 | ERROR("SO_RCVBUF"); 70 | return -1; 71 | } 72 | 73 | if (setsockopt(rth->fd, SOL_NETLINK, NETLINK_NO_ENOBUFS, &yes, sizeof(yes)) < 0) 74 | ERROR("NETLINK_NO_EBUFS"); 75 | 76 | memset(&rth->local, 0, sizeof(rth->local)); 77 | rth->local.nl_family = AF_NETLINK; 78 | rth->local.nl_groups = subscriptions; 79 | 80 | if (bind(rth->fd, (struct sockaddr *)&rth->local, sizeof(rth->local)) < 81 | 0) { 82 | ERROR("Cannot bind netlink socket"); 83 | return -1; 84 | } 85 | addr_len = sizeof(rth->local); 86 | if (getsockname(rth->fd, (struct sockaddr *)&rth->local, &addr_len) < 0) { 87 | ERROR("Cannot getsockname"); 88 | return -1; 89 | } 90 | if (addr_len != sizeof(rth->local)) { 91 | ERROR("Wrong address length %d\n", addr_len); 92 | return -1; 93 | } 94 | if (rth->local.nl_family != AF_NETLINK) { 95 | ERROR("Wrong address family %d\n", 96 | rth->local.nl_family); 97 | return -1; 98 | } 99 | rth->seq = time(NULL); 100 | return 0; 101 | } 102 | 103 | int rtnl_open(struct rtnl_handle *rth, unsigned subscriptions) 104 | { 105 | return rtnl_open_byproto(rth, subscriptions, NETLINK_ROUTE); 106 | } 107 | 108 | int rtnl_wilddump_request(struct rtnl_handle *rth, int family, int type) 109 | { 110 | struct { 111 | struct nlmsghdr nlh; 112 | struct rtgenmsg g; 113 | } req; 114 | struct sockaddr_nl nladdr; 115 | 116 | memset(&nladdr, 0, sizeof(nladdr)); 117 | nladdr.nl_family = AF_NETLINK; 118 | 119 | memset(&req, 0, sizeof(req)); 120 | req.nlh.nlmsg_len = sizeof(req); 121 | req.nlh.nlmsg_type = type; 122 | req.nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; 123 | req.nlh.nlmsg_pid = 0; 124 | req.nlh.nlmsg_seq = rth->dump = ++rth->seq; 125 | req.g.rtgen_family = family; 126 | 127 | return sendto(rth->fd, (void *)&req, sizeof(req), 0, 128 | (struct sockaddr *)&nladdr, sizeof(nladdr)); 129 | } 130 | 131 | int rtnl_send(struct rtnl_handle *rth, const char *buf, int len) 132 | { 133 | struct sockaddr_nl nladdr; 134 | 135 | memset(&nladdr, 0, sizeof(nladdr)); 136 | nladdr.nl_family = AF_NETLINK; 137 | 138 | return sendto(rth->fd, buf, len, 0, (struct sockaddr *)&nladdr, 139 | sizeof(nladdr)); 140 | } 141 | 142 | int rtnl_dump_request(struct rtnl_handle *rth, int type, void *req, int len) 143 | { 144 | struct nlmsghdr nlh; 145 | struct sockaddr_nl nladdr; 146 | struct iovec iov[2] = { 147 | {.iov_base = &nlh,.iov_len = sizeof(nlh)} 148 | , 149 | {.iov_base = req,.iov_len = len} 150 | }; 151 | struct msghdr msg = { 152 | .msg_name = &nladdr, 153 | .msg_namelen = sizeof(nladdr), 154 | .msg_iov = iov, 155 | .msg_iovlen = 2, 156 | }; 157 | 158 | memset(&nladdr, 0, sizeof(nladdr)); 159 | nladdr.nl_family = AF_NETLINK; 160 | 161 | nlh.nlmsg_len = NLMSG_LENGTH(len); 162 | nlh.nlmsg_type = type; 163 | nlh.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; 164 | nlh.nlmsg_pid = 0; 165 | nlh.nlmsg_seq = rth->dump = ++rth->seq; 166 | 167 | return sendmsg(rth->fd, &msg, 0); 168 | } 169 | 170 | int rtnl_dump_filter(struct rtnl_handle *rth, 171 | rtnl_filter_t filter, 172 | void *arg1, rtnl_filter_t junk, void *arg2) 173 | { 174 | struct sockaddr_nl nladdr; 175 | struct iovec iov; 176 | struct msghdr msg = { 177 | .msg_name = &nladdr, 178 | .msg_namelen = sizeof(nladdr), 179 | .msg_iov = &iov, 180 | .msg_iovlen = 1, 181 | }; 182 | char buf[16384]; 183 | 184 | iov.iov_base = buf; 185 | while (1) { 186 | int status; 187 | struct nlmsghdr *h; 188 | 189 | iov.iov_len = sizeof(buf); 190 | status = recvmsg(rth->fd, &msg, 0); 191 | 192 | if (status < 0) { 193 | if (errno == EINTR) 194 | continue; 195 | ERROR("OVERRUN"); 196 | continue; 197 | } 198 | 199 | if (status == 0) { 200 | ERROR("EOF on netlink\n"); 201 | return -1; 202 | } 203 | 204 | h = (struct nlmsghdr *)buf; 205 | while (NLMSG_OK(h, status)) { 206 | int err; 207 | 208 | if (nladdr.nl_pid != 0 || 209 | h->nlmsg_pid != rth->local.nl_pid || 210 | h->nlmsg_seq != rth->dump) { 211 | if (junk) { 212 | err = junk(&nladdr, h, arg2); 213 | if (err < 0) 214 | return err; 215 | } 216 | goto skip_it; 217 | } 218 | 219 | if (h->nlmsg_type == NLMSG_DONE) 220 | return 0; 221 | if (h->nlmsg_type == NLMSG_ERROR) { 222 | struct nlmsgerr *err = 223 | (struct nlmsgerr *)NLMSG_DATA(h); 224 | if (h->nlmsg_len < 225 | NLMSG_LENGTH(sizeof(struct nlmsgerr))) { 226 | ERROR("ERROR truncated\n"); 227 | } else { 228 | errno = -err->error; 229 | LOG("RTNETLINK answers"); 230 | } 231 | return -1; 232 | } 233 | err = filter(&nladdr, h, arg1); 234 | if (err < 0) 235 | return err; 236 | 237 | skip_it: 238 | h = NLMSG_NEXT(h, status); 239 | } 240 | if (msg.msg_flags & MSG_TRUNC) { 241 | ERROR("Message truncated\n"); 242 | continue; 243 | } 244 | if (status) { 245 | ERROR("!!!Remnant of size %d\n", status); 246 | return -1; 247 | } 248 | } 249 | } 250 | 251 | int rtnl_talk(struct rtnl_handle *rtnl, struct nlmsghdr *n, pid_t peer, 252 | unsigned groups, struct nlmsghdr *answer, 253 | rtnl_filter_t junk, void *jarg) 254 | { 255 | int status; 256 | unsigned seq; 257 | struct nlmsghdr *h; 258 | struct sockaddr_nl nladdr; 259 | struct iovec iov = { 260 | .iov_base = (void *)n, 261 | .iov_len = n->nlmsg_len 262 | }; 263 | struct msghdr msg = { 264 | .msg_name = &nladdr, 265 | .msg_namelen = sizeof(nladdr), 266 | .msg_iov = &iov, 267 | .msg_iovlen = 1, 268 | }; 269 | char buf[16384]; 270 | 271 | memset(&nladdr, 0, sizeof(nladdr)); 272 | nladdr.nl_family = AF_NETLINK; 273 | nladdr.nl_pid = peer; 274 | nladdr.nl_groups = groups; 275 | 276 | n->nlmsg_seq = seq = ++rtnl->seq; 277 | 278 | if (answer == NULL) 279 | n->nlmsg_flags |= NLM_F_ACK; 280 | 281 | status = sendmsg(rtnl->fd, &msg, 0); 282 | 283 | if (status < 0) { 284 | ERROR("Cannot talk to rtnetlink"); 285 | return -1; 286 | } 287 | 288 | memset(buf, 0, sizeof(buf)); 289 | 290 | iov.iov_base = buf; 291 | 292 | while (1) { 293 | iov.iov_len = sizeof(buf); 294 | status = recvmsg(rtnl->fd, &msg, 0); 295 | 296 | if (status < 0) { 297 | if (errno == EINTR) 298 | continue; 299 | ERROR("OVERRUN"); 300 | continue; 301 | } 302 | if (status == 0) { 303 | ERROR("EOF on netlink\n"); 304 | return -1; 305 | } 306 | if (msg.msg_namelen != sizeof(nladdr)) { 307 | ERROR("sender address length == %d\n", 308 | msg.msg_namelen); 309 | return -1; 310 | } 311 | for (h = (struct nlmsghdr *)buf; status >= sizeof(*h);) { 312 | int err; 313 | int len = h->nlmsg_len; 314 | int l = len - sizeof(*h); 315 | 316 | if (l < 0 || len > status) { 317 | if (msg.msg_flags & MSG_TRUNC) { 318 | ERROR("Truncated message\n"); 319 | return -1; 320 | } 321 | ERROR( 322 | "!!!malformed message: len=%d\n", len); 323 | return -1; 324 | } 325 | 326 | if (nladdr.nl_pid != peer || 327 | h->nlmsg_pid != rtnl->local.nl_pid || 328 | h->nlmsg_seq != seq) { 329 | if (junk) { 330 | err = junk(&nladdr, h, jarg); 331 | if (err < 0) 332 | return err; 333 | } 334 | /* Don't forget to skip that message. */ 335 | status -= NLMSG_ALIGN(len); 336 | h = (struct nlmsghdr *)((char *)h + 337 | NLMSG_ALIGN(len)); 338 | continue; 339 | } 340 | 341 | if (h->nlmsg_type == NLMSG_ERROR) { 342 | struct nlmsgerr *err = 343 | (struct nlmsgerr *)NLMSG_DATA(h); 344 | if (l < sizeof(struct nlmsgerr)) { 345 | ERROR("ERROR truncated\n"); 346 | } else { 347 | errno = -err->error; 348 | if (errno == 0) { 349 | if (answer) 350 | memcpy(answer, h, 351 | h->nlmsg_len); 352 | return 0; 353 | } 354 | LOG("RTNETLINK answers"); 355 | } 356 | return -1; 357 | } 358 | if (answer) { 359 | memcpy(answer, h, h->nlmsg_len); 360 | return 0; 361 | } 362 | 363 | ERROR("Unexpected reply!!!\n"); 364 | 365 | status -= NLMSG_ALIGN(len); 366 | h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); 367 | } 368 | if (msg.msg_flags & MSG_TRUNC) { 369 | ERROR("Message truncated\n"); 370 | continue; 371 | } 372 | if (status) { 373 | ERROR("!!!Remnant of size %d\n", status); 374 | return -1; 375 | } 376 | } 377 | } 378 | 379 | int rtnl_listen(struct rtnl_handle *rtnl, rtnl_filter_t handler, void *jarg) 380 | { 381 | int status; 382 | struct nlmsghdr *h; 383 | struct sockaddr_nl nladdr; 384 | struct iovec iov; 385 | struct msghdr msg = { 386 | .msg_name = &nladdr, 387 | .msg_namelen = sizeof(nladdr), 388 | .msg_iov = &iov, 389 | .msg_iovlen = 1, 390 | }; 391 | char buf[8192]; 392 | 393 | memset(&nladdr, 0, sizeof(nladdr)); 394 | nladdr.nl_family = AF_NETLINK; 395 | nladdr.nl_pid = 0; 396 | nladdr.nl_groups = 0; 397 | 398 | iov.iov_base = buf; 399 | while (1) { 400 | iov.iov_len = sizeof(buf); 401 | status = recvmsg(rtnl->fd, &msg, 0); 402 | 403 | if (status < 0) { 404 | if (errno == EINTR) 405 | continue; 406 | if (errno == EAGAIN) 407 | return 0; 408 | ERROR("OVERRUN: recvmsg(): error %d : %s\n", errno, strerror(errno)); 409 | return -1; 410 | } 411 | if (status == 0) { 412 | ERROR("EOF on netlink\n"); 413 | return -1; 414 | } 415 | if (msg.msg_namelen != sizeof(nladdr)) { 416 | ERROR("Sender address length == %d\n", 417 | msg.msg_namelen); 418 | continue; 419 | } 420 | for (h = (struct nlmsghdr *)buf; status >= sizeof(*h);) { 421 | int err; 422 | int len = h->nlmsg_len; 423 | int l = len - sizeof(*h); 424 | 425 | if (l < 0 || len > status) { 426 | if (msg.msg_flags & MSG_TRUNC) { 427 | ERROR("Truncated message\n"); 428 | continue; 429 | } 430 | ERROR( 431 | "!!!malformed message: len=%d\n", len); 432 | continue; 433 | } 434 | 435 | err = handler(&nladdr, h, jarg); 436 | if (err < 0) { 437 | ERROR("Handler returned %d\n", err); 438 | continue; 439 | } 440 | 441 | status -= NLMSG_ALIGN(len); 442 | h = (struct nlmsghdr *)((char *)h + NLMSG_ALIGN(len)); 443 | } 444 | if (msg.msg_flags & MSG_TRUNC) { 445 | ERROR("Message truncated\n"); 446 | continue; 447 | } 448 | if (status) { 449 | ERROR("!!!Remnant of size %d\n", status); 450 | continue; 451 | } 452 | } 453 | } 454 | 455 | int rtnl_from_file(FILE * rtnl, rtnl_filter_t handler, void *jarg) 456 | { 457 | int status; 458 | struct sockaddr_nl nladdr; 459 | char buf[8192]; 460 | struct nlmsghdr *h = (void *)buf; 461 | 462 | memset(&nladdr, 0, sizeof(nladdr)); 463 | nladdr.nl_family = AF_NETLINK; 464 | nladdr.nl_pid = 0; 465 | nladdr.nl_groups = 0; 466 | 467 | while (1) { 468 | int err, len; 469 | int l; 470 | 471 | status = fread(&buf, 1, sizeof(*h), rtnl); 472 | 473 | if (status < 0) { 474 | if (errno == EINTR) 475 | continue; 476 | ERROR("rtnl_from_file: fread"); 477 | return -1; 478 | } 479 | if (status == 0) 480 | return 0; 481 | 482 | len = h->nlmsg_len; 483 | l = len - sizeof(*h); 484 | 485 | if (l < 0 || len > sizeof(buf)) { 486 | ERROR("!!!malformed message: len=%d @%lu\n", 487 | len, ftell(rtnl)); 488 | return -1; 489 | } 490 | 491 | status = fread(NLMSG_DATA(h), 1, NLMSG_ALIGN(l), rtnl); 492 | 493 | if (status < 0) { 494 | ERROR("rtnl_from_file: fread"); 495 | return -1; 496 | } 497 | if (status < l) { 498 | ERROR("rtnl-from_file: truncated message\n"); 499 | return -1; 500 | } 501 | 502 | err = handler(&nladdr, h, jarg); 503 | if (err < 0) 504 | return err; 505 | } 506 | } 507 | 508 | int addattr32(struct nlmsghdr *n, int maxlen, int type, __u32 data) 509 | { 510 | int len = RTA_LENGTH(4); 511 | struct rtattr *rta; 512 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { 513 | ERROR( 514 | "addattr32: Error! max allowed bound %d exceeded\n", 515 | maxlen); 516 | return -1; 517 | } 518 | rta = NLMSG_TAIL(n); 519 | rta->rta_type = type; 520 | rta->rta_len = len; 521 | memcpy(RTA_DATA(rta), &data, 4); 522 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 523 | return 0; 524 | } 525 | 526 | int addattr8(struct nlmsghdr *n, int maxlen, int type, __u8 data) 527 | { 528 | int len = RTA_LENGTH(1); 529 | struct rtattr *rta; 530 | if (NLMSG_ALIGN(n->nlmsg_len) + len > maxlen) { 531 | ERROR( 532 | "addattr8: Error! max allowed bound %d exceeded\n", 533 | maxlen); 534 | return -1; 535 | } 536 | rta = NLMSG_TAIL(n); 537 | rta->rta_type = type; 538 | rta->rta_len = len; 539 | memcpy(RTA_DATA(rta), &data, 1); 540 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + len; 541 | return 0; 542 | } 543 | 544 | int addattr_l(struct nlmsghdr *n, int maxlen, int type, const void *data, 545 | int alen) 546 | { 547 | int len = RTA_LENGTH(alen); 548 | struct rtattr *rta; 549 | 550 | if (NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len) > maxlen) { 551 | ERROR( 552 | "addattr_l ERROR: message exceeded bound of %d\n", 553 | maxlen); 554 | return -1; 555 | } 556 | rta = NLMSG_TAIL(n); 557 | rta->rta_type = type; 558 | rta->rta_len = len; 559 | memcpy(RTA_DATA(rta), data, alen); 560 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len); 561 | return 0; 562 | } 563 | 564 | int addraw_l(struct nlmsghdr *n, int maxlen, const void *data, int len) 565 | { 566 | if (NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len) > maxlen) { 567 | ERROR( 568 | "addraw_l ERROR: message exceeded bound of %d\n", 569 | maxlen); 570 | return -1; 571 | } 572 | 573 | memcpy(NLMSG_TAIL(n), data, len); 574 | memset((void *)NLMSG_TAIL(n) + len, 0, NLMSG_ALIGN(len) - len); 575 | n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + NLMSG_ALIGN(len); 576 | return 0; 577 | } 578 | 579 | int rta_addattr32(struct rtattr *rta, int maxlen, int type, __u32 data) 580 | { 581 | int len = RTA_LENGTH(4); 582 | struct rtattr *subrta; 583 | 584 | if (RTA_ALIGN(rta->rta_len) + len > maxlen) { 585 | ERROR( 586 | "rta_addattr32: Error! max allowed bound %d exceeded\n", 587 | maxlen); 588 | return -1; 589 | } 590 | subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); 591 | subrta->rta_type = type; 592 | subrta->rta_len = len; 593 | memcpy(RTA_DATA(subrta), &data, 4); 594 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + len; 595 | return 0; 596 | } 597 | 598 | int rta_addattr_l(struct rtattr *rta, int maxlen, int type, 599 | const void *data, int alen) 600 | { 601 | struct rtattr *subrta; 602 | int len = RTA_LENGTH(alen); 603 | 604 | if (RTA_ALIGN(rta->rta_len) + RTA_ALIGN(len) > maxlen) { 605 | ERROR( 606 | "rta_addattr_l: Error! max allowed bound %d exceeded\n", 607 | maxlen); 608 | return -1; 609 | } 610 | subrta = (struct rtattr *)(((char *)rta) + RTA_ALIGN(rta->rta_len)); 611 | subrta->rta_type = type; 612 | subrta->rta_len = len; 613 | memcpy(RTA_DATA(subrta), data, alen); 614 | rta->rta_len = NLMSG_ALIGN(rta->rta_len) + RTA_ALIGN(len); 615 | return 0; 616 | } 617 | 618 | int rta_addattr8(struct rtattr *rta, int maxlen, int type, __u8 data) 619 | { 620 | return rta_addattr_l(rta, maxlen, type, &data, sizeof(__u8)); 621 | } 622 | 623 | int rta_addattr16(struct rtattr *rta, int maxlen, int type, __u16 data) 624 | { 625 | return rta_addattr_l(rta, maxlen, type, &data, sizeof(__u16)); 626 | } 627 | 628 | int rta_addattr64(struct rtattr *rta, int maxlen, int type, __u64 data) 629 | { 630 | return rta_addattr_l(rta, maxlen, type, &data, sizeof(__u64)); 631 | } 632 | 633 | struct rtattr *rta_nest(struct rtattr *rta, int maxlen, int type) 634 | { 635 | struct rtattr *nest = RTA_TAIL(rta); 636 | 637 | rta_addattr_l(rta, maxlen, type, NULL, 0); 638 | nest->rta_type |= NLA_F_NESTED; 639 | 640 | return nest; 641 | } 642 | 643 | int rta_nest_end(struct rtattr *rta, struct rtattr *nest) 644 | { 645 | nest->rta_len = (void *)RTA_TAIL(rta) - (void *)nest; 646 | 647 | return rta->rta_len; 648 | } 649 | 650 | int parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) 651 | { 652 | memset(tb, 0, sizeof(struct rtattr *) * (max + 1)); 653 | while (RTA_OK(rta, len)) { 654 | if (rta->rta_type <= max) 655 | tb[rta->rta_type] = rta; 656 | rta = RTA_NEXT(rta, len); 657 | } 658 | if (len) 659 | ERROR("!!!Deficit %d, rta_len=%d\n", len, 660 | rta->rta_len); 661 | return 0; 662 | } 663 | 664 | int parse_rtattr_byindex(struct rtattr *tb[], int max, struct rtattr *rta, 665 | int len) 666 | { 667 | int i = 0; 668 | 669 | memset(tb, 0, sizeof(struct rtattr *) * max); 670 | while (RTA_OK(rta, len)) { 671 | if (rta->rta_type <= max && i < max) 672 | tb[i++] = rta; 673 | rta = RTA_NEXT(rta, len); 674 | } 675 | if (len) 676 | ERROR("!!!Deficit %d, rta_len=%d\n", len, 677 | rta->rta_len); 678 | return i; 679 | } 680 | -------------------------------------------------------------------------------- /bridge_track.c: -------------------------------------------------------------------------------- 1 | /***************************************************************************** 2 | Copyright (c) 2006 EMC Corporation. 3 | Copyright (c) 2011 Factor-SPE 4 | 5 | This program is free software; you can redistribute it and/or modify it 6 | under the terms of the GNU General Public License as published by the Free 7 | Software Foundation; either version 2 of the License, or (at your option) 8 | any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but WITHOUT 11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 | more details. 14 | 15 | You should have received a copy of the GNU General Public License along with 16 | this program; if not, write to the Free Software Foundation, Inc., 59 17 | Temple Place - Suite 330, Boston, MA 02111-1307, USA. 18 | 19 | The full GNU General Public License is included in this distribution in the 20 | file called LICENSE. 21 | 22 | Authors: Srinivas Aji 23 | Authors: Vitalii Demianets 24 | 25 | ******************************************************************************/ 26 | #define _GNU_SOURCE 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "bridge_ctl.h" 40 | #include "bridge_track.h" 41 | #include "netif_utils.h" 42 | #include "packet.h" 43 | #include "log.h" 44 | #include "mstp.h" 45 | #include "driver.h" 46 | #include "libnetlink.h" 47 | 48 | #ifndef SYSFS_CLASS_NET 49 | #define SYSFS_CLASS_NET "/sys/class/net" 50 | #endif 51 | 52 | static LIST_HEAD(bridges); 53 | 54 | static bridge_t * create_br(int if_index) 55 | { 56 | bridge_t *br; 57 | TST((br = calloc(1, sizeof(*br))) != NULL, NULL); 58 | 59 | /* Init system dependent info */ 60 | br->sysdeps.if_index = if_index; 61 | if (!if_indextoname(if_index, br->sysdeps.name)) 62 | goto err; 63 | if (get_hwaddr(br->sysdeps.name, br->sysdeps.macaddr)) 64 | goto err; 65 | 66 | INFO("Add bridge %s", br->sysdeps.name); 67 | if(!MSTP_IN_bridge_create(br, br->sysdeps.macaddr)) 68 | goto err; 69 | 70 | list_add_tail(&br->list, &bridges); 71 | return br; 72 | err: 73 | free(br); 74 | return NULL; 75 | } 76 | 77 | static bridge_t * find_br(int if_index) 78 | { 79 | bridge_t *br; 80 | list_for_each_entry(br, &bridges, list) 81 | { 82 | if(br->sysdeps.if_index == if_index) 83 | return br; 84 | } 85 | return NULL; 86 | } 87 | 88 | static port_t * create_if(bridge_t * br, int if_index) 89 | { 90 | port_t *prt; 91 | TST((prt = calloc(1, sizeof(*prt))) != NULL, NULL); 92 | 93 | /* Init system dependent info */ 94 | prt->sysdeps.if_index = if_index; 95 | if (!if_indextoname(if_index, prt->sysdeps.name)) 96 | goto err; 97 | if (get_hwaddr(prt->sysdeps.name, prt->sysdeps.macaddr)) 98 | goto err; 99 | 100 | int portno; 101 | if(0 > (portno = get_bridge_portno(prt->sysdeps.name))) 102 | { 103 | ERROR("Couldn't get port number for %s", prt->sysdeps.name); 104 | goto err; 105 | } 106 | if((0 == portno) || (portno > MAX_PORT_NUMBER)) 107 | { 108 | ERROR("Port number for %s is invalid (%d)", prt->sysdeps.name, portno); 109 | goto err; 110 | } 111 | 112 | INFO("Add iface %s as port#%d to bridge %s", prt->sysdeps.name, 113 | portno, br->sysdeps.name); 114 | prt->bridge = br; 115 | if(!MSTP_IN_port_create_and_add_tail(prt, portno)) 116 | goto err; 117 | 118 | return prt; 119 | err: 120 | free(prt); 121 | return NULL; 122 | } 123 | 124 | static port_t * find_if(bridge_t * br, int if_index) 125 | { 126 | port_t *prt; 127 | list_for_each_entry(prt, &br->ports, br_list) 128 | { 129 | if(prt->sysdeps.if_index == if_index) 130 | return prt; 131 | } 132 | return NULL; 133 | } 134 | 135 | static inline void delete_if(port_t *prt) 136 | { 137 | MSTP_IN_delete_port(prt); 138 | free(prt); 139 | } 140 | 141 | static inline bool delete_if_byindex(bridge_t * br, int if_index) 142 | { 143 | port_t *prt; 144 | if(!(prt = find_if(br, if_index))) 145 | return false; 146 | delete_if(prt); 147 | return true; 148 | } 149 | 150 | static bool delete_br_byindex(int if_index) 151 | { 152 | bridge_t *br; 153 | if(!(br = find_br(if_index))) 154 | return false; 155 | 156 | INFO("Delete bridge %s (%d)", br->sysdeps.name, if_index); 157 | 158 | list_del(&br->list); 159 | MSTP_IN_delete_bridge(br); 160 | free(br); 161 | return true; 162 | } 163 | 164 | void bridge_one_second(void) 165 | { 166 | bridge_t *br; 167 | list_for_each_entry(br, &bridges, list) 168 | MSTP_IN_one_second(br); 169 | } 170 | 171 | /* New MAC address is stored in addr, which also holds the old value on entry. 172 | Return true if the address changed */ 173 | static bool check_mac_address(char *name, __u8 *addr) 174 | { 175 | __u8 temp_addr[ETH_ALEN]; 176 | if(get_hwaddr(name, temp_addr)) 177 | { 178 | LOG("Error getting hw address: %s", name); 179 | /* Error. Ignore the new value */ 180 | return false; 181 | } 182 | if(memcmp(addr, temp_addr, sizeof(temp_addr)) == 0) 183 | return false; 184 | else 185 | { 186 | memcpy(addr, temp_addr, sizeof(temp_addr)); 187 | return true; 188 | } 189 | } 190 | 191 | static void set_br_up(bridge_t * br, bool up) 192 | { 193 | bool changed = false; 194 | 195 | if(up != br->sysdeps.up) 196 | { 197 | INFO("%s was %s. Set %s", br->sysdeps.name, 198 | br->sysdeps.up ? "up" : "down", up ? "up" : "down"); 199 | br->sysdeps.up = up; 200 | changed = true; 201 | } 202 | 203 | if(check_mac_address(br->sysdeps.name, br->sysdeps.macaddr)) 204 | { 205 | /* MAC address changed */ 206 | /* Notify bridge address change */ 207 | MSTP_IN_set_bridge_address(br, br->sysdeps.macaddr); 208 | } 209 | 210 | if(changed) 211 | MSTP_IN_set_bridge_enable(br, br->sysdeps.up); 212 | } 213 | 214 | static void set_if_up(port_t *prt, bool up) 215 | { 216 | INFO("Port %s : %s", prt->sysdeps.name, (up ? "up" : "down")); 217 | int speed = -1; 218 | int duplex = -1; 219 | bool changed = false; 220 | bool bpdu_filter; 221 | 222 | if(check_mac_address(prt->sysdeps.name, prt->sysdeps.macaddr)) 223 | { 224 | /* MAC address changed */ 225 | if(check_mac_address(prt->bridge->sysdeps.name, 226 | prt->bridge->sysdeps.macaddr)) 227 | { 228 | /* Notify bridge address change */ 229 | MSTP_IN_set_bridge_address(prt->bridge, 230 | prt->bridge->sysdeps.macaddr); 231 | } 232 | } 233 | 234 | if(!up) 235 | { /* Down */ 236 | if(prt->sysdeps.up) 237 | { 238 | prt->sysdeps.up = false; 239 | changed = true; 240 | } 241 | } 242 | else 243 | { /* Up */ 244 | int r = ethtool_get_speed_duplex(prt->sysdeps.name, &speed, &duplex); 245 | if((r < 0) || (speed < 0)) 246 | speed = 10; 247 | if((r < 0) || (duplex < 0)) 248 | duplex = 1; /* Assume full duplex */ 249 | 250 | if(speed != prt->sysdeps.speed) 251 | { 252 | prt->sysdeps.speed = speed; 253 | changed = true; 254 | } 255 | if(duplex != prt->sysdeps.duplex) 256 | { 257 | prt->sysdeps.duplex = duplex; 258 | changed = true; 259 | } 260 | if(!prt->sysdeps.up) 261 | { 262 | prt->sysdeps.up = true; 263 | changed = true; 264 | } 265 | 266 | bpdu_filter = get_bpdu_filter(prt->sysdeps.name); 267 | if (bpdu_filter != prt->bpduFilterPort) { 268 | CIST_PortConfig cfg = { 269 | .bpdu_filter_port = bpdu_filter, 270 | .set_bpdu_filter_port = true 271 | }; 272 | 273 | MSTP_IN_set_cist_port_config(prt, &cfg); 274 | } 275 | } 276 | if(changed) 277 | MSTP_IN_set_port_enable(prt, prt->sysdeps.up, prt->sysdeps.speed, 278 | prt->sysdeps.duplex); 279 | } 280 | 281 | /* br_index == if_index means: interface is bridge master */ 282 | int bridge_notify(int br_index, int if_index, bool newlink, unsigned flags) 283 | { 284 | port_t *prt; 285 | bridge_t *br = NULL, *other_br; 286 | bool up = !!(flags & IFF_UP); 287 | bool running = up && (flags & IFF_RUNNING); 288 | 289 | LOG("br_index %d, if_index %d, newlink %d, up %d, running %d", 290 | br_index, if_index, newlink, up, running); 291 | 292 | if((br_index >= 0) && (br_index != if_index)) 293 | { 294 | if(!(br = find_br(br_index))) 295 | return -2; /* bridge not in list */ 296 | int br_flags = get_flags(br->sysdeps.name); 297 | if(br_flags >= 0) 298 | set_br_up(br, !!(br_flags & IFF_UP)); 299 | } 300 | 301 | if(br) 302 | { 303 | if(!(prt = find_if(br, if_index))) 304 | { 305 | if(!newlink) 306 | { 307 | INFO("Got DELLINK for unknown port %d on " 308 | "bridge %d", if_index, br_index); 309 | return -1; 310 | } 311 | /* Check if this interface is slave of another bridge */ 312 | list_for_each_entry(other_br, &bridges, list) 313 | { 314 | if(other_br != br) 315 | if(delete_if_byindex(other_br, if_index)) 316 | { 317 | INFO("Device %d has come to bridge %d. " 318 | "Missed notify for deletion from bridge %d", 319 | if_index, br_index, other_br->sysdeps.if_index); 320 | break; 321 | } 322 | } 323 | prt = create_if(br, if_index); 324 | } 325 | if(!prt) 326 | { 327 | ERROR("Couldn't create data for interface %d (master %d)", 328 | if_index, br_index); 329 | return -1; 330 | } 331 | if(!newlink) 332 | { 333 | delete_if(prt); 334 | return 0; 335 | } 336 | set_if_up(prt, running); /* And speed and duplex */ 337 | } 338 | else 339 | { /* Interface is not a bridge slave */ 340 | if(!newlink) 341 | { 342 | /* DELLINK not from bridge means interface unregistered. */ 343 | /* Cleanup removed bridge or removed bridge slave */ 344 | if(!delete_br_byindex(if_index)) 345 | list_for_each_entry(br, &bridges, list) 346 | { 347 | if(delete_if_byindex(br, if_index)) 348 | break; 349 | } 350 | return 0; 351 | } 352 | else 353 | { /* This may be a new link */ 354 | if(br_index == if_index) 355 | { 356 | if(!(br = find_br(br_index))) 357 | return -2; /* bridge not in list */ 358 | set_br_up(br, up); 359 | } 360 | } 361 | } 362 | return 0; 363 | } 364 | 365 | struct llc_header 366 | { 367 | __u8 dest_addr[ETH_ALEN]; 368 | __u8 src_addr[ETH_ALEN]; 369 | __be16 len8023; 370 | __u8 d_sap; 371 | __u8 s_sap; 372 | __u8 llc_ctrl; 373 | } __attribute__((packed)); 374 | 375 | /* LLC_PDU_xxx defines snitched from linux/net/llc_pdu.h */ 376 | #define LLC_PDU_LEN_U 3 /* header and 1 control byte */ 377 | #define LLC_PDU_TYPE_U 3 /* first two bits */ 378 | 379 | /* 7.12.3 of 802.1D */ 380 | #define LLC_SAP_BSPAN 0x42 381 | static const __u8 bridge_group_address[ETH_ALEN] = 382 | { 383 | 0x01, 0x80, 0xc2, 0x00, 0x00, 0x00 384 | }; 385 | 386 | void bridge_bpdu_rcv(int if_index, const unsigned char *data, int len) 387 | { 388 | port_t *prt = NULL; 389 | bridge_t *br; 390 | 391 | LOG("ifindex %d, len %d", if_index, len); 392 | 393 | list_for_each_entry(br, &bridges, list) 394 | { 395 | if((prt = find_if(br, if_index))) 396 | break; 397 | } 398 | if(!prt) 399 | return; 400 | 401 | /* sanity checks */ 402 | TSTM(br == prt->bridge,, "Bridge mismatch. This bridge is '%s' but port " 403 | "'%s' belongs to bridge '%s'", br->sysdeps.name, prt->bridge->sysdeps.name); 404 | TSTM(prt->sysdeps.up,, "Port '%s' should be up", prt->sysdeps.name); 405 | 406 | /* Validate Ethernet and LLC header, 407 | * maybe we can skip this check thanks to Berkeley filter in packet socket? 408 | */ 409 | struct llc_header *h; 410 | unsigned int l; 411 | TST(len > sizeof(struct llc_header),); 412 | h = (struct llc_header *)data; 413 | TST(0 == memcmp(h->dest_addr, bridge_group_address, ETH_ALEN), 414 | INFO("ifindex %d, len %d, %02hhX%02hhX%02hhX%02hhX%02hhX%02hhX", 415 | if_index, len, 416 | h->dest_addr[0], h->dest_addr[1], h->dest_addr[2], 417 | h->dest_addr[3], h->dest_addr[4], h->dest_addr[5]) 418 | ); 419 | l = __be16_to_cpu(h->len8023); 420 | TST(l <= ETH_DATA_LEN && l <= len - ETH_HLEN && l >= LLC_PDU_LEN_U, ); 421 | TST(h->d_sap == LLC_SAP_BSPAN && h->s_sap == LLC_SAP_BSPAN && (h->llc_ctrl & 0x3) == LLC_PDU_TYPE_U,); 422 | 423 | MSTP_IN_rx_bpdu(prt, 424 | /* Don't include LLC header */ 425 | (bpdu_t *)(data + sizeof(*h)), l - LLC_PDU_LEN_U); 426 | } 427 | 428 | static int br_set_state(struct rtnl_handle *rth, unsigned ifindex, __u8 state) 429 | { 430 | struct 431 | { 432 | struct nlmsghdr n; 433 | struct ifinfomsg ifi; 434 | char buf[256]; 435 | } req; 436 | 437 | memset(&req, 0, sizeof(req)); 438 | 439 | req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); 440 | req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE; 441 | req.n.nlmsg_type = RTM_SETLINK; 442 | req.ifi.ifi_family = AF_BRIDGE; 443 | req.ifi.ifi_index = ifindex; 444 | 445 | addattr8(&req.n, sizeof(req.buf), IFLA_PROTINFO, state); 446 | 447 | return rtnl_talk(rth, &req.n, 0, 0, NULL, NULL, NULL); 448 | } 449 | 450 | static int br_flush_port(char *ifname) 451 | { 452 | char fname[128]; 453 | snprintf(fname, sizeof(fname), SYSFS_CLASS_NET "/%s/brport/flush", ifname); 454 | int fd = open(fname, O_WRONLY); 455 | TSTM(0 <= fd, -1, "Couldn't open flush file %s for write: %m", fname); 456 | int write_result = write(fd, "1", 1); 457 | close(fd); 458 | TST(1 == write_result, -1); 459 | return 0; 460 | } 461 | 462 | static int br_set_ageing_time(char *brname, unsigned int ageing_time) 463 | { 464 | char fname[128], str_time[32]; 465 | snprintf(fname, sizeof(fname), SYSFS_CLASS_NET "/%s/bridge/ageing_time", 466 | brname); 467 | int fd = open(fname, O_WRONLY); 468 | TSTM(0 <= fd, -1, "Couldn't open file %s for write: %m", fname); 469 | int len = sprintf(str_time, "%u", ageing_time * HZ); 470 | int write_result = write(fd, str_time, len); 471 | close(fd); 472 | TST(len == write_result, -1); 473 | return 0; 474 | } 475 | 476 | /* External actions for MSTP protocol */ 477 | 478 | void MSTP_OUT_set_state(per_tree_port_t *ptp, int new_state) 479 | { 480 | char * state_name; 481 | port_t *prt = ptp->port; 482 | bridge_t *br = prt->bridge; 483 | 484 | if(ptp->state == new_state) 485 | return; 486 | ptp->state = driver_set_new_state(ptp, new_state); 487 | 488 | switch(ptp->state) 489 | { 490 | case BR_STATE_LISTENING: 491 | state_name = "listening"; 492 | break; 493 | case BR_STATE_LEARNING: 494 | state_name = "learning"; 495 | break; 496 | case BR_STATE_FORWARDING: 497 | state_name = "forwarding"; 498 | ++(prt->num_trans_fwd); 499 | break; 500 | case BR_STATE_BLOCKING: 501 | state_name = "blocking"; 502 | ++(prt->num_trans_blk); 503 | break; 504 | default: 505 | case BR_STATE_DISABLED: 506 | state_name = "disabled"; 507 | break; 508 | } 509 | INFO_MSTINAME(br, prt, ptp, "entering %s state", state_name); 510 | 511 | /* Translate new CIST state to the kernel bridge code */ 512 | if(0 == ptp->MSTID) 513 | { /* CIST */ 514 | if(0 > br_set_state(&rth_state, prt->sysdeps.if_index, ptp->state)) 515 | INFO_PRTNAME(br, prt, "Couldn't set kernel bridge state %s", 516 | state_name); 517 | } 518 | } 519 | 520 | /* This function initiates process of flushing 521 | * all entries for the given port in all FIDs for the 522 | * given tree. 523 | * When this process finishes, implementation should signal 524 | * this by calling MSTP_IN_all_fids_flushed(per_tree_port_t *ptp) 525 | */ 526 | void MSTP_OUT_flush_all_fids(per_tree_port_t * ptp) 527 | { 528 | port_t *prt = ptp->port; 529 | bridge_t *br = prt->bridge; 530 | 531 | /* Translate CIST flushing to the kernel bridge code */ 532 | if(0 == ptp->MSTID) 533 | { /* CIST */ 534 | if(0 > br_flush_port(prt->sysdeps.name)) 535 | ERROR_PRTNAME(br, prt, 536 | "Couldn't flush kernel bridge forwarding database"); 537 | } 538 | /* Completion signal MSTP_IN_all_fids_flushed will be called by driver */ 539 | INFO_MSTINAME(br, prt, ptp, "Flushing forwarding database"); 540 | driver_flush_all_fids(ptp); 541 | } 542 | 543 | void MSTP_OUT_set_ageing_time(port_t *prt, unsigned int ageingTime) 544 | { 545 | unsigned int actual_ageing_time; 546 | bridge_t *br = prt->bridge; 547 | 548 | actual_ageing_time = driver_set_ageing_time(prt, ageingTime); 549 | INFO_PRTNAME(br, prt, "Setting new ageing time to %u", actual_ageing_time); 550 | 551 | /* 552 | * Translate new ageing time to the kernel bridge code. 553 | * Kernel bridging code does not support per-port ageing time, 554 | * so set ageing time for the whole bridge. 555 | */ 556 | if(0 > br_set_ageing_time(br->sysdeps.name, actual_ageing_time)) 557 | ERROR_BRNAME(br, "Couldn't set new ageing time in kernel bridge"); 558 | } 559 | 560 | void MSTP_OUT_tx_bpdu(port_t *prt, bpdu_t * bpdu, int size) 561 | { 562 | char *bpdu_type, *tcflag; 563 | bridge_t *br = prt->bridge; 564 | 565 | switch(bpdu->protocolVersion) 566 | { 567 | case protoSTP: 568 | switch(bpdu->bpduType) 569 | { 570 | case bpduTypeConfig: 571 | bpdu_type = "STP-Config"; 572 | break; 573 | case bpduTypeTCN: 574 | bpdu_type = "STP-TCN"; 575 | break; 576 | default: 577 | bpdu_type = "STP-UnknownType"; 578 | } 579 | break; 580 | case protoRSTP: 581 | bpdu_type = "RST"; 582 | break; 583 | case protoMSTP: 584 | bpdu_type = "MST"; 585 | break; 586 | default: 587 | bpdu_type = "UnknownProto"; 588 | } 589 | 590 | ++(prt->num_tx_bpdu); 591 | if((protoSTP == bpdu->protocolVersion) && (bpduTypeTCN == bpdu->bpduType)) 592 | { 593 | ++(prt->num_tx_tcn); 594 | LOG_PRTNAME(br, prt, "sending %s BPDU", bpdu_type); 595 | } 596 | else 597 | { 598 | tcflag = ""; 599 | if(bpdu->flags & (1 << offsetTc)) 600 | { 601 | ++(prt->num_tx_tcn); 602 | tcflag = ", tcFlag"; 603 | } 604 | LOG_PRTNAME(br, prt, "sending %s BPDU%s", bpdu_type, tcflag); 605 | } 606 | 607 | struct llc_header h; 608 | memcpy(h.dest_addr, bridge_group_address, ETH_ALEN); 609 | memcpy(h.src_addr, prt->sysdeps.macaddr, ETH_ALEN); 610 | h.len8023 = __cpu_to_be16(size + LLC_PDU_LEN_U); 611 | h.d_sap = h.s_sap = LLC_SAP_BSPAN; 612 | h.llc_ctrl = LLC_PDU_TYPE_U; 613 | 614 | struct iovec iov[2] = 615 | { 616 | { .iov_base = &h, .iov_len = sizeof(h) }, 617 | { .iov_base = bpdu, .iov_len = size } 618 | }; 619 | 620 | packet_send(prt->sysdeps.if_index, iov, 2, sizeof(h) + size); 621 | } 622 | 623 | void MSTP_OUT_shutdown_port(port_t *prt) 624 | { 625 | if(0 > if_shutdown(prt->sysdeps.name)) 626 | ERROR_PRTNAME(prt->bridge, prt, "Couldn't shutdown port"); 627 | } 628 | 629 | static int not_dot_dotdot(const struct dirent *entry) 630 | { 631 | const char *n = entry->d_name; 632 | 633 | return strcmp(n, ".") || strcmp(n, ".."); 634 | } 635 | 636 | static int get_port_list(const char *br_ifname, struct dirent ***namelist) 637 | { 638 | char buf[256]; 639 | 640 | snprintf(buf, sizeof(buf), SYSFS_CLASS_NET "/%.230s/brif", br_ifname); 641 | 642 | return scandir(buf, namelist, not_dot_dotdot, versionsort); 643 | } 644 | 645 | int bridge_create(int bridge_idx, CIST_BridgeConfig *cfg) 646 | { 647 | struct dirent **namelist; 648 | int *port_list, n_ports; 649 | bridge_t *br, *other_br; 650 | port_t *port, *tmp; 651 | int flags; 652 | bool found; 653 | int i; 654 | 655 | br = find_br(bridge_idx); 656 | if (!br) 657 | br = create_br(bridge_idx); 658 | if (!br) 659 | return -1; 660 | 661 | MSTP_IN_set_cist_bridge_config(br, cfg); 662 | 663 | flags = get_flags(br->sysdeps.name); 664 | if (flags >= 0) 665 | set_br_up(br, !!(flags & IFF_UP)); 666 | 667 | n_ports = get_port_list(br->sysdeps.name, &namelist); 668 | port_list = alloca(n_ports * sizeof(*port_list)); 669 | 670 | for (i = 0; i < n_ports; i++) { 671 | port_list[i] = if_nametoindex(namelist[i]->d_name); 672 | free(namelist[i]); 673 | } 674 | free(namelist); 675 | 676 | list_for_each_entry_safe(port, tmp, &br->ports, br_list) { 677 | found = false; 678 | for (i = 0; i < n_ports; i++) { 679 | if (port->sysdeps.if_index != port_list[i]) 680 | continue; 681 | found = true; 682 | break; 683 | } 684 | 685 | if (found) 686 | continue; 687 | 688 | delete_if(port); 689 | } 690 | 691 | for (i = 0; i < n_ports; i++) { 692 | port = find_if(br, port_list[i]); 693 | if (port) 694 | continue; 695 | 696 | list_for_each_entry(other_br, &bridges, list) { 697 | if (br == other_br) 698 | continue; 699 | 700 | delete_if_byindex(other_br, port_list[i]); 701 | } 702 | 703 | port = find_if(br, port_list[i]); 704 | if (!port) 705 | port = create_if(br, port_list[i]); 706 | if (!port) 707 | continue; 708 | 709 | flags = get_flags(port->sysdeps.name); 710 | if (flags < 0) 711 | continue; 712 | 713 | set_if_up(port, !(~flags & (IFF_UP | IFF_RUNNING))); 714 | } 715 | 716 | return 0; 717 | } 718 | 719 | void bridge_delete(int index) 720 | { 721 | delete_br_byindex(index); 722 | } 723 | 724 | int bridge_track_fini(void) 725 | { 726 | INFO("Stopping all bridges"); 727 | bridge_t *br; 728 | list_for_each_entry(br, &bridges, list) 729 | { 730 | set_br_up(br, false); 731 | } 732 | return 0; 733 | } 734 | -------------------------------------------------------------------------------- /mstp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * mstp.h State machines from IEEE 802.1Q-2005 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 6 | * as published by the Free Software Foundation; either version 7 | * 2 of the License, or (at your option) any later version. 8 | * 9 | * Authors: Vitalii Demianets 10 | */ 11 | 12 | #ifndef MSTP_H 13 | #define MSTP_H 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | #include "bridge_ctl.h" 20 | 21 | /* #define HMAC_MDS_TEST_FUNCTIONS */ 22 | 23 | /* Useful macro for counting number of elements in array */ 24 | #define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x]))))) 25 | 26 | /* 27 | * assign() and cmp() macros that also do strict type-checking. See the 28 | * "unnecessary" pointer comparison. 29 | * NOTE: potential double-evaluation of the first argument in assign macro! 30 | * It is the price for type-safety ;) 31 | */ 32 | #define assign(x, y) ({ \ 33 | typeof(x) _assign1 = (x); \ 34 | typeof(y) _assign2 = (y); \ 35 | (void)(&_assign1 == &_assign2); \ 36 | (x) = _assign2; }) 37 | #define _ncmp(x, y) ({ \ 38 | typeof(x) _cmp1 = (x); \ 39 | typeof(y) _cmp2 = (y); \ 40 | (void)(&_cmp1 == &_cmp2); \ 41 | memcmp(&_cmp1, &_cmp2, sizeof(_cmp1)); }) 42 | #define cmp(x, _op, y) (_ncmp((x), (y)) _op 0) 43 | 44 | /* 13.7, Table 13-1 */ 45 | #define HMAC_KEY {0x13, 0xAC, 0x06, 0xA6, 0x2E, 0x47, 0xFD, 0x51, \ 46 | 0xF9, 0x5D, 0x2B, 0xA2, 0x43, 0xCD, 0x03, 0x46} 47 | extern void hmac_md5(unsigned char * text, int text_len, unsigned char * key, 48 | int key_len, caddr_t digest); 49 | #ifdef HMAC_MDS_TEST_FUNCTIONS 50 | extern bool MD5TestSuite(void); 51 | #endif /* HMAC_MDS_TEST_FUNCTIONS */ 52 | 53 | #define MAX_PORT_NUMBER 4095 54 | #define MAX_VID 4094 55 | #define MAX_FID 4095 56 | #define MAX_MSTID 4094 57 | 58 | /* MAX_xxx_MSTIS: CIST not counted */ 59 | #define MAX_STANDARD_MSTIS 64 60 | #define MAX_IMPLEMENTATION_MSTIS 63 61 | 62 | /* 13.37.1 */ 63 | #define MAX_PATH_COST 200000000u 64 | 65 | typedef union 66 | { 67 | __u64 u; 68 | struct 69 | { 70 | __be16 priority; 71 | __u8 mac_address[ETH_ALEN]; 72 | } __attribute__((packed)) s; 73 | } bridge_identifier_t; 74 | 75 | typedef __be16 port_identifier_t; 76 | 77 | /* These macros work well for both PortID and BridgeID */ 78 | #define GET_PRIORITY_FROM_IDENTIFIER(id) (((__u8 *)(&(id)))[0] & 0xF0) 79 | #define SET_PRIORITY_IN_IDENTIFIER(pri, id) do{ \ 80 | __u8 *first_octet = (__u8 *)(&(id)); \ 81 | *first_octet &= 0x0F; \ 82 | *first_octet |= (pri) & 0xF0; \ 83 | }while(0) 84 | 85 | #define CONFIGURATION_NAME_LEN 32 86 | #define CONFIGURATION_DIGEST_LEN 16 87 | typedef union 88 | { 89 | __u8 a[1 + CONFIGURATION_NAME_LEN + 2 + CONFIGURATION_DIGEST_LEN]; 90 | struct 91 | { 92 | __u8 selector; /* always 0 */ 93 | __u8 configuration_name[CONFIGURATION_NAME_LEN]; 94 | __be16 revision_level; 95 | __u8 configuration_digest[CONFIGURATION_DIGEST_LEN]; 96 | } __attribute__((packed)) s; 97 | } __attribute__((packed)) mst_configuration_identifier_t; 98 | 99 | typedef struct 100 | { 101 | bridge_identifier_t RRootID; 102 | __be32 IntRootPathCost; 103 | bridge_identifier_t DesignatedBridgeID; 104 | port_identifier_t DesignatedPortID; 105 | /* not used for MSTIs, only for CIST */ 106 | bridge_identifier_t RootID; 107 | __be32 ExtRootPathCost; 108 | } port_priority_vector_t; 109 | 110 | typedef struct 111 | { 112 | __u8 remainingHops; 113 | /* not used for MSTIs, only for CIST */ 114 | __u8 Forward_Delay; 115 | __u8 Max_Age; 116 | __u8 Message_Age; 117 | __u8 Hello_Time; 118 | } times_t; 119 | 120 | typedef struct 121 | { 122 | /* see bpduFlagOffset_t enum for offsets of flag bits */ 123 | __u8 flags; 124 | bridge_identifier_t mstiRRootID; 125 | __be32 mstiIntRootPathCost; 126 | /* only bits 7..4, bits 3..0 are zero on Tx and ignored on Rx */ 127 | __u8 bridgeIdentifierPriority; 128 | /* only bits 7..4, bits 3..0 are zero on Tx and ignored on Rx */ 129 | __u8 portIdentifierPriority; 130 | __u8 remainingHops; 131 | } __attribute__((packed)) msti_configuration_message_t; 132 | 133 | typedef struct 134 | { 135 | /* always zero for the Spanning Tree BPDUs */ 136 | __be16 protocolIdentifier; 137 | /* protoSTP for the Config and TCN 138 | * protoRSTP for the RST 139 | * protoMSTP for the MST 140 | * (see protocol_version_t enum) */ 141 | __u8 protocolVersion; 142 | /* values are defined in bpduType_t enum */ 143 | __u8 bpduType; 144 | /* TCN BPDU ends here */ 145 | /* see bpduFlagOffset_t enum for offsets of flag bits */ 146 | __u8 flags; 147 | bridge_identifier_t cistRootID; 148 | __be32 cistExtRootPathCost; 149 | bridge_identifier_t cistRRootID; 150 | port_identifier_t cistPortID; 151 | __u8 MessageAge[2]; 152 | __u8 MaxAge[2]; 153 | __u8 HelloTime[2]; 154 | __u8 ForwardDelay[2]; 155 | /* Config BPDU ends here */ 156 | __u8 version1_len; /* always zero */ 157 | /* RST BPDU ends here */ 158 | __be16 version3_len; 159 | mst_configuration_identifier_t mstConfigurationIdentifier; 160 | __be32 cistIntRootPathCost; 161 | bridge_identifier_t cistBridgeID; 162 | __u8 cistRemainingHops; 163 | msti_configuration_message_t mstConfiguration[MAX_STANDARD_MSTIS]; 164 | } __attribute__((packed)) bpdu_t; 165 | 166 | #define TCN_BPDU_SIZE offsetof(bpdu_t, flags) 167 | #define CONFIG_BPDU_SIZE offsetof(bpdu_t, version1_len) 168 | #define RST_BPDU_SIZE offsetof(bpdu_t, version3_len) 169 | #define MST_BPDU_SIZE_WO_MSTI_MSGS offsetof(bpdu_t, mstConfiguration) 170 | #define MST_BPDU_VER3LEN_WO_MSTI_MSGS (MST_BPDU_SIZE_WO_MSTI_MSGS \ 171 | - offsetof(bpdu_t, mstConfigurationIdentifier)) 172 | 173 | typedef enum 174 | { 175 | OtherInfo, 176 | SuperiorDesignatedInfo, 177 | RepeatedDesignatedInfo, 178 | InferiorDesignatedInfo, 179 | InferiorRootAlternateInfo 180 | } port_info_t; 181 | 182 | typedef enum 183 | { 184 | ioDisabled, 185 | ioMine, 186 | ioAged, 187 | ioReceived 188 | } port_info_origin_t; 189 | 190 | typedef enum 191 | { 192 | roleDisabled, 193 | roleRoot, 194 | roleDesignated, 195 | roleAlternate, 196 | roleBackup, 197 | roleMaster 198 | } port_role_t; 199 | 200 | typedef enum 201 | { 202 | encodedRoleMaster = 0, 203 | encodedRoleAlternateBackup = 1, 204 | encodedRoleRoot = 2, 205 | encodedRoleDesignated = 3 206 | } port_encoded_role_t; 207 | 208 | typedef enum 209 | { 210 | protoSTP = 0, 211 | protoRSTP = 2, 212 | protoMSTP = 3 213 | } protocol_version_t; 214 | 215 | typedef enum 216 | { 217 | bpduTypeConfig = 0, 218 | bpduTypeRST = 2, 219 | bpduTypeTCN = 128 220 | } bpduType_t; 221 | 222 | typedef enum 223 | { 224 | offsetTc = 0, 225 | offsetProposal = 1, 226 | offsetRole = 2, /* actually, role is coded in two-bit field */ 227 | offsetRole1 = 3, /* second bit of two-bit role field */ 228 | offsetLearnig = 4, 229 | offsetForwarding = 5, 230 | offsetAgreement = 6, 231 | offsetTcAck = 7 232 | /* in MSTI Configuration Message flags bit7 is used for Master flag */ 233 | #define offsetMaster offsetTcAck 234 | } bpduFlagOffset_t; 235 | 236 | #define BPDU_FLAGS_ROLE_SET(role) (((role) & 3) << offsetRole) 237 | #define BPDU_FLAGS_ROLE_GET(flags) (((flags) >> offsetRole) & 3) 238 | 239 | typedef enum 240 | { 241 | p2pAuto, 242 | p2pForceTrue, 243 | p2pForceFalse 244 | } 245 | admin_p2p_t; 246 | 247 | /* 13.28 Port Receive state machine */ 248 | typedef enum 249 | { 250 | PRSM_DISCARD, 251 | PRSM_RECEIVE 252 | } PRSM_states_t; 253 | 254 | /* 13.29 Port Protocol Migration state machine */ 255 | typedef enum 256 | { 257 | PPMSM_CHECKING_RSTP, 258 | PPMSM_SELECTING_STP, 259 | PPMSM_SENSING 260 | } PPMSM_states_t; 261 | 262 | /* 13.30 Bridge Detection state machine */ 263 | typedef enum 264 | { 265 | BDSM_EDGE, 266 | BDSM_NOT_EDGE 267 | } BDSM_states_t; 268 | 269 | /* 13.31 Port Transmit state machine */ 270 | typedef enum 271 | { 272 | PTSM_TRANSMIT_INIT, 273 | PTSM_TRANSMIT_CONFIG, 274 | PTSM_TRANSMIT_TCN, 275 | PTSM_TRANSMIT_RSTP, 276 | PTSM_TRANSMIT_PERIODIC, 277 | PTSM_IDLE 278 | } PTSM_states_t; 279 | 280 | /* 13.32 Port Information state machine */ 281 | /* #define PISM_ENABLE_LOG */ 282 | typedef enum 283 | { 284 | PISM_DISABLED, 285 | PISM_AGED, 286 | PISM_UPDATE, 287 | PISM_SUPERIOR_DESIGNATED, 288 | PISM_REPEATED_DESIGNATED, 289 | PISM_INFERIOR_DESIGNATED, 290 | PISM_NOT_DESIGNATED, 291 | PISM_OTHER, 292 | PISM_CURRENT, 293 | PISM_RECEIVE 294 | } PISM_states_t; 295 | 296 | /* 13.33 Port Role Selection state machine */ 297 | typedef enum 298 | { 299 | PRSSM_INIT_TREE, 300 | PRSSM_ROLE_SELECTION 301 | } PRSSM_states_t; 302 | 303 | /* 13.34 Port Role Transitions state machine */ 304 | /* #define PRTSM_ENABLE_LOG */ 305 | typedef enum 306 | { 307 | /* Disabled Port role transitions */ 308 | PRTSM_INIT_PORT, 309 | PRTSM_DISABLE_PORT, 310 | PRTSM_DISABLED_PORT, 311 | /* MasterPort role transitions */ 312 | PRTSM_MASTER_PROPOSED, 313 | PRTSM_MASTER_AGREED, 314 | PRTSM_MASTER_SYNCED, 315 | PRTSM_MASTER_RETIRED, 316 | PRTSM_MASTER_FORWARD, 317 | PRTSM_MASTER_LEARN, 318 | PRTSM_MASTER_DISCARD, 319 | PRTSM_MASTER_PORT, 320 | /* RootPort role transitions */ 321 | PRTSM_ROOT_PROPOSED, 322 | PRTSM_ROOT_AGREED, 323 | PRTSM_ROOT_SYNCED, 324 | PRTSM_REROOT, 325 | PRTSM_ROOT_FORWARD, 326 | PRTSM_ROOT_LEARN, 327 | PRTSM_REROOTED, 328 | PRTSM_ROOT_PORT, 329 | /* DesignatedPort role transitions */ 330 | PRTSM_DESIGNATED_PROPOSE, 331 | PRTSM_DESIGNATED_AGREED, 332 | PRTSM_DESIGNATED_SYNCED, 333 | PRTSM_DESIGNATED_RETIRED, 334 | PRTSM_DESIGNATED_FORWARD, 335 | PRTSM_DESIGNATED_LEARN, 336 | PRTSM_DESIGNATED_DISCARD, 337 | PRTSM_DESIGNATED_PORT, 338 | /* AlternatePort and BackupPort role transitions */ 339 | PRTSM_BLOCK_PORT, 340 | PRTSM_BACKUP_PORT, 341 | PRTSM_ALTERNATE_PROPOSED, 342 | PRTSM_ALTERNATE_AGREED, 343 | PRTSM_ALTERNATE_PORT 344 | } PRTSM_states_t; 345 | 346 | /* 13.35 Port State Transition state machine */ 347 | typedef enum 348 | { 349 | PSTSM_DISCARDING, 350 | PSTSM_LEARNING, 351 | PSTSM_FORWARDING 352 | } PSTSM_states_t; 353 | 354 | /* 13.36 Topology Change state machine */ 355 | typedef enum 356 | { 357 | TCSM_INACTIVE, 358 | TCSM_LEARNING, 359 | TCSM_DETECTED, 360 | TCSM_NOTIFIED_TCN, 361 | TCSM_NOTIFIED_TC, 362 | TCSM_PROPAGATING, 363 | TCSM_ACKNOWLEDGED, 364 | TCSM_ACTIVE 365 | } TCSM_states_t; 366 | 367 | /* 368 | * Following standard-defined variables are not defined as variables. 369 | * Their functionality is implemented indirectly by other means: 370 | * - BEGIN, tick, ageingTime. 371 | */ 372 | 373 | typedef struct 374 | { 375 | struct list_head list; /* anchor in global list of bridges */ 376 | 377 | /* List of all ports */ 378 | struct list_head ports; 379 | /* List of all tree instances, first in list (trees.next) is CIST */ 380 | struct list_head trees; 381 | #define GET_CIST_TREE(br) list_entry((br)->trees.next, tree_t, bridge_list) 382 | 383 | bool bridgeEnabled; 384 | 385 | /* Per-bridge configuration parameters */ 386 | mst_configuration_identifier_t MstConfigId; /* 13.24.b */ 387 | protocol_version_t ForceProtocolVersion; /* 13.22.e */ 388 | __u8 MaxHops; /* 13.22.o */ 389 | __u8 Forward_Delay; /* 13.22.f */ 390 | __u8 Max_Age; /* 13.22.i */ 391 | /* The 802.1Q-2005 (13.22.j) says that this parameter is substituted by 392 | * the per-port Hello Time, but we still need it for compatibility 393 | * with old STP implementations. 394 | */ 395 | __u8 Hello_Time; 396 | unsigned int Transmit_Hold_Count; /* 13.22.g */ 397 | unsigned int Migrate_Time; /* 13.22.h */ 398 | unsigned int Ageing_Time; /* 8.8.3 */ 399 | 400 | __u16 vid2fid[MAX_VID + 1]; 401 | __be16 fid2mstid[MAX_FID + 1]; 402 | 403 | /* not in standard */ 404 | unsigned int uptime; 405 | 406 | sysdep_br_data_t sysdeps; 407 | } bridge_t; 408 | 409 | typedef struct 410 | { 411 | struct list_head bridge_list; /* anchor in bridge's list of trees */ 412 | bridge_t * bridge; 413 | __be16 MSTID; /* 0 == CIST */ 414 | 415 | /* List of the per-port data structures for this tree instance */ 416 | struct list_head ports; 417 | 418 | /* 13.23.(c,f,g) Per-bridge per-tree variables */ 419 | bridge_identifier_t BridgeIdentifier; 420 | port_identifier_t rootPortId; 421 | port_priority_vector_t rootPriority; 422 | 423 | /* 13.23.d This is totally calculated from BridgeIdentifier */ 424 | port_priority_vector_t BridgePriority; 425 | 426 | /* 13.23.e Some waste of space here, as MSTIs only use 427 | * remainingHops member of the struct times_t, 428 | * but saves extra checks and improves readability */ 429 | times_t BridgeTimes, rootTimes; 430 | 431 | /* 12.8.1.1.3.(b,c,d) */ 432 | unsigned int time_since_topology_change; 433 | unsigned int topology_change_count; 434 | bool topology_change; 435 | char topology_change_port[IFNAMSIZ]; 436 | char last_topology_change_port[IFNAMSIZ]; 437 | 438 | /* State machines */ 439 | PRSSM_states_t PRSSM_state; 440 | 441 | } tree_t; 442 | 443 | typedef struct 444 | { 445 | struct list_head br_list; /* anchor in bridge's list of ports */ 446 | bridge_t * bridge; 447 | __be16 port_number; 448 | 449 | /* List of all tree instances, first in list (trees.next) is CIST. 450 | * List is sorted by MSTID (by insertion procedure MSTP_IN_create_msti). 451 | */ 452 | struct list_head trees; 453 | #define GET_CIST_PTP_FROM_PORT(prt) \ 454 | list_entry((prt)->trees.next, per_tree_port_t, port_list) 455 | 456 | /* 13.21.(a,b,c) Per-port timers */ 457 | unsigned int mdelayWhile, helloWhen, edgeDelayWhile; 458 | 459 | /* 13.24.(b,c,e,f,g,j,k,l,m,n,o,p,q,r,aw) Per-port variables */ 460 | unsigned int txCount; 461 | bool operEdge, portEnabled, infoInternal, rcvdInternal; 462 | bool mcheck, rcvdBpdu, rcvdRSTP, rcvdSTP, rcvdTcAck, rcvdTcn, sendRSTP; 463 | bool tcAck, newInfo, newInfoMsti; 464 | 465 | /* 6.4.3 */ 466 | bool operPointToPointMAC; 467 | 468 | /* Per-port configuration parameters */ 469 | bool restrictedRole, restrictedTcn; /* 13.24.(h,i) */ 470 | __u32 ExternalPortPathCost; /* 13.22.p */ 471 | __u32 AdminExternalPortPathCost; /* 0 = calculate from speed */ 472 | admin_p2p_t AdminP2P; /* 6.4.3 */ 473 | bool AdminEdgePort; /* 13.22.k */ 474 | bool AutoEdge; /* 13.22.m */ 475 | bool BpduGuardPort; 476 | bool BpduGuardError; 477 | bool NetworkPort; 478 | bool BaInconsistent; 479 | bool dontTxmtBpdu; 480 | bool bpduFilterPort; 481 | 482 | unsigned int rapidAgeingWhile; 483 | unsigned int brAssuRcvdInfoWhile; 484 | 485 | /* State machines */ 486 | PRSM_states_t PRSM_state; 487 | PPMSM_states_t PPMSM_state; 488 | BDSM_states_t BDSM_state; 489 | PTSM_states_t PTSM_state; 490 | 491 | /* Copy of the received BPDU */ 492 | bpdu_t rcvdBpduData; 493 | int rcvdBpduNumOfMstis; 494 | 495 | bool deleted; 496 | 497 | sysdep_if_data_t sysdeps; 498 | unsigned int num_rx_bpdu_filtered; 499 | unsigned int num_rx_bpdu; 500 | unsigned int num_rx_tcn; 501 | unsigned int num_tx_bpdu; 502 | unsigned int num_tx_tcn; 503 | unsigned int num_trans_fwd; 504 | unsigned int num_trans_blk; 505 | } port_t; 506 | 507 | typedef struct 508 | { 509 | struct list_head port_list; /* anchor in port's list of trees */ 510 | struct list_head tree_list; /* anchor in tree's list of per-port data */ 511 | port_t *port; 512 | tree_t *tree; 513 | __be16 MSTID; /* 0 == CIST */ 514 | 515 | int state; /* BR_STATE_xxx */ 516 | 517 | /* 13.21.(d,e,f,g,h) Per-port per-tree timers */ 518 | unsigned int fdWhile, rrWhile, rbWhile, tcWhile, rcvdInfoWhile; 519 | 520 | /* 13.24.(s,t,u,v,w,x,y,z,aa,ab,ac,ad,ae,af,ag,ai,aj,ak,ap,as,at,au,av) 521 | * Per-port per-tree variables */ 522 | bool agree, agreed, disputed, forward, forwarding, learn, learning; 523 | port_info_t rcvdInfo; 524 | port_info_origin_t infoIs; 525 | bool proposed, proposing, rcvdMsg, rcvdTc, reRoot, reselect, selected; 526 | bool fdbFlush, tcProp, updtInfo, sync, synced; 527 | port_identifier_t portId; 528 | port_role_t role, selectedRole; 529 | 530 | /* 13.24.(al,an,aq) Some waste of space here, as MSTIs don't use 531 | * RootID and ExtRootPathCost members of the struct port_priority_vector_t, 532 | * but saves extra checks and improves readability */ 533 | port_priority_vector_t designatedPriority, msgPriority, portPriority; 534 | 535 | /* 13.24.(am,ao,ar) Some waste of space here, as MSTIs only use 536 | * remainingHops member of the struct times_t, 537 | * but saves extra checks and improves readability */ 538 | times_t designatedTimes, msgTimes, portTimes; 539 | 540 | /* 13.24.(ax,ay) Per-port per-MSTI variables, not applicable to CIST */ 541 | bool master, mastered; 542 | 543 | /* Per-port per-tree configuration parameters */ 544 | __u32 InternalPortPathCost; /* 13.22.q */ 545 | __u32 AdminInternalPortPathCost; /* 0 = calculate from speed */ 546 | 547 | /* not in standard, used for calculation of port uptime */ 548 | unsigned int start_time; 549 | 550 | /* State machines */ 551 | PISM_states_t PISM_state; 552 | PRTSM_states_t PRTSM_state; 553 | PSTSM_states_t PSTSM_state; 554 | TCSM_states_t TCSM_state; 555 | 556 | /* Auxiliary flag, helps preventing infinite recursion */ 557 | bool calledFromFlushRoutine; 558 | 559 | /* Pointer to the corresponding MSTI Configuration Message 560 | * in the port->rcvdBpduData */ 561 | msti_configuration_message_t *rcvdMstiConfig; 562 | } per_tree_port_t; 563 | 564 | /* External events (inputs) */ 565 | bool MSTP_IN_bridge_create(bridge_t *br, __u8 *macaddr); 566 | bool MSTP_IN_port_create_and_add_tail(port_t *prt, __u16 portno); 567 | void MSTP_IN_delete_port(port_t *prt); 568 | void MSTP_IN_delete_bridge(bridge_t *br); 569 | void MSTP_IN_set_bridge_address(bridge_t *br, __u8 *macaddr); 570 | void MSTP_IN_set_bridge_enable(bridge_t *br, bool up); 571 | void MSTP_IN_set_port_enable(port_t *prt, bool up, int speed, int duplex); 572 | void MSTP_IN_one_second(bridge_t *br); 573 | void MSTP_IN_all_fids_flushed(per_tree_port_t *ptp); 574 | void MSTP_IN_rx_bpdu(port_t *prt, bpdu_t *bpdu, int size); 575 | 576 | bool MSTP_IN_set_vid2fid(bridge_t *br, __u16 vid, __u16 fid); 577 | bool MSTP_IN_set_all_vids2fids(bridge_t *br, __u16 *vids2fids); 578 | bool MSTP_IN_set_fid2mstid(bridge_t *br, __u16 fid, __u16 mstid); 579 | bool MSTP_IN_set_all_fids2mstids(bridge_t *br, __u16 *fids2mstids); 580 | bool MSTP_IN_get_mstilist(bridge_t *br, int *num_mstis, __u16 *mstids); 581 | bool MSTP_IN_create_msti(bridge_t *br, __u16 mstid); 582 | bool MSTP_IN_delete_msti(bridge_t *br, __u16 mstid); 583 | void MSTP_IN_set_mst_config_id(bridge_t *br, __u16 revision, __u8 *name); 584 | 585 | /* External actions (outputs) */ 586 | void MSTP_OUT_set_state(per_tree_port_t *ptp, int new_state); 587 | void MSTP_OUT_flush_all_fids(per_tree_port_t *ptp); 588 | void MSTP_OUT_set_ageing_time(port_t *prt, unsigned int ageingTime); 589 | void MSTP_OUT_tx_bpdu(port_t *prt, bpdu_t *bpdu, int size); 590 | void MSTP_OUT_shutdown_port(port_t *prt); 591 | 592 | /* Structures for communicating with user */ 593 | /* 12.8.1.1 Read CIST Bridge Protocol Parameters */ 594 | typedef struct 595 | { 596 | bridge_identifier_t bridge_id; 597 | unsigned int time_since_topology_change; 598 | unsigned int topology_change_count; 599 | bool topology_change; 600 | char topology_change_port[IFNAMSIZ]; 601 | char last_topology_change_port[IFNAMSIZ]; 602 | bridge_identifier_t designated_root; 603 | unsigned int root_path_cost; 604 | port_identifier_t root_port_id; 605 | __u8 root_max_age; 606 | __u8 root_forward_delay; 607 | __u8 bridge_max_age; 608 | __u8 bridge_forward_delay; 609 | unsigned int tx_hold_count; 610 | protocol_version_t protocol_version; 611 | bridge_identifier_t regional_root; 612 | unsigned int internal_path_cost; 613 | bool enabled; /* not in standard */ 614 | unsigned int Ageing_Time; 615 | __u8 max_hops; 616 | __u8 bridge_hello_time; 617 | } CIST_BridgeStatus; 618 | 619 | void MSTP_IN_get_cist_bridge_status(bridge_t *br, CIST_BridgeStatus *status); 620 | 621 | /* 12.8.1.2 Read MSTI Bridge Protocol Parameters */ 622 | typedef struct 623 | { 624 | bridge_identifier_t bridge_id; 625 | unsigned int time_since_topology_change; 626 | unsigned int topology_change_count; 627 | bool topology_change; 628 | char topology_change_port[IFNAMSIZ]; 629 | char last_topology_change_port[IFNAMSIZ]; 630 | bridge_identifier_t regional_root; 631 | unsigned int internal_path_cost; 632 | port_identifier_t root_port_id; 633 | } MSTI_BridgeStatus; 634 | 635 | void MSTP_IN_get_msti_bridge_status(tree_t *tree, MSTI_BridgeStatus *status); 636 | 637 | /* 12.8.1.3 Set CIST Bridge Protocol Parameters */ 638 | typedef struct 639 | { 640 | __u8 bridge_max_age; 641 | bool set_bridge_max_age; 642 | 643 | __u8 bridge_forward_delay; 644 | bool set_bridge_forward_delay; 645 | 646 | /* Superseded by MSTP_IN_set_msti_bridge_config for the CIST. 647 | * __u8 bridge_priority; 648 | * bool set_bridge_priority; */ 649 | 650 | protocol_version_t protocol_version; 651 | bool set_protocol_version; 652 | 653 | unsigned int tx_hold_count; 654 | bool set_tx_hold_count; 655 | 656 | __u8 max_hops; 657 | bool set_max_hops; 658 | 659 | __u8 bridge_hello_time; 660 | bool set_bridge_hello_time; 661 | 662 | unsigned int bridge_ageing_time; 663 | bool set_bridge_ageing_time; 664 | } CIST_BridgeConfig; 665 | 666 | int MSTP_IN_set_cist_bridge_config(bridge_t *br, CIST_BridgeConfig *cfg); 667 | 668 | /* 12.8.1.4 Set MSTI Bridge Protocol Parameters */ 669 | /* No need in special structure for single parameter Bridge Priority */ 670 | 671 | int MSTP_IN_set_msti_bridge_config(tree_t *tree, __u8 bridge_priority); 672 | 673 | /* 12.8.2.1 Read CIST Port Parameters */ 674 | typedef struct 675 | { 676 | unsigned int uptime; 677 | int state; /* BR_STATE_xxx */ 678 | port_identifier_t port_id; 679 | __u32 admin_external_port_path_cost; /* not in standard. 0 = auto */ 680 | __u32 external_port_path_cost; 681 | bridge_identifier_t designated_root; /* from portPriority */ 682 | __u32 designated_external_cost; /* from portPriority */ 683 | bridge_identifier_t designated_bridge; /* from portPriority */ 684 | port_identifier_t designated_port; /* from portPriority */ 685 | bool tc_ack; /* tcAck */ 686 | __u8 port_hello_time; /* from portTimes */ 687 | bool admin_edge_port; 688 | bool auto_edge_port; /* not in standard */ 689 | bool oper_edge_port; 690 | /* 802.1Q-2005 wants here MAC_Enabled & MAC_Operational. We don't know 691 | * neither of these. Return portEnabled and feel happy. */ 692 | bool enabled; 693 | admin_p2p_t admin_p2p; 694 | bool oper_p2p; 695 | bool restricted_role; 696 | bool restricted_tcn; 697 | port_role_t role; 698 | bool disputed; 699 | bridge_identifier_t designated_regional_root; /* from portPriority */ 700 | __u32 designated_internal_cost; /* from portPriority */ 701 | __u32 admin_internal_port_path_cost; /* not in standard. 0 = auto */ 702 | __u32 internal_port_path_cost; /* not in standard */ 703 | bool bpdu_guard_port; 704 | bool bpdu_guard_error; 705 | bool bpdu_filter_port; 706 | bool network_port; 707 | bool ba_inconsistent; 708 | unsigned int num_rx_bpdu_filtered; 709 | unsigned int num_rx_bpdu; 710 | unsigned int num_rx_tcn; 711 | unsigned int num_tx_bpdu; 712 | unsigned int num_tx_tcn; 713 | unsigned int num_trans_fwd; 714 | unsigned int num_trans_blk; 715 | bool rcvdBpdu; 716 | bool rcvdRSTP; 717 | bool rcvdSTP; 718 | bool rcvdTcAck; 719 | bool rcvdTcn; 720 | bool sendRSTP; 721 | } CIST_PortStatus; 722 | 723 | void MSTP_IN_get_cist_port_status(port_t *prt, CIST_PortStatus *status); 724 | 725 | /* 12.8.2.2 Read MSTI Port Parameters */ 726 | typedef struct 727 | { 728 | unsigned int uptime; 729 | int state; /* BR_STATE_xxx */ 730 | port_identifier_t port_id; 731 | __u32 admin_internal_port_path_cost; /* not in standard. 0 = auto */ 732 | __u32 internal_port_path_cost; 733 | bridge_identifier_t designated_regional_root; /* from portPriority */ 734 | __u32 designated_internal_cost; /* from portPriority */ 735 | bridge_identifier_t designated_bridge; /* from portPriority */ 736 | port_identifier_t designated_port; /* from portPriority */ 737 | port_role_t role; 738 | bool disputed; 739 | } MSTI_PortStatus; 740 | 741 | void MSTP_IN_get_msti_port_status(per_tree_port_t *ptp, 742 | MSTI_PortStatus *status); 743 | 744 | /* 12.8.2.3 Set CIST port parameters */ 745 | typedef struct 746 | { 747 | __u32 admin_external_port_path_cost; /* not in standard. 0 = auto */ 748 | bool set_admin_external_port_path_cost; 749 | 750 | /* Superseded by MSTP_IN_set_msti_port_config for the CIST. 751 | * __u32 admin_internal_port_path_cost; 752 | * bool set_admin_internal_port_path_cost; 753 | * 754 | * __u8 port_priority; 755 | * bool set_port_priority; 756 | */ 757 | 758 | bool admin_edge_port; 759 | bool set_admin_edge_port; 760 | 761 | bool auto_edge_port; /* not in standard */ 762 | bool set_auto_edge_port; 763 | 764 | admin_p2p_t admin_p2p; 765 | bool set_admin_p2p; 766 | 767 | bool restricted_role; 768 | bool set_restricted_role; 769 | 770 | bool restricted_tcn; 771 | bool set_restricted_tcn; 772 | 773 | bool bpdu_guard_port; 774 | bool set_bpdu_guard_port; 775 | 776 | bool network_port; 777 | bool set_network_port; 778 | 779 | bool dont_txmt; 780 | bool set_dont_txmt; 781 | 782 | bool bpdu_filter_port; 783 | bool set_bpdu_filter_port; 784 | } CIST_PortConfig; 785 | 786 | int MSTP_IN_set_cist_port_config(port_t *prt, CIST_PortConfig *cfg); 787 | 788 | /* 12.8.2.4 Set MSTI port parameters */ 789 | typedef struct 790 | { 791 | __u32 admin_internal_port_path_cost; /* 0 = auto */ 792 | bool set_admin_internal_port_path_cost; 793 | 794 | __u8 port_priority; 795 | bool set_port_priority; 796 | } MSTI_PortConfig; 797 | 798 | int MSTP_IN_set_msti_port_config(per_tree_port_t *ptp, MSTI_PortConfig *cfg); 799 | 800 | /* 12.8.2.5 Force BPDU Migration Check */ 801 | int MSTP_IN_port_mcheck(port_t *prt); 802 | 803 | #endif /* MSTP_H */ 804 | --------------------------------------------------------------------------------