| @@ -2295,13 +2295,60 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) · 2294 unmodified lines |
| 2295 | return skb->len; | 2295 | return skb->len; |
| 2296 | } | 2296 | } |
| 2297 | | 2297 | |
| | 2298 | static inline size_t bridge_nlmsg_size(void) |
| | 2299 | { |
| | 2300 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) |
| | 2301 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ |
| | 2302 | + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ |
| | 2303 | + nla_total_size(sizeof(u32)) /* IFLA_MASTER */ |
| | 2304 | + nla_total_size(sizeof(u32)) /* IFLA_MTU */ |
| | 2305 | + nla_total_size(sizeof(u32)) /* IFLA_LINK */ |
| | 2306 | + nla_total_size(sizeof(u32)) /* IFLA_OPERSTATE */ |
| | 2307 | + nla_total_size(sizeof(u8)) /* IFLA_PROTINFO */ |
| | 2308 | + nla_total_size(sizeof(struct nlattr)) /* IFLA_AF_SPEC */ |
| | 2309 | + nla_total_size(sizeof(u16)) /* IFLA_BRIDGE_FLAGS */ |
| | 2310 | + nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_MODE */ |
| | 2311 | } |
| | 2312 | |
| | 2313 | static int rtnl_bridge_notify(struct net_device *dev, u16 flags) |
| | 2314 | { |
| | 2315 | struct net *net = dev_net(dev); |
| | 2316 | struct net_device *master = dev->master; |
| | 2317 | struct sk_buff *skb; |
| | 2318 | int err = -EOPNOTSUPP; |
| | 2319 | |
| | 2320 | skb = nlmsg_new(bridge_nlmsg_size(), GFP_ATOMIC); |
| | 2321 | if (!skb) { |
| | 2322 | err = -ENOMEM; |
| | 2323 | goto errout; |
| | 2324 | } |
| | 2325 | |
| | 2326 | if (!flags && master && master->netdev_ops->ndo_bridge_getlink) |
| | 2327 | err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); |
| | 2328 | else if (dev->netdev_ops->ndo_bridge_getlink) |
| | 2329 | err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); |
| | 2330 | |
| | 2331 | if (err < 0) |
| | 2332 | goto errout; |
| | 2333 | |
| | 2334 | rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); |
| | 2335 | return 0; |
| | 2336 | errout: |
| | 2337 | WARN_ON(err == -EMSGSIZE); |
| | 2338 | kfree_skb(skb); |
| | 2339 | rtnl_set_sk_err(net, RTNLGRP_LINK, err); |
| | 2340 | return err; |
| | 2341 | } |
| | 2342 | |
| 2298 | static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, | 2343 | static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, |
| 2299 | void *arg) | 2344 | void *arg) |
| 2300 | { | 2345 | { |
| 2301 | struct net *net = sock_net(skb->sk); | 2346 | struct net *net = sock_net(skb->sk); |
| 2302 | struct ifinfomsg *ifm; | 2347 | struct ifinfomsg *ifm; |
| 2303 | struct net_device *dev; | 2348 | struct net_device *dev; |
| 2304 | int err = -EINVAL; | 2349 | struct nlattr *br_spec, *attr = NULL; |
| | 2350 | int rem, err = -EOPNOTSUPP; |
| | 2351 | u16 flags = 0; |
| 2305 | | 2352 | |
| 2306 | if (nlmsg_len(nlh) < sizeof(*ifm)) | 2353 | if (nlmsg_len(nlh) < sizeof(*ifm)) |
| 2307 | return -EINVAL; | 2354 | return -EINVAL; |
| @@ -2316,15 +2363,45 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, · 8 unmodified lines |
| 2316 | return -ENODEV; | 2363 | return -ENODEV; |
| 2317 | } | 2364 | } |
| 2318 | | 2365 | |
| 2319 | if (dev->master && dev->master->netdev_ops->ndo_bridge_setlink) { | 2366 | br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); |
| | 2367 | if (br_spec) { |
| | 2368 | nla_for_each_nested(attr, br_spec, rem) { |
| | 2369 | if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { |
| | 2370 | flags = nla_get_u16(attr); |
| | 2371 | break; |
| | 2372 | } |
| | 2373 | } |
| | 2374 | } |
| | 2375 | |
| | 2376 | if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { |
| | 2377 | if (!dev->master || |
| | 2378 | !dev->master->netdev_ops->ndo_bridge_setlink) { |
| | 2379 | err = -EOPNOTSUPP; |
| | 2380 | goto out; |
| | 2381 | } |
| | 2382 | |
| 2320 | err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh); | 2383 | err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh); |
| 2321 | if (err) | 2384 | if (err) |
| 2322 | goto out; | 2385 | goto out; |
| | 2386 | |
| | 2387 | flags &= ~BRIDGE_FLAGS_MASTER; |
| 2323 | } | 2388 | } |
| 2324 | | 2389 | |
| 2325 | if (dev->netdev_ops->ndo_bridge_setlink) | 2390 | if ((flags & BRIDGE_FLAGS_SELF)) { |
| 2326 | err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh); | 2391 | if (!dev->netdev_ops->ndo_bridge_setlink) |
| | 2392 | err = -EOPNOTSUPP; |
| | 2393 | else |
| | 2394 | err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh); |
| | 2395 | |
| | 2396 | if (!err) |
| | 2397 | flags &= ~BRIDGE_FLAGS_SELF; |
| | 2398 | } |
| 2327 | | 2399 | |
| | 2400 | if (attr && nla_type(attr) == IFLA_BRIDGE_FLAGS) |
| | 2401 | memcpy(nla_data(attr), &flags, sizeof(flags)); |
| | 2402 | /* Generate event to notify upper layer of bridge change */ |
| | 2403 | if (!err) |
| | 2404 | err = rtnl_bridge_notify(dev, flags); |
| 2328 | out: | 2405 | out: |
| 2329 | return err; | 2406 | return err; |
| 2330 | } | 2407 | } |