xref: /openbmc/linux/net/bridge/br_netlink.c (revision 32fe21c0c0a3091552fea8f2f7e4905f547a3433)
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