diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c index 77fc80e7e..e9867b0db 100644 --- a/src/drivers/driver_roboswitch.c +++ b/src/drivers/driver_roboswitch.c @@ -16,12 +16,18 @@ #include #include #include +#include #include #include "common.h" #include "driver.h" +#include "l2_packet/l2_packet.h" -#define ROBO_PHY_ADDR 0x1E /* RoboSwitch PHY address */ +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL 0x888e +#endif + +#define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ /* MII access registers */ #define ROBO_MII_PAGE 0x10 /* MII page register */ @@ -46,10 +52,10 @@ #define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ /* VLAN page registers */ -#define ROBO_VLAN_ACCESS 0x06 /* VLAN table Access register */ -#define ROBO_VLAN_ACCESS_5365 0x08 /* VLAN table Access register (5365) */ -#define ROBO_VLAN_READ 0x0C /* VLAN read register */ -#define ROBO_VLAN_MAX 0xFF /* Maximum number of VLANs */ +#define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */ +#define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */ +#define ROBO_VLAN_READ 0x0c /* VLAN read register */ +#define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */ static const u8 pae_group_addr[ETH_ALEN] = @@ -58,9 +64,11 @@ static const u8 pae_group_addr[ETH_ALEN] = struct wpa_driver_roboswitch_data { void *ctx; + struct l2_packet_data *l2; char ifname[IFNAMSIZ + 1]; + u8 own_addr[ETH_ALEN]; struct ifreq ifr; - int fd; + int fd, is_5350; u16 ports; }; @@ -72,6 +80,18 @@ static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) } +/* + * RoboSwitch uses 16-bit Big Endian addresses. + * The ordering of the words is reversed in the MII registers. + */ +static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be) +{ + int i; + for (i = 0; i < ETH_ALEN; i += 2) + be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); +} + + static u16 wpa_driver_roboswitch_mdio_read( struct wpa_driver_roboswitch_data *drv, u8 reg) { @@ -157,6 +177,19 @@ static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, } +static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && + os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) { + wpa_supplicant_rx_eapol(drv->ctx, src_addr, buf + 14, + len - 14); + } +} + + static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) { ssid[0] = 0; @@ -181,6 +214,32 @@ static int wpa_driver_roboswitch_get_capa(void *priv, } +static int wpa_driver_roboswitch_set_param(void *priv, const char *param) +{ + struct wpa_driver_roboswitch_data *drv = priv; + char *sep; + + if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) { + sep = drv->ifname + os_strlen(drv->ifname); + *sep = '.'; + drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL, + wpa_driver_roboswitch_receive, drv, + 1); + if (drv->l2 == NULL) { + wpa_printf(MSG_INFO, "%s: Unable to listen on %s", + __func__, drv->ifname); + return -1; + } + *sep = '\0'; + l2_packet_get_own_addr(drv->l2, drv->own_addr); + } else { + wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__); + drv->l2 = NULL; + } + return 0; +} + + static const char * wpa_driver_roboswitch_get_ifname(void *priv) { struct wpa_driver_roboswitch_data *drv = priv; @@ -189,137 +248,109 @@ static const char * wpa_driver_roboswitch_get_ifname(void *priv) static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, - const u8 *addr) + u16 ports, const u8 *addr) { - int i; - u16 _read, zero = 0; - /* For reasons of simplicity we assume ETH_ALEN is even. */ - u16 addr_word[ETH_ALEN / 2]; - /* RoboSwitch uses 16-bit Big Endian addresses. */ - /* The ordering of the words is reversed in the MII registers. */ - for (i = 0; i < ETH_ALEN; i += 2) - addr_word[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); + u16 read1[3], read2[3], addr_be16[3]; + + wpa_driver_roboswitch_addr_be16(addr, addr_be16); - /* check if multiport addresses are not yet enabled */ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_CONF, &_read, 1) < 0) + ROBO_ARLCTRL_CONF, read1, 1) < 0) return -1; - - if (!(_read & (1 << 4))) { - _read |= 1 << 4; + if (!(read1[0] & (1 << 4))) { + /* multiport addresses are not yet enabled */ + read1[0] |= 1 << 4; wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, addr_word, 3); + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_1, &drv->ports, - 1); + ROBO_ARLCTRL_VEC_1, &ports, 1); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &zero, 1); + ROBO_ARLCTRL_ADDR_2, addr_be16, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_CONF, &_read, 1); - return 0; + ROBO_ARLCTRL_VEC_2, &ports, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, read1, 1); + } else { + /* if both multiport addresses are the same we can add */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, read1, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, read2, 3); + if (os_memcmp(read1, read2, 6) != 0) + return -1; + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, read1, 1); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, read2, 1); + if (read1[0] != read2[0]) + return -1; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports, 1); } - - /* check if multiport address 1 is free */ - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, - &_read, 1); - if (_read == 0) { - wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, addr_word, 3); - wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_1, &drv->ports, - 1); - return 0; - } - /* check if multiport address 2 is free */ - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, - &_read, 1); - if (_read == 0) { - wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, addr_word, 3); - wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &drv->ports, - 1); - return 0; - } - - /* out of free multiport addresses */ - return -1; + return 0; } static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, - const u8 *addr) + u16 ports, const u8 *addr) { - int i; - u16 _read[3], zero = 0; - u16 addr_word[ETH_ALEN / 2]; /* same as at join */ + u16 _read, addr_be16[3], addr_read[3], ports_read; - for (i = 0; i < ETH_ALEN; i += 2) - addr_word[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); + wpa_driver_roboswitch_addr_be16(addr, addr_be16); - /* check if multiport address 1 was used */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, + &_read, 1); + /* If ARL control is disabled, there is nothing to leave. */ + if (!(_read & (1 << 4))) return -1; + + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_read, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, - _read, 1); - if (_read[0] == drv->ports) { + &ports_read, 1); + /* check if we occupy multiport address 1 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, _read, 3); - if (os_memcmp(_read, addr_word, 6) == 0) { - wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_1, &zero, - 1); - goto clean_up; - } - } - - /* check if multiport address 2 was used */ - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_2, - _read, 1); - if (_read[0] == drv->ports) { + ROBO_ARLCTRL_ADDR_2, addr_read, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, _read, 3); - if (os_memcmp(_read, addr_word, 6) == 0) { + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* and multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + _read &= ~(1 << 4); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &zero, - 1); - goto clean_up; - } - } - - /* used multiport address not found */ - return -1; - -clean_up: - /* leave the multiport registers in a sane state */ - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, - _read, 1); - if (_read[0] == 0) { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, _read, 1); - if (_read[0] == 0) { - wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_CONF, _read, - 1); - _read[0] &= ~(1 << 4); - wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_CONF, _read, + ROBO_ARLCTRL_CONF, &_read, 1); } else { wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_2, _read, - 3); - wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_ADDR_1, _read, - 3); + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, _read, - 1); + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_1, _read, - 1); + ROBO_ARLCTRL_ADDR_2, + addr_read, 3); wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, - ROBO_ARLCTRL_VEC_2, &zero, - 1); + ROBO_ARLCTRL_VEC_2, + &ports_read, 1); } + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* or multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + } else return -1; } return 0; } @@ -328,40 +359,35 @@ clean_up: static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) { struct wpa_driver_roboswitch_data *drv; - int len = -1, sep = -1; - u16 vlan_max = ROBO_VLAN_MAX, vlan = 0, vlan_read[2]; + char *sep; + u16 vlan = 0, _read[2]; drv = os_zalloc(sizeof(*drv)); if (drv == NULL) return NULL; drv->ctx = ctx; + drv->own_addr[0] = '\0'; - while (ifname[++len]) { - if (ifname[len] == '.') - sep = len; - } - if (sep < 0 || sep >= len - 1) { + /* copy ifname and take a pointer to the second to last character */ + sep = drv->ifname + + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; + /* find the '.' seperating and */ + while (sep > drv->ifname && *sep != '.') sep--; + if (sep <= drv->ifname) { wpa_printf(MSG_INFO, "%s: No . pair in " - "interface name %s", __func__, ifname); + "interface name %s", __func__, drv->ifname); os_free(drv); return NULL; } - if (sep > IFNAMSIZ) { - wpa_printf(MSG_INFO, "%s: Interface name %s is too long", - __func__, ifname); - os_free(drv); - return NULL; - } - os_memcpy(drv->ifname, ifname, sep); - drv->ifname[sep] = '\0'; - while (++sep < len) { - if (ifname[sep] < '0' || ifname[sep] > '9') { + *sep = '\0'; + while (*++sep) { + if (*sep < '0' || *sep > '9') { wpa_printf(MSG_INFO, "%s: Invalid vlan specification " "in interface name %s", __func__, ifname); os_free(drv); return NULL; } vlan *= 10; - vlan += ifname[sep] - '0'; + vlan += *sep - '0'; if (vlan > ROBO_VLAN_MAX) { wpa_printf(MSG_INFO, "%s: VLAN out of range in " "interface name %s", __func__, ifname); @@ -391,31 +417,32 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) return NULL; } + /* set and read back to see if the register can be used */ + _read[0] = ROBO_VLAN_MAX; + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read + 1, 1); + drv->is_5350 = _read[0] == _read[1]; + /* set the read bit */ vlan |= 1 << 13; - /* set and read back to see if the register can be used */ - wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS, - &vlan_max, 1); - wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS, - &vlan_max, 1); - if (vlan_max == ROBO_VLAN_MAX) /* pre-5365 */ - wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, - ROBO_VLAN_ACCESS, &vlan, 1); - else /* 5365 uses a different register */ - wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, - ROBO_VLAN_ACCESS_5365, &vlan, 1); - wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, - vlan_read, 2); - if (!(vlan_read[1] & (1 << 4))) { + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, + drv->is_5350 ? ROBO_VLAN_ACCESS_5350 + : ROBO_VLAN_ACCESS, + &vlan, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read, + drv->is_5350 ? 2 : 1); + if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) { wpa_printf(MSG_INFO, "%s: Could not get port information for " "VLAN %d", __func__, vlan & ~(1 << 13)); os_free(drv); return NULL; } - drv->ports = vlan_read[0] & 0x001F; + drv->ports = _read[0] & 0x001F; /* add the MII port */ drv->ports |= 1 << 8; - if (wpa_driver_roboswitch_join(drv, pae_group_addr) < 0) { + if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) { wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); os_free(drv); return NULL; @@ -432,7 +459,11 @@ static void wpa_driver_roboswitch_deinit(void *priv) { struct wpa_driver_roboswitch_data *drv = priv; - if (wpa_driver_roboswitch_leave(drv, pae_group_addr) < 0) { + if (drv->l2) { + l2_packet_deinit(drv->l2); + drv->l2 = NULL; + } + if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) { wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", __func__); } @@ -450,5 +481,6 @@ const struct wpa_driver_ops wpa_driver_roboswitch_ops = { .get_capa = wpa_driver_roboswitch_get_capa, .init = wpa_driver_roboswitch_init, .deinit = wpa_driver_roboswitch_deinit, + .set_param = wpa_driver_roboswitch_set_param, .get_ifname = wpa_driver_roboswitch_get_ifname, };