| /* |
| * |
| * Copyright (C) 2007 Mindspeed Technologies, Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| #include "cmm.h" |
| #include "ffbridge.h" |
| #include "itf.h" |
| #include <sys/ioctl.h> |
| #include <net/if.h> |
| #include <sys/socket.h> |
| #include <string.h> |
| #include <ctype.h> |
| |
| static void __cmmGetBridgePorts(int fd, struct interface *bridge) |
| { |
| unsigned long args[4] = {BRCTL_GET_PORT_LIST, (unsigned long)bridge->ifindices, MAX_PORTS, 0}; |
| struct ifreq ifr; |
| int max_ports; |
| |
| memset(bridge->ifindices, 0, MAX_PORTS * sizeof(int)); |
| if (____itf_get_name(bridge, ifr.ifr_name, sizeof(ifr.ifr_name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s::%d: ____itf_get_name(%d) failed\n", __func__, __LINE__, bridge->ifindex); |
| |
| goto out; |
| } |
| |
| ifr.ifr_data = (char *) &args; |
| |
| if ((max_ports = ioctl(fd, SIOCDEVPRIVATE, &ifr)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s::%d: ioctl() %s\n", __func__, __LINE__, strerror(errno)); |
| goto out; |
| } |
| out: |
| return; |
| } |
| |
| void __cmmGetBridges(int fd) |
| { |
| int ifindices[MAX_BRIDGES]; |
| unsigned long args[3] = {BRCTL_GET_BRIDGES, (unsigned long)ifindices, MAX_BRIDGES}; |
| struct interface *itf; |
| char ifname[IFNAMSIZ]; |
| int ifindex; |
| int num, i; |
| |
| num = ioctl(fd, SIOCGIFBR, args); |
| if (num < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s::%d: ioctl() %s\n", __func__, __LINE__, strerror(errno)); |
| |
| goto out; |
| } |
| |
| for (i = 0; i < num; i++) |
| { |
| ifindex = ifindices[i]; |
| |
| itf = __itf_find(ifindex); |
| if (!itf) |
| continue; |
| |
| itf->itf_flags |= ITF_BRIDGE; |
| |
| __cmmGetBridgePorts(fd, itf); |
| |
| cmm_print(DEBUG_INFO, "%s::%d: %s is a bridge\n", __func__, __LINE__, if_indextoname(itf->ifindex, ifname)); |
| } |
| |
| out: |
| return; |
| } |
| |
| static void cmmBrgetAllMacPort(int br_ifindex) |
| { |
| struct __fdb_entry fe[CHUNK]; |
| int i, n; |
| unsigned long args[4]; |
| struct ifreq ifr; |
| int retries; |
| int fd; |
| |
| cmm_print(DEBUG_INFO, "%s(%d)\n", __func__, br_ifindex); |
| |
| if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s::%d: socket() %s\n", __func__, __LINE__, strerror(errno)); |
| goto err; |
| } |
| |
| args[0] = BRCTL_GET_FDB_ENTRIES; |
| args[1] = (unsigned long) fe; |
| args[2] = CHUNK; |
| args[3] = 0; |
| |
| if (__itf_get_name(br_ifindex, ifr.ifr_name, sizeof(ifr.ifr_name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s::%d: __itf_get_name(%d) failed\n", __func__, __LINE__, br_ifindex); |
| |
| goto err; |
| } |
| |
| ifr.ifr_data = (char *) args; |
| |
| while (1) { |
| retries = 0; |
| |
| retry: |
| n = ioctl(fd, SIOCDEVPRIVATE, &ifr); |
| /* table can change during ioctl processing */ |
| if (n < 0) |
| { |
| if (errno == EAGAIN) |
| { |
| if (++retries < 10) |
| goto retry; |
| else |
| goto close; |
| } |
| |
| cmm_print(DEBUG_ERROR, "%s::%d: ioctl() %s\n", __func__, __LINE__, strerror(errno)); |
| goto close; |
| |
| } else if (n == 0) |
| goto close; |
| |
| for (i = 0; i < n; i++) { |
| if (fe[i].is_local) |
| continue; |
| |
| __cmmNeighUpdateAllMacs(br_ifindex, fe[i].mac_addr, fe[i].port_no); |
| } |
| |
| args[3] += n; |
| } |
| |
| close: |
| close(fd); |
| |
| err: |
| return; |
| } |
| |
| |
| int cmmBrToFF(struct RtEntry *route) |
| { |
| int ifindex; |
| char brname[IFNAMSIZ], ifname[IFNAMSIZ]; |
| |
| if (!route->neighEntry) |
| { |
| cmm_print(DEBUG_ERROR, "%s: neighbor entry not set in route\n", __func__); |
| goto err; |
| } |
| |
| /* FIXME Update also if more than N seconds have passed since last update */ |
| if (route->neighEntry->port < 0) |
| cmmBrgetAllMacPort(route->oifindex); |
| |
| if (route->neighEntry->port < 0) |
| goto err; |
| |
| ifindex = __itf_get_from_bridge_port(route->oifindex, route->neighEntry->port); |
| if (ifindex <= 0) |
| goto err; |
| |
| if (route->phys_oifindex != ifindex) |
| { |
| route->phys_oifindex = ifindex; |
| } |
| |
| cmm_print(DEBUG_INFO, "%s::%d: if:%s br:%s port:%d mac:%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n", |
| __func__, __LINE__, |
| if_indextoname(route->phys_oifindex, ifname), |
| if_indextoname(route->oifindex, brname), |
| route->neighEntry->port, |
| route->neighEntry->macAddr[0], route->neighEntry->macAddr[1], |
| route->neighEntry->macAddr[2], route->neighEntry->macAddr[3], |
| route->neighEntry->macAddr[4], route->neighEntry->macAddr[5]); |
| |
| return 0; |
| |
| err: |
| return -1; |
| } |
| |
| |
| /* This function gets the physical port information from the bridge_port */ |
| int cmmBrGetPhysItf(int br_ifindex, unsigned char *fdb_mac) |
| { |
| struct __fdb_entry fe[CHUNK]; |
| int i, n; |
| unsigned long args[4]; |
| struct ifreq ifr; |
| int retries; |
| int fd, phys_ifindex = -1; |
| |
| cmm_print(DEBUG_INFO, "%s(%d)\n", __func__, br_ifindex); |
| |
| if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s::%d: socket() %s\n", __func__, __LINE__, strerror(errno)); |
| goto err; |
| } |
| |
| args[0] = BRCTL_GET_FDB_ENTRIES; |
| args[1] = (unsigned long) fe; |
| args[2] = CHUNK; |
| args[3] = 0; |
| |
| if (__itf_get_name(br_ifindex, ifr.ifr_name, sizeof(ifr.ifr_name)) < 0) |
| { |
| cmm_print(DEBUG_ERROR, "%s::%d: __itf_get_name(%d) failed\n", __func__, __LINE__, br_ifindex); |
| |
| goto err; |
| } |
| |
| ifr.ifr_data = (char *) args; |
| |
| while (1) { |
| retries = 0; |
| |
| retry: |
| n = ioctl(fd, SIOCDEVPRIVATE, &ifr); |
| /* table can change during ioctl processing */ |
| if (n < 0) |
| { |
| if (errno == EAGAIN) |
| { |
| if (++retries < 10) |
| goto retry; |
| else |
| goto close; |
| } |
| |
| cmm_print(DEBUG_ERROR, "%s::%d: ioctl() %s\n", __func__, __LINE__, strerror(errno)); |
| goto close; |
| |
| } else if (n == 0) |
| goto close; |
| |
| for (i = 0; i < n; i++) { |
| if (fe[i].is_local) |
| continue; |
| |
| if (memcmp(fdb_mac, fe[i].mac_addr, 6) == 0) |
| { |
| cmm_print(DEBUG_INFO, "%s(%d) Found mac\n", __func__, fe[i].port_no); |
| phys_ifindex = __itf_get_from_bridge_port(br_ifindex, fe[i].port_no); |
| goto close; |
| } |
| } |
| |
| args[3] += n; |
| } |
| |
| close: |
| close(fd); |
| |
| err: |
| return (phys_ifindex); |
| } |
| |
| |