aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6/ip6_tunnel.c
diff options
context:
space:
mode:
authorNicolas Dichtel <nicolas.dichtel@6wind.com>2012-11-14 00:14:00 -0500
committerDavid S. Miller <davem@davemloft.net>2012-11-14 22:02:37 -0500
commit0b112457229d8a17198a02f3cca32922d2e374f1 (patch)
tree406e33de384b8b66394db866ac89e887f9d334f5 /net/ipv6/ip6_tunnel.c
parentb58d731acc599b69fc50cb40e13f30f0f16fcb3f (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.c164
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
254static 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
277out:
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
301failed_free: 319failed_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
1251static 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
1233static void 1265static void
1234ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) 1266ip6_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
1529static 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
1545static 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
1580static 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
1595static 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
1501static size_t ip6_tnl_get_size(const struct net_device *dev) 1619static 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
1663static 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
1545static struct rtnl_link_ops ip6_link_ops __read_mostly = { 1674static 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};