diff options
author | Vlad Yasevich <vyasevic@redhat.com> | 2013-02-13 07:00:12 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-02-13 19:41:46 -0500 |
commit | 407af3299ef1ac7e87ce3fb530e32a009d1a9efd (patch) | |
tree | 9572e0b02eb9f6c2952b490f3a79f4b790fcea3c /net | |
parent | 85f46c6baef1486ce20e13dd7cdea5dd15be2a90 (diff) |
bridge: Add netlink interface to configure vlans on bridge ports
Add a netlink interface to add and remove vlan configuration on bridge port.
The interface uses the RTM_SETLINK message and encodes the vlan
configuration inside the IFLA_AF_SPEC. It is possble to include multiple
vlans to either add or remove in a single message.
Signed-off-by: Vlad Yasevich <vyasevic@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/bridge/br_device.c | 1 | ||||
-rw-r--r-- | net/bridge/br_if.c | 1 | ||||
-rw-r--r-- | net/bridge/br_netlink.c | 139 | ||||
-rw-r--r-- | net/bridge/br_private.h | 1 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 72 |
5 files changed, 196 insertions, 18 deletions
diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 35a2c2c84f33..091bedf266a0 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c | |||
@@ -316,6 +316,7 @@ static const struct net_device_ops br_netdev_ops = { | |||
316 | .ndo_fdb_dump = br_fdb_dump, | 316 | .ndo_fdb_dump = br_fdb_dump, |
317 | .ndo_bridge_getlink = br_getlink, | 317 | .ndo_bridge_getlink = br_getlink, |
318 | .ndo_bridge_setlink = br_setlink, | 318 | .ndo_bridge_setlink = br_setlink, |
319 | .ndo_bridge_dellink = br_dellink, | ||
319 | }; | 320 | }; |
320 | 321 | ||
321 | static void br_dev_free(struct net_device *dev) | 322 | static void br_dev_free(struct net_device *dev) |
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index af9d65ab4001..335c60cebfd1 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/if_ether.h> | 23 | #include <linux/if_ether.h> |
24 | #include <linux/slab.h> | 24 | #include <linux/slab.h> |
25 | #include <net/sock.h> | 25 | #include <net/sock.h> |
26 | #include <linux/if_vlan.h> | ||
26 | 27 | ||
27 | #include "br_private.h" | 28 | #include "br_private.h" |
28 | 29 | ||
diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 39ca9796f3f7..534a9f4587a9 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <net/rtnetlink.h> | 16 | #include <net/rtnetlink.h> |
17 | #include <net/net_namespace.h> | 17 | #include <net/net_namespace.h> |
18 | #include <net/sock.h> | 18 | #include <net/sock.h> |
19 | #include <uapi/linux/if_bridge.h> | ||
19 | 20 | ||
20 | #include "br_private.h" | 21 | #include "br_private.h" |
21 | #include "br_private_stp.h" | 22 | #include "br_private_stp.h" |
@@ -119,10 +120,14 @@ nla_put_failure: | |||
119 | */ | 120 | */ |
120 | void br_ifinfo_notify(int event, struct net_bridge_port *port) | 121 | void br_ifinfo_notify(int event, struct net_bridge_port *port) |
121 | { | 122 | { |
122 | struct net *net = dev_net(port->dev); | 123 | struct net *net; |
123 | struct sk_buff *skb; | 124 | struct sk_buff *skb; |
124 | int err = -ENOBUFS; | 125 | int err = -ENOBUFS; |
125 | 126 | ||
127 | if (!port) | ||
128 | return; | ||
129 | |||
130 | net = dev_net(port->dev); | ||
126 | br_debug(port->br, "port %u(%s) event %d\n", | 131 | br_debug(port->br, "port %u(%s) event %d\n", |
127 | (unsigned int)port->port_no, port->dev->name, event); | 132 | (unsigned int)port->port_no, port->dev->name, event); |
128 | 133 | ||
@@ -144,6 +149,7 @@ errout: | |||
144 | rtnl_set_sk_err(net, RTNLGRP_LINK, err); | 149 | rtnl_set_sk_err(net, RTNLGRP_LINK, err); |
145 | } | 150 | } |
146 | 151 | ||
152 | |||
147 | /* | 153 | /* |
148 | * Dump information about all ports, in response to GETLINK | 154 | * Dump information about all ports, in response to GETLINK |
149 | */ | 155 | */ |
@@ -162,6 +168,64 @@ out: | |||
162 | return err; | 168 | return err; |
163 | } | 169 | } |
164 | 170 | ||
171 | const struct nla_policy ifla_br_policy[IFLA_MAX+1] = { | ||
172 | [IFLA_BRIDGE_FLAGS] = { .type = NLA_U16 }, | ||
173 | [IFLA_BRIDGE_MODE] = { .type = NLA_U16 }, | ||
174 | [IFLA_BRIDGE_VLAN_INFO] = { .type = NLA_BINARY, | ||
175 | .len = sizeof(struct bridge_vlan_info), }, | ||
176 | }; | ||
177 | |||
178 | static int br_afspec(struct net_bridge *br, | ||
179 | struct net_bridge_port *p, | ||
180 | struct nlattr *af_spec, | ||
181 | int cmd) | ||
182 | { | ||
183 | struct nlattr *tb[IFLA_BRIDGE_MAX+1]; | ||
184 | int err = 0; | ||
185 | |||
186 | err = nla_parse_nested(tb, IFLA_BRIDGE_MAX, af_spec, ifla_br_policy); | ||
187 | if (err) | ||
188 | return err; | ||
189 | |||
190 | if (tb[IFLA_BRIDGE_VLAN_INFO]) { | ||
191 | struct bridge_vlan_info *vinfo; | ||
192 | |||
193 | vinfo = nla_data(tb[IFLA_BRIDGE_VLAN_INFO]); | ||
194 | |||
195 | if (vinfo->vid >= VLAN_N_VID) | ||
196 | return -EINVAL; | ||
197 | |||
198 | switch (cmd) { | ||
199 | case RTM_SETLINK: | ||
200 | if (p) { | ||
201 | err = nbp_vlan_add(p, vinfo->vid); | ||
202 | if (err) | ||
203 | break; | ||
204 | |||
205 | if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) | ||
206 | err = br_vlan_add(p->br, vinfo->vid); | ||
207 | } else | ||
208 | err = br_vlan_add(br, vinfo->vid); | ||
209 | |||
210 | if (err) | ||
211 | break; | ||
212 | |||
213 | break; | ||
214 | |||
215 | case RTM_DELLINK: | ||
216 | if (p) { | ||
217 | nbp_vlan_delete(p, vinfo->vid); | ||
218 | if (vinfo->flags & BRIDGE_VLAN_INFO_MASTER) | ||
219 | br_vlan_delete(p->br, vinfo->vid); | ||
220 | } else | ||
221 | br_vlan_delete(br, vinfo->vid); | ||
222 | break; | ||
223 | } | ||
224 | } | ||
225 | |||
226 | return err; | ||
227 | } | ||
228 | |||
165 | static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { | 229 | static const struct nla_policy ifla_brport_policy[IFLA_BRPORT_MAX + 1] = { |
166 | [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, | 230 | [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, |
167 | [IFLA_BRPORT_COST] = { .type = NLA_U32 }, | 231 | [IFLA_BRPORT_COST] = { .type = NLA_U32 }, |
@@ -241,6 +305,7 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) | |||
241 | { | 305 | { |
242 | struct ifinfomsg *ifm; | 306 | struct ifinfomsg *ifm; |
243 | struct nlattr *protinfo; | 307 | struct nlattr *protinfo; |
308 | struct nlattr *afspec; | ||
244 | struct net_bridge_port *p; | 309 | struct net_bridge_port *p; |
245 | struct nlattr *tb[IFLA_BRPORT_MAX + 1]; | 310 | struct nlattr *tb[IFLA_BRPORT_MAX + 1]; |
246 | int err; | 311 | int err; |
@@ -248,38 +313,76 @@ int br_setlink(struct net_device *dev, struct nlmsghdr *nlh) | |||
248 | ifm = nlmsg_data(nlh); | 313 | ifm = nlmsg_data(nlh); |
249 | 314 | ||
250 | protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); | 315 | protinfo = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_PROTINFO); |
251 | if (!protinfo) | 316 | afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC); |
317 | if (!protinfo && !afspec) | ||
252 | return 0; | 318 | return 0; |
253 | 319 | ||
254 | p = br_port_get_rtnl(dev); | 320 | p = br_port_get_rtnl(dev); |
255 | if (!p) | 321 | /* We want to accept dev as bridge itself if the AF_SPEC |
322 | * is set to see if someone is setting vlan info on the brigde | ||
323 | */ | ||
324 | if (!p && ((dev->priv_flags & IFF_EBRIDGE) && !afspec)) | ||
256 | return -EINVAL; | 325 | return -EINVAL; |
257 | 326 | ||
258 | if (protinfo->nla_type & NLA_F_NESTED) { | 327 | if (p && protinfo) { |
259 | err = nla_parse_nested(tb, IFLA_BRPORT_MAX, | 328 | if (protinfo->nla_type & NLA_F_NESTED) { |
260 | protinfo, ifla_brport_policy); | 329 | err = nla_parse_nested(tb, IFLA_BRPORT_MAX, |
330 | protinfo, ifla_brport_policy); | ||
331 | if (err) | ||
332 | return err; | ||
333 | |||
334 | spin_lock_bh(&p->br->lock); | ||
335 | err = br_setport(p, tb); | ||
336 | spin_unlock_bh(&p->br->lock); | ||
337 | } else { | ||
338 | /* Binary compatability with old RSTP */ | ||
339 | if (nla_len(protinfo) < sizeof(u8)) | ||
340 | return -EINVAL; | ||
341 | |||
342 | spin_lock_bh(&p->br->lock); | ||
343 | err = br_set_port_state(p, nla_get_u8(protinfo)); | ||
344 | spin_unlock_bh(&p->br->lock); | ||
345 | } | ||
261 | if (err) | 346 | if (err) |
262 | return err; | 347 | goto out; |
263 | 348 | } | |
264 | spin_lock_bh(&p->br->lock); | ||
265 | err = br_setport(p, tb); | ||
266 | spin_unlock_bh(&p->br->lock); | ||
267 | } else { | ||
268 | /* Binary compatability with old RSTP */ | ||
269 | if (nla_len(protinfo) < sizeof(u8)) | ||
270 | return -EINVAL; | ||
271 | 349 | ||
272 | spin_lock_bh(&p->br->lock); | 350 | if (afspec) { |
273 | err = br_set_port_state(p, nla_get_u8(protinfo)); | 351 | err = br_afspec((struct net_bridge *)netdev_priv(dev), p, |
274 | spin_unlock_bh(&p->br->lock); | 352 | afspec, RTM_SETLINK); |
275 | } | 353 | } |
276 | 354 | ||
277 | if (err == 0) | 355 | if (err == 0) |
278 | br_ifinfo_notify(RTM_NEWLINK, p); | 356 | br_ifinfo_notify(RTM_NEWLINK, p); |
279 | 357 | ||
358 | out: | ||
280 | return err; | 359 | return err; |
281 | } | 360 | } |
282 | 361 | ||
362 | /* Delete port information */ | ||
363 | int br_dellink(struct net_device *dev, struct nlmsghdr *nlh) | ||
364 | { | ||
365 | struct ifinfomsg *ifm; | ||
366 | struct nlattr *afspec; | ||
367 | struct net_bridge_port *p; | ||
368 | int err; | ||
369 | |||
370 | ifm = nlmsg_data(nlh); | ||
371 | |||
372 | afspec = nlmsg_find_attr(nlh, sizeof(*ifm), IFLA_AF_SPEC); | ||
373 | if (!afspec) | ||
374 | return 0; | ||
375 | |||
376 | p = br_port_get_rtnl(dev); | ||
377 | /* We want to accept dev as bridge itself as well */ | ||
378 | if (!p && !(dev->priv_flags & IFF_EBRIDGE)) | ||
379 | return -EINVAL; | ||
380 | |||
381 | err = br_afspec((struct net_bridge *)netdev_priv(dev), p, | ||
382 | afspec, RTM_DELLINK); | ||
383 | |||
384 | return err; | ||
385 | } | ||
283 | static int br_validate(struct nlattr *tb[], struct nlattr *data[]) | 386 | static int br_validate(struct nlattr *tb[], struct nlattr *data[]) |
284 | { | 387 | { |
285 | if (tb[IFLA_ADDRESS]) { | 388 | if (tb[IFLA_ADDRESS]) { |
diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index f0f24610d111..a42f9d49a64e 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h | |||
@@ -713,6 +713,7 @@ extern int br_netlink_init(void); | |||
713 | extern void br_netlink_fini(void); | 713 | extern void br_netlink_fini(void); |
714 | extern void br_ifinfo_notify(int event, struct net_bridge_port *port); | 714 | extern void br_ifinfo_notify(int event, struct net_bridge_port *port); |
715 | extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); | 715 | extern int br_setlink(struct net_device *dev, struct nlmsghdr *nlmsg); |
716 | extern int br_dellink(struct net_device *dev, struct nlmsghdr *nlmsg); | ||
716 | extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, | 717 | extern int br_getlink(struct sk_buff *skb, u32 pid, u32 seq, |
717 | struct net_device *dev); | 718 | struct net_device *dev); |
718 | 719 | ||
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c1e4db60eeca..2c9ccbfbd93c 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -2464,6 +2464,77 @@ out: | |||
2464 | return err; | 2464 | return err; |
2465 | } | 2465 | } |
2466 | 2466 | ||
2467 | static int rtnl_bridge_dellink(struct sk_buff *skb, struct nlmsghdr *nlh, | ||
2468 | void *arg) | ||
2469 | { | ||
2470 | struct net *net = sock_net(skb->sk); | ||
2471 | struct ifinfomsg *ifm; | ||
2472 | struct net_device *dev; | ||
2473 | struct nlattr *br_spec, *attr = NULL; | ||
2474 | int rem, err = -EOPNOTSUPP; | ||
2475 | u16 oflags, flags = 0; | ||
2476 | bool have_flags = false; | ||
2477 | |||
2478 | if (nlmsg_len(nlh) < sizeof(*ifm)) | ||
2479 | return -EINVAL; | ||
2480 | |||
2481 | ifm = nlmsg_data(nlh); | ||
2482 | if (ifm->ifi_family != AF_BRIDGE) | ||
2483 | return -EPFNOSUPPORT; | ||
2484 | |||
2485 | dev = __dev_get_by_index(net, ifm->ifi_index); | ||
2486 | if (!dev) { | ||
2487 | pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n"); | ||
2488 | return -ENODEV; | ||
2489 | } | ||
2490 | |||
2491 | br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); | ||
2492 | if (br_spec) { | ||
2493 | nla_for_each_nested(attr, br_spec, rem) { | ||
2494 | if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { | ||
2495 | have_flags = true; | ||
2496 | flags = nla_get_u16(attr); | ||
2497 | break; | ||
2498 | } | ||
2499 | } | ||
2500 | } | ||
2501 | |||
2502 | oflags = flags; | ||
2503 | |||
2504 | if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { | ||
2505 | struct net_device *br_dev = netdev_master_upper_dev_get(dev); | ||
2506 | |||
2507 | if (!br_dev || !br_dev->netdev_ops->ndo_bridge_dellink) { | ||
2508 | err = -EOPNOTSUPP; | ||
2509 | goto out; | ||
2510 | } | ||
2511 | |||
2512 | err = br_dev->netdev_ops->ndo_bridge_dellink(dev, nlh); | ||
2513 | if (err) | ||
2514 | goto out; | ||
2515 | |||
2516 | flags &= ~BRIDGE_FLAGS_MASTER; | ||
2517 | } | ||
2518 | |||
2519 | if ((flags & BRIDGE_FLAGS_SELF)) { | ||
2520 | if (!dev->netdev_ops->ndo_bridge_dellink) | ||
2521 | err = -EOPNOTSUPP; | ||
2522 | else | ||
2523 | err = dev->netdev_ops->ndo_bridge_dellink(dev, nlh); | ||
2524 | |||
2525 | if (!err) | ||
2526 | flags &= ~BRIDGE_FLAGS_SELF; | ||
2527 | } | ||
2528 | |||
2529 | if (have_flags) | ||
2530 | memcpy(nla_data(attr), &flags, sizeof(flags)); | ||
2531 | /* Generate event to notify upper layer of bridge change */ | ||
2532 | if (!err) | ||
2533 | err = rtnl_bridge_notify(dev, oflags); | ||
2534 | out: | ||
2535 | return err; | ||
2536 | } | ||
2537 | |||
2467 | /* Protected by RTNL sempahore. */ | 2538 | /* Protected by RTNL sempahore. */ |
2468 | static struct rtattr **rta_buf; | 2539 | static struct rtattr **rta_buf; |
2469 | static int rtattr_max; | 2540 | static int rtattr_max; |
@@ -2647,6 +2718,7 @@ void __init rtnetlink_init(void) | |||
2647 | rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); | 2718 | rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); |
2648 | 2719 | ||
2649 | rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL); | 2720 | rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL); |
2721 | rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL); | ||
2650 | rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL); | 2722 | rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL); |
2651 | } | 2723 | } |
2652 | 2724 | ||