diff options
author | Nicolas Dichtel <nicolas.dichtel@6wind.com> | 2012-11-14 00:14:00 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-11-14 22:02:37 -0500 |
commit | 0b112457229d8a17198a02f3cca32922d2e374f1 (patch) | |
tree | 406e33de384b8b66394db866ac89e887f9d334f5 /net/ipv6/ip6_tunnel.c | |
parent | b58d731acc599b69fc50cb40e13f30f0f16fcb3f (diff) |
ip6tnl: add support of link creation via rtnl
This patch add the support of 'ip link .. type ip6tnl'.
Signed-off-by: Nicolas Dichtel <nicolas.dichtel@6wind.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6/ip6_tunnel.c')
-rw-r--r-- | net/ipv6/ip6_tunnel.c | 164 |
1 files changed, 149 insertions, 15 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c index c054847a4e27..ab4d05633bfd 100644 --- a/net/ipv6/ip6_tunnel.c +++ b/net/ipv6/ip6_tunnel.c | |||
@@ -251,6 +251,33 @@ static void ip6_dev_free(struct net_device *dev) | |||
251 | free_netdev(dev); | 251 | free_netdev(dev); |
252 | } | 252 | } |
253 | 253 | ||
254 | static int ip6_tnl_create2(struct net_device *dev) | ||
255 | { | ||
256 | struct ip6_tnl *t = netdev_priv(dev); | ||
257 | struct net *net = dev_net(dev); | ||
258 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | ||
259 | int err; | ||
260 | |||
261 | t = netdev_priv(dev); | ||
262 | err = ip6_tnl_dev_init(dev); | ||
263 | if (err < 0) | ||
264 | goto out; | ||
265 | |||
266 | err = register_netdevice(dev); | ||
267 | if (err < 0) | ||
268 | goto out; | ||
269 | |||
270 | strcpy(t->parms.name, dev->name); | ||
271 | dev->rtnl_link_ops = &ip6_link_ops; | ||
272 | |||
273 | dev_hold(dev); | ||
274 | ip6_tnl_link(ip6n, t); | ||
275 | return 0; | ||
276 | |||
277 | out: | ||
278 | return err; | ||
279 | } | ||
280 | |||
254 | /** | 281 | /** |
255 | * ip6_tnl_create - create a new tunnel | 282 | * ip6_tnl_create - create a new tunnel |
256 | * @p: tunnel parameters | 283 | * @p: tunnel parameters |
@@ -269,7 +296,6 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) | |||
269 | struct ip6_tnl *t; | 296 | struct ip6_tnl *t; |
270 | char name[IFNAMSIZ]; | 297 | char name[IFNAMSIZ]; |
271 | int err; | 298 | int err; |
272 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | ||
273 | 299 | ||
274 | if (p->name[0]) | 300 | if (p->name[0]) |
275 | strlcpy(name, p->name, IFNAMSIZ); | 301 | strlcpy(name, p->name, IFNAMSIZ); |
@@ -284,18 +310,10 @@ static struct ip6_tnl *ip6_tnl_create(struct net *net, struct __ip6_tnl_parm *p) | |||
284 | 310 | ||
285 | t = netdev_priv(dev); | 311 | t = netdev_priv(dev); |
286 | t->parms = *p; | 312 | t->parms = *p; |
287 | err = ip6_tnl_dev_init(dev); | 313 | err = ip6_tnl_create2(dev); |
288 | if (err < 0) | 314 | if (err < 0) |
289 | goto failed_free; | 315 | goto failed_free; |
290 | 316 | ||
291 | if ((err = register_netdevice(dev)) < 0) | ||
292 | goto failed_free; | ||
293 | |||
294 | strcpy(t->parms.name, dev->name); | ||
295 | dev->rtnl_link_ops = &ip6_link_ops; | ||
296 | |||
297 | dev_hold(dev); | ||
298 | ip6_tnl_link(ip6n, t); | ||
299 | return t; | 317 | return t; |
300 | 318 | ||
301 | failed_free: | 319 | failed_free: |
@@ -1230,6 +1248,20 @@ ip6_tnl_change(struct ip6_tnl *t, const struct __ip6_tnl_parm *p) | |||
1230 | return 0; | 1248 | return 0; |
1231 | } | 1249 | } |
1232 | 1250 | ||
1251 | static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p) | ||
1252 | { | ||
1253 | struct net *net = dev_net(t->dev); | ||
1254 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | ||
1255 | int err; | ||
1256 | |||
1257 | ip6_tnl_unlink(ip6n, t); | ||
1258 | synchronize_net(); | ||
1259 | err = ip6_tnl_change(t, p); | ||
1260 | ip6_tnl_link(ip6n, t); | ||
1261 | netdev_state_change(t->dev); | ||
1262 | return err; | ||
1263 | } | ||
1264 | |||
1233 | static void | 1265 | static void |
1234 | ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) | 1266 | ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) |
1235 | { | 1267 | { |
@@ -1338,11 +1370,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1338 | } else | 1370 | } else |
1339 | t = netdev_priv(dev); | 1371 | t = netdev_priv(dev); |
1340 | 1372 | ||
1341 | ip6_tnl_unlink(ip6n, t); | 1373 | err = ip6_tnl_update(t, &p1); |
1342 | synchronize_net(); | ||
1343 | err = ip6_tnl_change(t, &p1); | ||
1344 | ip6_tnl_link(ip6n, t); | ||
1345 | netdev_state_change(dev); | ||
1346 | } | 1374 | } |
1347 | if (t) { | 1375 | if (t) { |
1348 | err = 0; | 1376 | err = 0; |
@@ -1498,6 +1526,96 @@ static int __net_init ip6_fb_tnl_dev_init(struct net_device *dev) | |||
1498 | return 0; | 1526 | return 0; |
1499 | } | 1527 | } |
1500 | 1528 | ||
1529 | static int ip6_tnl_validate(struct nlattr *tb[], struct nlattr *data[]) | ||
1530 | { | ||
1531 | u8 proto; | ||
1532 | |||
1533 | if (!data) | ||
1534 | return 0; | ||
1535 | |||
1536 | proto = nla_get_u8(data[IFLA_IPTUN_PROTO]); | ||
1537 | if (proto != IPPROTO_IPV6 && | ||
1538 | proto != IPPROTO_IPIP && | ||
1539 | proto != 0) | ||
1540 | return -EINVAL; | ||
1541 | |||
1542 | return 0; | ||
1543 | } | ||
1544 | |||
1545 | static void ip6_tnl_netlink_parms(struct nlattr *data[], | ||
1546 | struct __ip6_tnl_parm *parms) | ||
1547 | { | ||
1548 | memset(parms, 0, sizeof(*parms)); | ||
1549 | |||
1550 | if (!data) | ||
1551 | return; | ||
1552 | |||
1553 | if (data[IFLA_IPTUN_LINK]) | ||
1554 | parms->link = nla_get_u32(data[IFLA_IPTUN_LINK]); | ||
1555 | |||
1556 | if (data[IFLA_IPTUN_LOCAL]) | ||
1557 | nla_memcpy(&parms->laddr, data[IFLA_IPTUN_LOCAL], | ||
1558 | sizeof(struct in6_addr)); | ||
1559 | |||
1560 | if (data[IFLA_IPTUN_REMOTE]) | ||
1561 | nla_memcpy(&parms->raddr, data[IFLA_IPTUN_REMOTE], | ||
1562 | sizeof(struct in6_addr)); | ||
1563 | |||
1564 | if (data[IFLA_IPTUN_TTL]) | ||
1565 | parms->hop_limit = nla_get_u8(data[IFLA_IPTUN_TTL]); | ||
1566 | |||
1567 | if (data[IFLA_IPTUN_ENCAP_LIMIT]) | ||
1568 | parms->encap_limit = nla_get_u8(data[IFLA_IPTUN_ENCAP_LIMIT]); | ||
1569 | |||
1570 | if (data[IFLA_IPTUN_FLOWINFO]) | ||
1571 | parms->flowinfo = nla_get_u32(data[IFLA_IPTUN_FLOWINFO]); | ||
1572 | |||
1573 | if (data[IFLA_IPTUN_FLAGS]) | ||
1574 | parms->flags = nla_get_u32(data[IFLA_IPTUN_FLAGS]); | ||
1575 | |||
1576 | if (data[IFLA_IPTUN_PROTO]) | ||
1577 | parms->proto = nla_get_u8(data[IFLA_IPTUN_PROTO]); | ||
1578 | } | ||
1579 | |||
1580 | static int ip6_tnl_newlink(struct net *src_net, struct net_device *dev, | ||
1581 | struct nlattr *tb[], struct nlattr *data[]) | ||
1582 | { | ||
1583 | struct net *net = dev_net(dev); | ||
1584 | struct ip6_tnl *nt; | ||
1585 | |||
1586 | nt = netdev_priv(dev); | ||
1587 | ip6_tnl_netlink_parms(data, &nt->parms); | ||
1588 | |||
1589 | if (ip6_tnl_locate(net, &nt->parms, 0)) | ||
1590 | return -EEXIST; | ||
1591 | |||
1592 | return ip6_tnl_create2(dev); | ||
1593 | } | ||
1594 | |||
1595 | static int ip6_tnl_changelink(struct net_device *dev, struct nlattr *tb[], | ||
1596 | struct nlattr *data[]) | ||
1597 | { | ||
1598 | struct ip6_tnl *t; | ||
1599 | struct __ip6_tnl_parm p; | ||
1600 | struct net *net = dev_net(dev); | ||
1601 | struct ip6_tnl_net *ip6n = net_generic(net, ip6_tnl_net_id); | ||
1602 | |||
1603 | if (dev == ip6n->fb_tnl_dev) | ||
1604 | return -EINVAL; | ||
1605 | |||
1606 | ip6_tnl_netlink_parms(data, &p); | ||
1607 | |||
1608 | t = ip6_tnl_locate(net, &p, 0); | ||
1609 | |||
1610 | if (t) { | ||
1611 | if (t->dev != dev) | ||
1612 | return -EEXIST; | ||
1613 | } else | ||
1614 | t = netdev_priv(dev); | ||
1615 | |||
1616 | return ip6_tnl_update(t, &p); | ||
1617 | } | ||
1618 | |||
1501 | static size_t ip6_tnl_get_size(const struct net_device *dev) | 1619 | static size_t ip6_tnl_get_size(const struct net_device *dev) |
1502 | { | 1620 | { |
1503 | return | 1621 | return |
@@ -1542,10 +1660,26 @@ nla_put_failure: | |||
1542 | return -EMSGSIZE; | 1660 | return -EMSGSIZE; |
1543 | } | 1661 | } |
1544 | 1662 | ||
1663 | static const struct nla_policy ip6_tnl_policy[IFLA_IPTUN_MAX + 1] = { | ||
1664 | [IFLA_IPTUN_LINK] = { .type = NLA_U32 }, | ||
1665 | [IFLA_IPTUN_LOCAL] = { .len = sizeof(struct in6_addr) }, | ||
1666 | [IFLA_IPTUN_REMOTE] = { .len = sizeof(struct in6_addr) }, | ||
1667 | [IFLA_IPTUN_TTL] = { .type = NLA_U8 }, | ||
1668 | [IFLA_IPTUN_ENCAP_LIMIT] = { .type = NLA_U8 }, | ||
1669 | [IFLA_IPTUN_FLOWINFO] = { .type = NLA_U32 }, | ||
1670 | [IFLA_IPTUN_FLAGS] = { .type = NLA_U32 }, | ||
1671 | [IFLA_IPTUN_PROTO] = { .type = NLA_U8 }, | ||
1672 | }; | ||
1673 | |||
1545 | static struct rtnl_link_ops ip6_link_ops __read_mostly = { | 1674 | static struct rtnl_link_ops ip6_link_ops __read_mostly = { |
1546 | .kind = "ip6tnl", | 1675 | .kind = "ip6tnl", |
1547 | .maxtype = IFLA_IPTUN_MAX, | 1676 | .maxtype = IFLA_IPTUN_MAX, |
1677 | .policy = ip6_tnl_policy, | ||
1548 | .priv_size = sizeof(struct ip6_tnl), | 1678 | .priv_size = sizeof(struct ip6_tnl), |
1679 | .setup = ip6_tnl_dev_setup, | ||
1680 | .validate = ip6_tnl_validate, | ||
1681 | .newlink = ip6_tnl_newlink, | ||
1682 | .changelink = ip6_tnl_changelink, | ||
1549 | .get_size = ip6_tnl_get_size, | 1683 | .get_size = ip6_tnl_get_size, |
1550 | .fill_info = ip6_tnl_fill_info, | 1684 | .fill_info = ip6_tnl_fill_info, |
1551 | }; | 1685 | }; |