diff options
Diffstat (limited to 'net/core/rtnetlink.c')
| -rw-r--r-- | net/core/rtnetlink.c | 230 |
1 files changed, 224 insertions, 6 deletions
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index fad649ae4dec..1868625af25e 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
| @@ -128,7 +128,7 @@ static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex) | |||
| 128 | if (tab == NULL || tab[msgindex].doit == NULL) | 128 | if (tab == NULL || tab[msgindex].doit == NULL) |
| 129 | tab = rtnl_msg_handlers[PF_UNSPEC]; | 129 | tab = rtnl_msg_handlers[PF_UNSPEC]; |
| 130 | 130 | ||
| 131 | return tab ? tab[msgindex].doit : NULL; | 131 | return tab[msgindex].doit; |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) | 134 | static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) |
| @@ -143,7 +143,7 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) | |||
| 143 | if (tab == NULL || tab[msgindex].dumpit == NULL) | 143 | if (tab == NULL || tab[msgindex].dumpit == NULL) |
| 144 | tab = rtnl_msg_handlers[PF_UNSPEC]; | 144 | tab = rtnl_msg_handlers[PF_UNSPEC]; |
| 145 | 145 | ||
| 146 | return tab ? tab[msgindex].dumpit : NULL; | 146 | return tab[msgindex].dumpit; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex) | 149 | static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex) |
| @@ -158,7 +158,7 @@ static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex) | |||
| 158 | if (tab == NULL || tab[msgindex].calcit == NULL) | 158 | if (tab == NULL || tab[msgindex].calcit == NULL) |
| 159 | tab = rtnl_msg_handlers[PF_UNSPEC]; | 159 | tab = rtnl_msg_handlers[PF_UNSPEC]; |
| 160 | 160 | ||
| 161 | return tab ? tab[msgindex].calcit : NULL; | 161 | return tab[msgindex].calcit; |
| 162 | } | 162 | } |
| 163 | 163 | ||
| 164 | /** | 164 | /** |
| @@ -1316,6 +1316,10 @@ static int do_setlink(struct net_device *dev, struct ifinfomsg *ifm, | |||
| 1316 | err = PTR_ERR(net); | 1316 | err = PTR_ERR(net); |
| 1317 | goto errout; | 1317 | goto errout; |
| 1318 | } | 1318 | } |
| 1319 | if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) { | ||
| 1320 | err = -EPERM; | ||
| 1321 | goto errout; | ||
| 1322 | } | ||
| 1319 | err = dev_change_net_namespace(dev, net, ifname); | 1323 | err = dev_change_net_namespace(dev, net, ifname); |
| 1320 | put_net(net); | 1324 | put_net(net); |
| 1321 | if (err) | 1325 | if (err) |
| @@ -1638,7 +1642,7 @@ int rtnl_configure_link(struct net_device *dev, const struct ifinfomsg *ifm) | |||
| 1638 | } | 1642 | } |
| 1639 | EXPORT_SYMBOL(rtnl_configure_link); | 1643 | EXPORT_SYMBOL(rtnl_configure_link); |
| 1640 | 1644 | ||
| 1641 | struct net_device *rtnl_create_link(struct net *src_net, struct net *net, | 1645 | struct net_device *rtnl_create_link(struct net *net, |
| 1642 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) | 1646 | char *ifname, const struct rtnl_link_ops *ops, struct nlattr *tb[]) |
| 1643 | { | 1647 | { |
| 1644 | int err; | 1648 | int err; |
| @@ -1836,7 +1840,7 @@ replay: | |||
| 1836 | if (IS_ERR(dest_net)) | 1840 | if (IS_ERR(dest_net)) |
| 1837 | return PTR_ERR(dest_net); | 1841 | return PTR_ERR(dest_net); |
| 1838 | 1842 | ||
| 1839 | dev = rtnl_create_link(net, dest_net, ifname, ops, tb); | 1843 | dev = rtnl_create_link(dest_net, ifname, ops, tb); |
| 1840 | if (IS_ERR(dev)) { | 1844 | if (IS_ERR(dev)) { |
| 1841 | err = PTR_ERR(dev); | 1845 | err = PTR_ERR(dev); |
| 1842 | goto out; | 1846 | goto out; |
| @@ -2057,6 +2061,9 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
| 2057 | u8 *addr; | 2061 | u8 *addr; |
| 2058 | int err; | 2062 | int err; |
| 2059 | 2063 | ||
| 2064 | if (!capable(CAP_NET_ADMIN)) | ||
| 2065 | return -EPERM; | ||
| 2066 | |||
| 2060 | err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); | 2067 | err = nlmsg_parse(nlh, sizeof(*ndm), tb, NDA_MAX, NULL); |
| 2061 | if (err < 0) | 2068 | if (err < 0) |
| 2062 | return err; | 2069 | return err; |
| @@ -2123,6 +2130,9 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) | |||
| 2123 | int err = -EINVAL; | 2130 | int err = -EINVAL; |
| 2124 | __u8 *addr; | 2131 | __u8 *addr; |
| 2125 | 2132 | ||
| 2133 | if (!capable(CAP_NET_ADMIN)) | ||
| 2134 | return -EPERM; | ||
| 2135 | |||
| 2126 | if (nlmsg_len(nlh) < sizeof(*ndm)) | 2136 | if (nlmsg_len(nlh) < sizeof(*ndm)) |
| 2127 | return -EINVAL; | 2137 | return -EINVAL; |
| 2128 | 2138 | ||
| @@ -2253,6 +2263,211 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) | |||
| 2253 | return skb->len; | 2263 | return skb->len; |
| 2254 | } | 2264 | } |
| 2255 | 2265 | ||
| 2266 | int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, | ||
| 2267 | struct net_device *dev, u16 mode) | ||
| 2268 | { | ||
| 2269 | struct nlmsghdr *nlh; | ||
| 2270 | struct ifinfomsg *ifm; | ||
| 2271 | struct nlattr *br_afspec; | ||
| 2272 | u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; | ||
| 2273 | |||
| 2274 | nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), NLM_F_MULTI); | ||
| 2275 | if (nlh == NULL) | ||
| 2276 | return -EMSGSIZE; | ||
| 2277 | |||
| 2278 | ifm = nlmsg_data(nlh); | ||
| 2279 | ifm->ifi_family = AF_BRIDGE; | ||
| 2280 | ifm->__ifi_pad = 0; | ||
| 2281 | ifm->ifi_type = dev->type; | ||
| 2282 | ifm->ifi_index = dev->ifindex; | ||
| 2283 | ifm->ifi_flags = dev_get_flags(dev); | ||
| 2284 | ifm->ifi_change = 0; | ||
| 2285 | |||
| 2286 | |||
| 2287 | if (nla_put_string(skb, IFLA_IFNAME, dev->name) || | ||
| 2288 | nla_put_u32(skb, IFLA_MTU, dev->mtu) || | ||
| 2289 | nla_put_u8(skb, IFLA_OPERSTATE, operstate) || | ||
| 2290 | (dev->master && | ||
| 2291 | nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || | ||
| 2292 | (dev->addr_len && | ||
| 2293 | nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || | ||
| 2294 | (dev->ifindex != dev->iflink && | ||
| 2295 | nla_put_u32(skb, IFLA_LINK, dev->iflink))) | ||
| 2296 | goto nla_put_failure; | ||
| 2297 | |||
| 2298 | br_afspec = nla_nest_start(skb, IFLA_AF_SPEC); | ||
| 2299 | if (!br_afspec) | ||
| 2300 | goto nla_put_failure; | ||
| 2301 | |||
| 2302 | if (nla_put_u16(skb, IFLA_BRIDGE_FLAGS, BRIDGE_FLAGS_SELF) || | ||
| 2303 | nla_put_u16(skb, IFLA_BRIDGE_MODE, mode)) { | ||
| 2304 | nla_nest_cancel(skb, br_afspec); | ||
| 2305 | goto nla_put_failure; | ||
| 2306 | } | ||
| 2307 | nla_nest_end(skb, br_afspec); | ||
| 2308 | |||
| 2309 | return nlmsg_end(skb, nlh); | ||
| 2310 | nla_put_failure: | ||
| 2311 | nlmsg_cancel(skb, nlh); | ||
| 2312 | return -EMSGSIZE; | ||
| 2313 | } | ||
| 2314 | EXPORT_SYMBOL(ndo_dflt_bridge_getlink); | ||
| 2315 | |||
| 2316 | static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) | ||
| 2317 | { | ||
| 2318 | struct net *net = sock_net(skb->sk); | ||
| 2319 | struct net_device *dev; | ||
| 2320 | int idx = 0; | ||
| 2321 | u32 portid = NETLINK_CB(cb->skb).portid; | ||
| 2322 | u32 seq = cb->nlh->nlmsg_seq; | ||
| 2323 | |||
| 2324 | rcu_read_lock(); | ||
| 2325 | for_each_netdev_rcu(net, dev) { | ||
| 2326 | const struct net_device_ops *ops = dev->netdev_ops; | ||
| 2327 | struct net_device *master = dev->master; | ||
| 2328 | |||
| 2329 | if (master && master->netdev_ops->ndo_bridge_getlink) { | ||
| 2330 | if (idx >= cb->args[0] && | ||
| 2331 | master->netdev_ops->ndo_bridge_getlink( | ||
| 2332 | skb, portid, seq, dev) < 0) | ||
| 2333 | break; | ||
| 2334 | idx++; | ||
| 2335 | } | ||
| 2336 | |||
| 2337 | if (ops->ndo_bridge_getlink) { | ||
| 2338 | if (idx >= cb->args[0] && | ||
| 2339 | ops->ndo_bridge_getlink(skb, portid, seq, dev) < 0) | ||
| 2340 | break; | ||
| 2341 | idx++; | ||
| 2342 | } | ||
| 2343 | } | ||
| 2344 | rcu_read_unlock(); | ||
| 2345 | cb->args[0] = idx; | ||
| 2346 | |||
| 2347 | return skb->len; | ||
| 2348 | } | ||
| 2349 | |||
| 2350 | static inline size_t bridge_nlmsg_size(void) | ||
| 2351 | { | ||
| 2352 | return NLMSG_ALIGN(sizeof(struct ifinfomsg)) | ||
| 2353 | + nla_total_size(IFNAMSIZ) /* IFLA_IFNAME */ | ||
| 2354 | + nla_total_size(MAX_ADDR_LEN) /* IFLA_ADDRESS */ | ||
| 2355 | + nla_total_size(sizeof(u32)) /* IFLA_MASTER */ | ||
| 2356 | + nla_total_size(sizeof(u32)) /* IFLA_MTU */ | ||
| 2357 | + nla_total_size(sizeof(u32)) /* IFLA_LINK */ | ||
| 2358 | + nla_total_size(sizeof(u32)) /* IFLA_OPERSTATE */ | ||
| 2359 | + nla_total_size(sizeof(u8)) /* IFLA_PROTINFO */ | ||
| 2360 | + nla_total_size(sizeof(struct nlattr)) /* IFLA_AF_SPEC */ | ||
| 2361 | + nla_total_size(sizeof(u16)) /* IFLA_BRIDGE_FLAGS */ | ||
| 2362 | + nla_total_size(sizeof(u16)); /* IFLA_BRIDGE_MODE */ | ||
| 2363 | } | ||
| 2364 | |||
| 2365 | static int rtnl_bridge_notify(struct net_device *dev, u16 flags) | ||
| 2366 | { | ||
| 2367 | struct net *net = dev_net(dev); | ||
| 2368 | struct net_device *master = dev->master; | ||
| 2369 | struct sk_buff *skb; | ||
| 2370 | int err = -EOPNOTSUPP; | ||
| 2371 | |||
| 2372 | skb = nlmsg_new(bridge_nlmsg_size(), GFP_ATOMIC); | ||
| 2373 | if (!skb) { | ||
| 2374 | err = -ENOMEM; | ||
| 2375 | goto errout; | ||
| 2376 | } | ||
| 2377 | |||
| 2378 | if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) && | ||
| 2379 | master && master->netdev_ops->ndo_bridge_getlink) { | ||
| 2380 | err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); | ||
| 2381 | if (err < 0) | ||
| 2382 | goto errout; | ||
| 2383 | } | ||
| 2384 | |||
| 2385 | if ((flags & BRIDGE_FLAGS_SELF) && | ||
| 2386 | dev->netdev_ops->ndo_bridge_getlink) { | ||
| 2387 | err = dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); | ||
| 2388 | if (err < 0) | ||
| 2389 | goto errout; | ||
| 2390 | } | ||
| 2391 | |||
| 2392 | rtnl_notify(skb, net, 0, RTNLGRP_LINK, NULL, GFP_ATOMIC); | ||
| 2393 | return 0; | ||
| 2394 | errout: | ||
| 2395 | WARN_ON(err == -EMSGSIZE); | ||
| 2396 | kfree_skb(skb); | ||
| 2397 | rtnl_set_sk_err(net, RTNLGRP_LINK, err); | ||
| 2398 | return err; | ||
| 2399 | } | ||
| 2400 | |||
| 2401 | static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, | ||
| 2402 | void *arg) | ||
| 2403 | { | ||
| 2404 | struct net *net = sock_net(skb->sk); | ||
| 2405 | struct ifinfomsg *ifm; | ||
| 2406 | struct net_device *dev; | ||
| 2407 | struct nlattr *br_spec, *attr = NULL; | ||
| 2408 | int rem, err = -EOPNOTSUPP; | ||
| 2409 | u16 oflags, flags = 0; | ||
| 2410 | bool have_flags = false; | ||
| 2411 | |||
| 2412 | if (nlmsg_len(nlh) < sizeof(*ifm)) | ||
| 2413 | return -EINVAL; | ||
| 2414 | |||
| 2415 | ifm = nlmsg_data(nlh); | ||
| 2416 | if (ifm->ifi_family != AF_BRIDGE) | ||
| 2417 | return -EPFNOSUPPORT; | ||
| 2418 | |||
| 2419 | dev = __dev_get_by_index(net, ifm->ifi_index); | ||
| 2420 | if (!dev) { | ||
| 2421 | pr_info("PF_BRIDGE: RTM_SETLINK with unknown ifindex\n"); | ||
| 2422 | return -ENODEV; | ||
| 2423 | } | ||
| 2424 | |||
| 2425 | br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); | ||
| 2426 | if (br_spec) { | ||
| 2427 | nla_for_each_nested(attr, br_spec, rem) { | ||
| 2428 | if (nla_type(attr) == IFLA_BRIDGE_FLAGS) { | ||
| 2429 | have_flags = true; | ||
| 2430 | flags = nla_get_u16(attr); | ||
| 2431 | break; | ||
| 2432 | } | ||
| 2433 | } | ||
| 2434 | } | ||
| 2435 | |||
| 2436 | oflags = flags; | ||
| 2437 | |||
| 2438 | if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { | ||
| 2439 | if (!dev->master || | ||
| 2440 | !dev->master->netdev_ops->ndo_bridge_setlink) { | ||
| 2441 | err = -EOPNOTSUPP; | ||
| 2442 | goto out; | ||
| 2443 | } | ||
| 2444 | |||
| 2445 | err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh); | ||
| 2446 | if (err) | ||
| 2447 | goto out; | ||
| 2448 | |||
| 2449 | flags &= ~BRIDGE_FLAGS_MASTER; | ||
| 2450 | } | ||
| 2451 | |||
| 2452 | if ((flags & BRIDGE_FLAGS_SELF)) { | ||
| 2453 | if (!dev->netdev_ops->ndo_bridge_setlink) | ||
| 2454 | err = -EOPNOTSUPP; | ||
| 2455 | else | ||
| 2456 | err = dev->netdev_ops->ndo_bridge_setlink(dev, nlh); | ||
| 2457 | |||
| 2458 | if (!err) | ||
| 2459 | flags &= ~BRIDGE_FLAGS_SELF; | ||
| 2460 | } | ||
| 2461 | |||
| 2462 | if (have_flags) | ||
| 2463 | memcpy(nla_data(attr), &flags, sizeof(flags)); | ||
| 2464 | /* Generate event to notify upper layer of bridge change */ | ||
| 2465 | if (!err) | ||
| 2466 | err = rtnl_bridge_notify(dev, oflags); | ||
| 2467 | out: | ||
| 2468 | return err; | ||
| 2469 | } | ||
| 2470 | |||
| 2256 | /* Protected by RTNL sempahore. */ | 2471 | /* Protected by RTNL sempahore. */ |
| 2257 | static struct rtattr **rta_buf; | 2472 | static struct rtattr **rta_buf; |
| 2258 | static int rtattr_max; | 2473 | static int rtattr_max; |
| @@ -2283,7 +2498,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh) | |||
| 2283 | sz_idx = type>>2; | 2498 | sz_idx = type>>2; |
| 2284 | kind = type&3; | 2499 | kind = type&3; |
| 2285 | 2500 | ||
| 2286 | if (kind != 2 && !capable(CAP_NET_ADMIN)) | 2501 | if (kind != 2 && !ns_capable(net->user_ns, CAP_NET_ADMIN)) |
| 2287 | return -EPERM; | 2502 | return -EPERM; |
| 2288 | 2503 | ||
| 2289 | if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { | 2504 | if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { |
| @@ -2434,5 +2649,8 @@ void __init rtnetlink_init(void) | |||
| 2434 | rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL); | 2649 | rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL); |
| 2435 | rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL); | 2650 | rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL); |
| 2436 | rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); | 2651 | rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); |
| 2652 | |||
| 2653 | rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL); | ||
| 2654 | rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL); | ||
| 2437 | } | 2655 | } |
| 2438 | 2656 | ||
