diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-17 16:15:55 -0500 |
commit | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (patch) | |
tree | a8f4d49d63b1ecc92f2fddceba0655b2472c5bd9 /net/bridge/br_netlink.c | |
parent | 406089d01562f1e2bf9f089fd7637009ebaad589 (diff) |
Patched in Tegra support.
Diffstat (limited to 'net/bridge/br_netlink.c')
-rw-r--r-- | net/bridge/br_netlink.c | 276 |
1 files changed, 108 insertions, 168 deletions
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 5dc66abcc9e..e5f9ece3c9a 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c | |||
@@ -18,46 +18,17 @@ | |||
18 | #include <net/sock.h> | 18 | #include <net/sock.h> |
19 | 19 | ||
20 | #include "br_private.h" | 20 | #include "br_private.h" |
21 | #include "br_private_stp.h" | ||
22 | |||
23 | static inline size_t br_port_info_size(void) | ||
24 | { | ||
25 | return nla_total_size(1) /* IFLA_BRPORT_STATE */ | ||
26 | + nla_total_size(2) /* IFLA_BRPORT_PRIORITY */ | ||
27 | + nla_total_size(4) /* IFLA_BRPORT_COST */ | ||
28 | + nla_total_size(1) /* IFLA_BRPORT_MODE */ | ||
29 | + nla_total_size(1) /* IFLA_BRPORT_GUARD */ | ||
30 | + nla_total_size(1) /* IFLA_BRPORT_PROTECT */ | ||
31 | + 0; | ||
32 | } | ||
33 | 21 | ||
34 | static inline size_t br_nlmsg_size(void) | 22 | static inline size_t br_nlmsg_size(void) |
35 | { | 23 | { |
36 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) | 24 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) |
37 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ | 25 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ |
38 | + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ | 26 | + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ |
39 | + nla_total_size(4) /* IFLA_MASTER */ | 27 | + nla_total_size(4) /* IFLA_MASTER */ |
40 | + nla_total_size(4) /* IFLA_MTU */ | 28 | + nla_total_size(4) /* IFLA_MTU */ |
41 | + nla_total_size(4) /* IFLA_LINK */ | 29 | + nla_total_size(4) /* IFLA_LINK */ |
42 | + nla_total_size(1) /* IFLA_OPERSTATE */ | 30 | + nla_total_size(1) /* IFLA_OPERSTATE */ |
43 | + nla_total_size(br_port_info_size()); /* IFLA_PROTINFO */ | 31 | + nla_total_size(1); /* IFLA_PROTINFO */ |
44 | } | ||
45 | |||
46 | static int br_port_fill_attrs(struct sk_buff *skb, | ||
47 | const struct net_bridge_port *p) | ||
48 | { | ||
49 | u8 mode = !!(p->flags & BR_HAIRPIN_MODE); | ||
50 | |||
51 | if (nla_put_u8(skb, IFLA_BRPORT_STATE, p->state) || | ||
52 | nla_put_u16(skb, IFLA_BRPORT_PRIORITY, p->priority) || | ||
53 | nla_put_u32(skb, IFLA_BRPORT_COST, p->path_cost) || | ||
54 | nla_put_u8(skb, IFLA_BRPORT_MODE, mode) || | ||
55 | nla_put_u8(skb, IFLA_BRPORT_GUARD, !!(p->flags & BR_BPDU_GUARD)) || | ||
56 | nla_put_u8(skb, IFLA_BRPORT_PROTECT, !!(p->flags & BR_ROOT_BLOCK)) || | ||
57 | nla_put_u8(skb, IFLA_BRPORT_FAST_LEAVE, !!(p->flags & BR_MULTICAST_FAST_LEAVE))) | ||
58 | return -EMSGSIZE; | ||
59 | |||
60 | return 0; | ||
61 | } | 32 | } |
62 | 33 | ||
63 | /* | 34 | /* |
@@ -88,24 +59,19 @@ static int br_fill_ifinfo(struct sk_buff *skb, const struct net_bridge_port *por | |||
88 | hdr->ifi_flags = dev_get_flags(dev); | 59 | hdr->ifi_flags = dev_get_flags(dev); |
89 | hdr->ifi_change = 0; | 60 | hdr->ifi_change = 0; |
90 | 61 | ||
91 | if (nla_put_string(skb, IFLA_IFNAME, dev->name) || | 62 | NLA_PUT_STRING(skb, IFLA_IFNAME, dev->name); |
92 | nla_put_u32(skb, IFLA_MASTER, br->dev->ifindex) || | 63 | NLA_PUT_U32(skb, IFLA_MASTER, br->dev->ifindex); |
93 | nla_put_u32(skb, IFLA_MTU, dev->mtu) || | 64 | NLA_PUT_U32(skb, IFLA_MTU, dev->mtu); |
94 | nla_put_u8(skb, IFLA_OPERSTATE, operstate) || | 65 | NLA_PUT_U8(skb, IFLA_OPERSTATE, operstate); |
95 | (dev->addr_len && | 66 | |
96 | nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || | 67 | if (dev->addr_len) |
97 | (dev->ifindex != dev->iflink && | 68 | NLA_PUT(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr); |
98 | nla_put_u32(skb, IFLA_LINK, dev->iflink))) | 69 | |
99 | goto nla_put_failure; | 70 | if (dev->ifindex != dev->iflink) |
100 | 71 | NLA_PUT_U32(skb, IFLA_LINK, dev->iflink); | |
101 | if (event == RTM_NEWLINK) { | 72 | |
102 | struct nlattr *nest | 73 | if (event == RTM_NEWLINK) |
103 | = nla_nest_start(skb, IFLA_PROTINFO | NLA_F_NESTED); | 74 | NLA_PUT_U8(skb, IFLA_PROTINFO, port->state); |
104 | |||
105 | if (nest == NULL || br_port_fill_attrs(skb, port) < 0) | ||
106 | goto nla_put_failure; | ||
107 | nla_nest_end(skb, nest); | ||
108 | } | ||
109 | 75 | ||
110 | return nlmsg_end(skb, nlh); | 76 | return nlmsg_end(skb, nlh); |
111 | 77 | ||
@@ -124,7 +90,7 @@ void br_ifinfo_notify(int event, struct net_bridge_port *port) | |||
124 | int err = -ENOBUFS; | 90 | int err = -ENOBUFS; |
125 | 91 | ||
126 | br_debug(port->br, "port %u(%s) event %d\n", | 92 | br_debug(port->br, "port %u(%s) event %d\n", |
127 | (unsigned int)port->port_no, port->dev->name, event); | 93 | (unsigned)port->port_no, port->dev->name, event); |
128 | 94 | ||
129 | skb = nlmsg_new(br_nlmsg_size(), GFP_ATOMIC); | 95 | skb = nlmsg_new(br_nlmsg_size(), GFP_ATOMIC); |
130 | if (skb == NULL) | 96 | if (skb == NULL) |
@@ -147,134 +113,84 @@ errout: | |||
147 | /* | 113 | /* |
148 | * Dump information about all ports, in response to GETLINK | 114 | * Dump information about all ports, in response to GETLINK |
149 | */ | 115 | */ |
150 | int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, | 116 | static int br_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) |
151 | struct net_device *dev) | ||
152 | { | ||
153 | int err = 0; | ||
154 | struct net_bridge_port *port = br_port_get_rcu(dev); | ||
155 | |||
156 | /* not a bridge port */ | ||
157 | if (!port) | ||
158 | goto out; | ||
159 | |||
160 | err = br_fill_ifinfo(skb, port, pid, seq, RTM_NEWLINK, NLM_F_MULTI); | ||
161 | out: | ||
162 | return err; | ||
163 | } | ||
164 | |||
165 | static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { | ||
166 | [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, | ||
167 | [IFLA_BRPORT_COST] = { .type = NLA_U32 }, | ||
168 | [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, | ||
169 | [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, | ||
170 | [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, | ||
171 | [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, | ||
172 | }; | ||
173 | |||
174 | /* Change the state of the port and notify spanning tree */ | ||
175 | static int br_set_port_state(struct net_bridge_port *p, u8 state) | ||
176 | { | ||
177 | if (state > BR_STATE_BLOCKING) | ||
178 | return -EINVAL; | ||
179 | |||
180 | /* if kernel STP is running, don't allow changes */ | ||
181 | if (p->br->stp_enabled == BR_KERNEL_STP) | ||
182 | return -EBUSY; | ||
183 | |||
184 | if (!netif_running(p->dev) || | ||
185 | (!netif_carrier_ok(p->dev) && state != BR_STATE_DISABLED)) | ||
186 | return -ENETDOWN; | ||
187 | |||
188 | p->state = state; | ||
189 | br_log_state(p); | ||
190 | br_port_state_selection(p->br); | ||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | /* Set/clear or port flags based on attribute */ | ||
195 | static void br_set_port_flag(struct net_bridge_port *p, struct nlattr *tb[], | ||
196 | int attrtype, unsigned long mask) | ||
197 | { | 117 | { |
198 | if (tb[attrtype]) { | 118 | struct net *net = sock_net(skb->sk); |
199 | u8 flag = nla_get_u8(tb[attrtype]); | 119 | struct net_device *dev; |
200 | if (flag) | 120 | int idx; |
201 | p->flags |= mask; | 121 | |
202 | else | 122 | idx = 0; |
203 | p->flags &= ~mask; | 123 | rcu_read_lock(); |
124 | for_each_netdev_rcu(net, dev) { | ||
125 | struct net_bridge_port *port = br_port_get_rcu(dev); | ||
126 | |||
127 | /* not a bridge port */ | ||
128 | if (!port || idx < cb->args[0]) | ||
129 | goto skip; | ||
130 | |||
131 | if (br_fill_ifinfo(skb, port, | ||
132 | NETLINK_CB(cb->skb).pid, | ||
133 | cb->nlh->nlmsg_seq, RTM_NEWLINK, | ||
134 | NLM_F_MULTI) < 0) | ||
135 | break; | ||
136 | skip: | ||
137 | ++idx; | ||
204 | } | 138 | } |
205 | } | 139 | rcu_read_unlock(); |
140 | cb->args[0] = idx; | ||
206 | 141 | ||
207 | /* Process bridge protocol info on port */ | 142 | return skb->len; |
208 | static int br_setport(struct net_bridge_port *p, struct nlattr *tb[]) | ||
209 | { | ||
210 | int err; | ||
211 | |||
212 | br_set_port_flag(p, tb, IFLA_BRPORT_MODE, BR_HAIRPIN_MODE); | ||
213 | br_set_port_flag(p, tb, IFLA_BRPORT_GUARD, BR_BPDU_GUARD); | ||
214 | br_set_port_flag(p, tb, IFLA_BRPORT_FAST_LEAVE, BR_MULTICAST_FAST_LEAVE); | ||
215 | |||
216 | if (tb[IFLA_BRPORT_COST]) { | ||
217 | err = br_stp_set_path_cost(p, nla_get_u32(tb[IFLA_BRPORT_COST])); | ||
218 | if (err) | ||
219 | return err; | ||
220 | } | ||
221 | |||
222 | if (tb[IFLA_BRPORT_PRIORITY]) { | ||
223 | err = br_stp_set_port_priority(p, nla_get_u16(tb[IFLA_BRPORT_PRIORITY])); | ||
224 | if (err) | ||
225 | return err; | ||
226 | } | ||
227 | |||
228 | if (tb[IFLA_BRPORT_STATE]) { | ||
229 | err = br_set_port_state(p, nla_get_u8(tb[IFLA_BRPORT_STATE])); | ||
230 | if (err) | ||
231 | return err; | ||
232 | } | ||
233 | return 0; | ||
234 | } | 143 | } |
235 | 144 | ||
236 | /* Change state and parameters on port. */ | 145 | /* |
237 | int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) | 146 | * Change state of port (ie from forwarding to blocking etc) |
147 | * Used by spanning tree in user space. | ||
148 | */ | ||
149 | static int br_rtm_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | ||
238 | { | 150 | { |
151 | struct net *net = sock_net(skb->sk); | ||
239 | struct ifinfomsg *ifm; | 152 | struct ifinfomsg *ifm; |
240 | struct nlattr *protinfo; | 153 | struct nlattr *protinfo; |
154 | struct net_device *dev; | ||
241 | struct net_bridge_port *p; | 155 | struct net_bridge_port *p; |
242 | struct nlattr *tb[IFLA_BRPORT_MAX + 1]; | 156 | u8 new_state; |
243 | int err; | 157 | |
158 | if (nlmsg_len(nlh) < sizeof(*ifm)) | ||
159 | return -EINVAL; | ||
244 | 160 | ||
245 | ifm = nlmsg_data(nlh); | 161 | ifm = nlmsg_data(nlh); |
162 | if (ifm->ifi_family != AF_BRIDGE) | ||
163 | return -EPFNOSUPPORT; | ||
246 | 164 | ||
247 | protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); | 165 | protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); |
248 | if (!protinfo) | 166 | if (!protinfo || nla_len(protinfo) < sizeof(u8)) |
249 | return 0; | 167 | return -EINVAL; |
168 | |||
169 | new_state = nla_get_u8(protinfo); | ||
170 | if (new_state > BR_STATE_BLOCKING) | ||
171 | return -EINVAL; | ||
172 | |||
173 | dev = __dev_get_by_index(net, ifm->ifi_index); | ||
174 | if (!dev) | ||
175 | return -ENODEV; | ||
250 | 176 | ||
251 | p = br_port_get_rtnl(dev); | 177 | p = br_port_get_rtnl(dev); |
252 | if (!p) | 178 | if (!p) |
253 | return -EINVAL; | 179 | return -EINVAL; |
254 | 180 | ||
255 | if (protinfo->nla_type & NLA_F_NESTED) { | 181 | /* if kernel STP is running, don't allow changes */ |
256 | err = nla_parse_nested(tb, IFLA_BRPORT_MAX, | 182 | if (p->br->stp_enabled == BR_KERNEL_STP) |
257 | protinfo, ifla_brport_policy); | 183 | return -EBUSY; |
258 | if (err) | ||
259 | return err; | ||
260 | |||
261 | spin_lock_bh(&p->br->lock); | ||
262 | err = br_setport(p, tb); | ||
263 | spin_unlock_bh(&p->br->lock); | ||
264 | } else { | ||
265 | /* Binary compatability with old RSTP */ | ||
266 | if (nla_len(protinfo) < sizeof(u8)) | ||
267 | return -EINVAL; | ||
268 | 184 | ||
269 | spin_lock_bh(&p->br->lock); | 185 | if (!netif_running(dev) || |
270 | err = br_set_port_state(p, nla_get_u8(protinfo)); | 186 | (!netif_carrier_ok(dev) && new_state != BR_STATE_DISABLED)) |
271 | spin_unlock_bh(&p->br->lock); | 187 | return -ENETDOWN; |
272 | } | ||
273 | 188 | ||
274 | if (err == 0) | 189 | p->state = new_state; |
275 | br_ifinfo_notify(RTM_NEWLINK, p); | 190 | br_log_state(p); |
191 | br_ifinfo_notify(RTM_NEWLINK, p); | ||
276 | 192 | ||
277 | return err; | 193 | return 0; |
278 | } | 194 | } |
279 | 195 | ||
280 | static int br_validate(struct nlattr *tb[], struct nlattr *data[]) | 196 | static int br_validate(struct nlattr *tb[], struct nlattr *data[]) |
@@ -289,7 +205,7 @@ static int br_validate(struct nlattr *tb[], struct nlattr *data[]) | |||
289 | return 0; | 205 | return 0; |
290 | } | 206 | } |
291 | 207 | ||
292 | struct rtnl_link_ops br_link_ops __read_mostly = { | 208 | static struct rtnl_link_ops br_link_ops __read_mostly = { |
293 | .kind = "bridge", | 209 | .kind = "bridge", |
294 | .priv_size = sizeof(struct net_bridge), | 210 | .priv_size = sizeof(struct net_bridge), |
295 | .setup = br_dev_setup, | 211 | .setup = br_dev_setup, |
@@ -301,19 +217,43 @@ int __init br_netlink_init(void) | |||
301 | { | 217 | { |
302 | int err; | 218 | int err; |
303 | 219 | ||
304 | br_mdb_init(); | ||
305 | err = rtnl_link_register(&br_link_ops); | 220 | err = rtnl_link_register(&br_link_ops); |
221 | if (err < 0) | ||
222 | goto err1; | ||
223 | |||
224 | err = __rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, | ||
225 | br_dump_ifinfo, NULL); | ||
226 | if (err) | ||
227 | goto err2; | ||
228 | err = __rtnl_register(PF_BRIDGE, RTM_SETLINK, | ||
229 | br_rtm_setlink, NULL, NULL); | ||
230 | if (err) | ||
231 | goto err3; | ||
232 | err = __rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, | ||
233 | br_fdb_add, NULL, NULL); | ||
306 | if (err) | 234 | if (err) |
307 | goto out; | 235 | goto err3; |
236 | err = __rtnl_register(PF_BRIDGE, RTM_DELNEIGH, | ||
237 | br_fdb_delete, NULL, NULL); | ||
238 | if (err) | ||
239 | goto err3; | ||
240 | err = __rtnl_register(PF_BRIDGE, RTM_GETNEIGH, | ||
241 | NULL, br_fdb_dump, NULL); | ||
242 | if (err) | ||
243 | goto err3; | ||
308 | 244 | ||
309 | return 0; | 245 | return 0; |
310 | out: | 246 | |
311 | br_mdb_uninit(); | 247 | err3: |
248 | rtnl_unregister_all(PF_BRIDGE); | ||
249 | err2: | ||
250 | rtnl_link_unregister(&br_link_ops); | ||
251 | err1: | ||
312 | return err; | 252 | return err; |
313 | } | 253 | } |
314 | 254 | ||
315 | void __exit br_netlink_fini(void) | 255 | void __exit br_netlink_fini(void) |
316 | { | 256 | { |
317 | br_mdb_uninit(); | ||
318 | rtnl_link_unregister(&br_link_ops); | 257 | rtnl_link_unregister(&br_link_ops); |
258 | rtnl_unregister_all(PF_BRIDGE); | ||
319 | } | 259 | } |