111dc1f36SStephen Hemminger /* 211dc1f36SStephen Hemminger * Bridge netlink control interface 311dc1f36SStephen Hemminger * 411dc1f36SStephen Hemminger * Authors: 511dc1f36SStephen Hemminger * Stephen Hemminger <shemminger@osdl.org> 611dc1f36SStephen Hemminger * 711dc1f36SStephen Hemminger * This program is free software; you can redistribute it and/or 811dc1f36SStephen Hemminger * modify it under the terms of the GNU General Public License 911dc1f36SStephen Hemminger * as published by the Free Software Foundation; either version 1011dc1f36SStephen Hemminger * 2 of the License, or (at your option) any later version. 1111dc1f36SStephen Hemminger */ 1211dc1f36SStephen Hemminger 1311dc1f36SStephen Hemminger #include <linux/kernel.h> 14*32fe21c0SThomas Graf #include <net/rtnetlink.h> 1511dc1f36SStephen Hemminger #include "br_private.h" 1611dc1f36SStephen Hemminger 17339bf98fSThomas Graf static inline size_t br_nlmsg_size(void) 18339bf98fSThomas Graf { 19339bf98fSThomas Graf return NLMSG_ALIGN(sizeof(struct ifinfomsg)) 20339bf98fSThomas Graf + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ 21339bf98fSThomas Graf + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ 22339bf98fSThomas Graf + nla_total_size(4) /* IFLA_MASTER */ 23339bf98fSThomas Graf + nla_total_size(4) /* IFLA_MTU */ 24339bf98fSThomas Graf + nla_total_size(4) /* IFLA_LINK */ 25339bf98fSThomas Graf + nla_total_size(1) /* IFLA_OPERSTATE */ 26339bf98fSThomas Graf + nla_total_size(1); /* IFLA_PROTINFO */ 27339bf98fSThomas Graf } 28339bf98fSThomas Graf 2911dc1f36SStephen Hemminger /* 3011dc1f36SStephen Hemminger * Create one netlink message for one interface 3111dc1f36SStephen Hemminger * Contains port and master info as well as carrier and bridge state. 3211dc1f36SStephen Hemminger */ 3311dc1f36SStephen Hemminger static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *port, 3411dc1f36SStephen Hemminger u32 pid, u32 seq, int event, unsigned int flags) 3511dc1f36SStephen Hemminger { 3611dc1f36SStephen Hemminger const struct net_bridge *br = port->br; 3711dc1f36SStephen Hemminger const struct net_device *dev = port->dev; 3874685962SThomas Graf struct ifinfomsg *hdr; 3911dc1f36SStephen Hemminger struct nlmsghdr *nlh; 4011dc1f36SStephen Hemminger u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; 4111dc1f36SStephen Hemminger 4211dc1f36SStephen Hemminger pr_debug("br_fill_info event %d port %s master %s\n", 4311dc1f36SStephen Hemminger event, dev->name, br->dev->name); 4411dc1f36SStephen Hemminger 4574685962SThomas Graf nlh = nlmsg_put(skb, pid, seq, event, sizeof(*hdr), flags); 4674685962SThomas Graf if (nlh == NULL) 4726932566SPatrick McHardy return -EMSGSIZE; 4811dc1f36SStephen Hemminger 4974685962SThomas Graf hdr = nlmsg_data(nlh); 5074685962SThomas Graf hdr->ifi_family = AF_BRIDGE; 5174685962SThomas Graf hdr->__ifi_pad = 0; 5274685962SThomas Graf hdr->ifi_type = dev->type; 5374685962SThomas Graf hdr->ifi_index = dev->ifindex; 5474685962SThomas Graf hdr->ifi_flags = dev_get_flags(dev); 5574685962SThomas Graf hdr->ifi_change = 0; 5611dc1f36SStephen Hemminger 5774685962SThomas Graf NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); 5874685962SThomas Graf NLA_PUT_U32(skb, IFLA_MASTER, br->dev->ifindex); 5974685962SThomas Graf NLA_PUT_U32(skb, IFLA_MTU, dev->mtu); 6074685962SThomas Graf NLA_PUT_U8(skb, IFLA_OPERSTATE, operstate); 6111dc1f36SStephen Hemminger 6211dc1f36SStephen Hemminger if (dev->addr_len) 6374685962SThomas Graf NLA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr); 6411dc1f36SStephen Hemminger 6511dc1f36SStephen Hemminger if (dev->ifindex != dev->iflink) 6674685962SThomas Graf NLA_PUT_U32(skb, IFLA_LINK, dev->iflink); 6711dc1f36SStephen Hemminger 6811dc1f36SStephen Hemminger if (event == RTM_NEWLINK) 6974685962SThomas Graf NLA_PUT_U8(skb, IFLA_PROTINFO, port->state); 7011dc1f36SStephen Hemminger 7174685962SThomas Graf return nlmsg_end(skb, nlh); 7211dc1f36SStephen Hemminger 7374685962SThomas Graf nla_put_failure: 7426932566SPatrick McHardy nlmsg_cancel(skb, nlh); 7526932566SPatrick McHardy return -EMSGSIZE; 7611dc1f36SStephen Hemminger } 7711dc1f36SStephen Hemminger 7811dc1f36SStephen Hemminger /* 7911dc1f36SStephen Hemminger * Notify listeners of a change in port information 8011dc1f36SStephen Hemminger */ 8111dc1f36SStephen Hemminger void br_ifinfo_notify(int event, struct net_bridge_port *port) 8211dc1f36SStephen Hemminger { 8311dc1f36SStephen Hemminger struct sk_buff *skb; 84280a306cSThomas Graf int err = -ENOBUFS; 8511dc1f36SStephen Hemminger 8611dc1f36SStephen Hemminger pr_debug("bridge notify event=%d\n", event); 87339bf98fSThomas Graf skb = nlmsg_new(br_nlmsg_size(), GFP_ATOMIC); 88280a306cSThomas Graf if (skb == NULL) 89280a306cSThomas Graf goto errout; 9011dc1f36SStephen Hemminger 91280a306cSThomas Graf err = br_fill_ifinfo(skb, port, 0, 0, event, 0); 9226932566SPatrick McHardy if (err < 0) { 9326932566SPatrick McHardy /* -EMSGSIZE implies BUG in br_nlmsg_size() */ 9426932566SPatrick McHardy WARN_ON(err == -EMSGSIZE); 9526932566SPatrick McHardy kfree_skb(skb); 9626932566SPatrick McHardy goto errout; 9726932566SPatrick McHardy } 98280a306cSThomas Graf err = rtnl_notify(skb, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); 99280a306cSThomas Graf errout: 100280a306cSThomas Graf if (err < 0) 101280a306cSThomas Graf rtnl_set_sk_err(RTNLGRP_LINK, err); 10211dc1f36SStephen Hemminger } 10311dc1f36SStephen Hemminger 10411dc1f36SStephen Hemminger /* 10511dc1f36SStephen Hemminger * Dump information about all ports, in response to GETLINK 10611dc1f36SStephen Hemminger */ 10711dc1f36SStephen Hemminger static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) 10811dc1f36SStephen Hemminger { 10911dc1f36SStephen Hemminger struct net_device *dev; 11011dc1f36SStephen Hemminger int idx; 11111dc1f36SStephen Hemminger 11211dc1f36SStephen Hemminger read_lock(&dev_base_lock); 11311dc1f36SStephen Hemminger for (dev = dev_base, idx = 0; dev; dev = dev->next) { 11411dc1f36SStephen Hemminger /* not a bridge port */ 11574685962SThomas Graf if (dev->br_port == NULL || idx < cb->args[0]) 11674685962SThomas Graf goto skip; 11711dc1f36SStephen Hemminger 11874685962SThomas Graf if (br_fill_ifinfo(skb, dev->br_port, NETLINK_CB(cb->skb).pid, 11974685962SThomas Graf cb->nlh->nlmsg_seq, RTM_NEWLINK, 12074685962SThomas Graf NLM_F_MULTI) < 0) 12111dc1f36SStephen Hemminger break; 12274685962SThomas Graf skip: 12311dc1f36SStephen Hemminger ++idx; 12411dc1f36SStephen Hemminger } 12511dc1f36SStephen Hemminger read_unlock(&dev_base_lock); 12611dc1f36SStephen Hemminger 12711dc1f36SStephen Hemminger cb->args[0] = idx; 12811dc1f36SStephen Hemminger 12911dc1f36SStephen Hemminger return skb->len; 13011dc1f36SStephen Hemminger } 13111dc1f36SStephen Hemminger 13211dc1f36SStephen Hemminger /* 13311dc1f36SStephen Hemminger * Change state of port (ie from forwarding to blocking etc) 13411dc1f36SStephen Hemminger * Used by spanning tree in user space. 13511dc1f36SStephen Hemminger */ 13611dc1f36SStephen Hemminger static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) 13711dc1f36SStephen Hemminger { 13874685962SThomas Graf struct ifinfomsg *ifm; 13974685962SThomas Graf struct nlattr *protinfo; 14011dc1f36SStephen Hemminger struct net_device *dev; 14111dc1f36SStephen Hemminger struct net_bridge_port *p; 14211dc1f36SStephen Hemminger u8 new_state; 14311dc1f36SStephen Hemminger 14474685962SThomas Graf if (nlmsg_len(nlh) < sizeof(*ifm)) 14574685962SThomas Graf return -EINVAL; 14674685962SThomas Graf 14774685962SThomas Graf ifm = nlmsg_data(nlh); 14811dc1f36SStephen Hemminger if (ifm->ifi_family != AF_BRIDGE) 14911dc1f36SStephen Hemminger return -EPFNOSUPPORT; 15011dc1f36SStephen Hemminger 15174685962SThomas Graf protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); 15274685962SThomas Graf if (!protinfo || nla_len(protinfo) < sizeof(u8)) 15311dc1f36SStephen Hemminger return -EINVAL; 15411dc1f36SStephen Hemminger 15574685962SThomas Graf new_state = nla_get_u8(protinfo); 15611dc1f36SStephen Hemminger if (new_state > BR_STATE_BLOCKING) 15711dc1f36SStephen Hemminger return -EINVAL; 15811dc1f36SStephen Hemminger 15911dc1f36SStephen Hemminger dev = __dev_get_by_index(ifm->ifi_index); 16011dc1f36SStephen Hemminger if (!dev) 16111dc1f36SStephen Hemminger return -ENODEV; 16211dc1f36SStephen Hemminger 16311dc1f36SStephen Hemminger p = dev->br_port; 16411dc1f36SStephen Hemminger if (!p) 16511dc1f36SStephen Hemminger return -EINVAL; 16611dc1f36SStephen Hemminger 16711dc1f36SStephen Hemminger /* if kernel STP is running, don't allow changes */ 16811dc1f36SStephen Hemminger if (p->br->stp_enabled) 16911dc1f36SStephen Hemminger return -EBUSY; 17011dc1f36SStephen Hemminger 17174685962SThomas Graf if (!netif_running(dev) || 17274685962SThomas Graf (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)) 17311dc1f36SStephen Hemminger return -ENETDOWN; 17411dc1f36SStephen Hemminger 17511dc1f36SStephen Hemminger p->state = new_state; 17611dc1f36SStephen Hemminger br_log_state(p); 17711dc1f36SStephen Hemminger return 0; 17811dc1f36SStephen Hemminger } 17911dc1f36SStephen Hemminger 18011dc1f36SStephen Hemminger 181*32fe21c0SThomas Graf int __init br_netlink_init(void) 18211dc1f36SStephen Hemminger { 183*32fe21c0SThomas Graf if (__rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, br_dump_ifinfo)) 184*32fe21c0SThomas Graf return -ENOBUFS; 185*32fe21c0SThomas Graf 186*32fe21c0SThomas Graf /* Only the first call to __rtnl_register can fail */ 187*32fe21c0SThomas Graf __rtnl_register(PF_BRIDGE, RTM_SETLINK, br_rtm_setlink, NULL); 188*32fe21c0SThomas Graf 189*32fe21c0SThomas Graf return 0; 19011dc1f36SStephen Hemminger } 19111dc1f36SStephen Hemminger 19211dc1f36SStephen Hemminger void __exit br_netlink_fini(void) 19311dc1f36SStephen Hemminger { 194*32fe21c0SThomas Graf rtnl_unregister_all(PF_BRIDGE); 19511dc1f36SStephen Hemminger } 19611dc1f36SStephen Hemminger 197