/* * Dump information about all ports, in response to GETLINK */ static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) { struct net *net = sock_net(skb->sk); struct net_device *dev; int idx; idx = 0; for_each_netdev(net, dev) { /* not a bridge port */ if (!br_port_exists(dev) || idx < cb->args[0]) goto skip; if (br_fill_ifinfo(skb, br_port_get(dev), NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWLINK, NLM_F_MULTI) < 0) break; skip: ++idx; } cb->args[0] = idx; return skb->len; }
/* * Change state of port (ie from forwarding to blocking etc) * Used by spanning tree in user space. */ static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); struct ifinfomsg *ifm; struct nlattr *protinfo; struct net_device *dev; struct net_bridge_port *p; u8 new_state; if (nlmsg_len(nlh) < sizeof(*ifm)) return -EINVAL; ifm = nlmsg_data(nlh); if (ifm->ifi_family != AF_BRIDGE) return -EPFNOSUPPORT; protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); if (!protinfo || nla_len(protinfo) < sizeof(u8)) return -EINVAL; new_state = nla_get_u8(protinfo); if (new_state > BR_STATE_BLOCKING) return -EINVAL; dev = __dev_get_by_index(net, ifm->ifi_index); if (!dev) return -ENODEV; if (!br_port_exists(dev)) return -EINVAL; p = br_port_get(dev); /* if kernel STP is running, don't allow changes */ if (p->br->stp_enabled == BR_KERNEL_STP) return -EBUSY; if (!netif_running(dev) || (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)) return -ENETDOWN; p->state = new_state; br_log_state(p); return 0; }
/* * Handle changes in state of network devices enslaved to a bridge. * * Note: don't care about up/down if bridge itself is down, because * port state is checked when bridge is brought up. */ static int br_device_event(struct notifier_block *unused, unsigned long event, void *ptr) { struct net_device *dev = ptr; struct net_bridge_port *p = br_port_get(dev); struct net_bridge *br; bool changed_addr; int err; /* not a port of a bridge */ if (!br_port_exists(dev)) return NOTIFY_DONE; p = br_port_get(dev); br = p->br; switch (event) { case NETDEV_CHANGEMTU: dev_set_mtu(br->dev, br_min_mtu(br)); break; case NETDEV_CHANGEADDR: spin_lock_bh(&br->lock); br_fdb_changeaddr(p, dev->dev_addr); changed_addr = br_stp_recalculate_bridge_id(br); spin_unlock_bh(&br->lock); if (changed_addr) call_netdevice_notifiers(NETDEV_CHANGEADDR, br->dev); break; case NETDEV_CHANGE: br_port_carrier_check(p); break; case NETDEV_FEAT_CHANGE: spin_lock_bh(&br->lock); if (netif_running(br->dev)) br_features_recompute(br); spin_unlock_bh(&br->lock); break; case NETDEV_DOWN: spin_lock_bh(&br->lock); if (br->dev->flags & IFF_UP) br_stp_disable_port(p); spin_unlock_bh(&br->lock); break; case NETDEV_UP: if (netif_carrier_ok(dev) && (br->dev->flags & IFF_UP)) { spin_lock_bh(&br->lock); br_stp_enable_port(p); spin_unlock_bh(&br->lock); } break; case NETDEV_UNREGISTER: br_del_if(br, dev); break; case NETDEV_CHANGENAME: err = br_sysfs_renameif(p); if (err) return notifier_from_errno(err); break; case NETDEV_PRE_TYPE_CHANGE: /* Forbid underlaying device to change its type. */ return NOTIFY_BAD; } /* Events that may cause spanning tree to refresh */ if (event == NETDEV_CHANGEADDR || event == NETDEV_UP || event == NETDEV_CHANGE || event == NETDEV_DOWN) br_ifinfo_notify(RTM_NEWLINK, p); return NOTIFY_DONE; }