aboutsummaryrefslogtreecommitdiffstats
path: root/net/ipv6
diff options
context:
space:
mode:
authorAlexey Andriyanov <alan@al-an.info>2014-10-29 03:54:52 -0400
committerDavid S. Miller <davem@davemloft.net>2014-10-30 16:09:20 -0400
commitacf722f73499d85e959ce99cf22d1b827d0b273a (patch)
tree1e6e328a330c5b25ceddd9887be6b073f667204c /net/ipv6
parent43728fa5c5e475e6f0059ec739e715fc49e4a478 (diff)
ip6_tunnel: allow to change mode for the ip6tnl0
The fallback device is in ipv6 mode by default. The mode can not be changed in runtime, so there is no way to decapsulate ip4in6 packets coming from various sources without creating the specific tunnel ifaces for each peer. This allows to update the fallback tunnel device, but only the mode could be changed. Usual command should work for the fallback device: `ip -6 tun change ip6tnl0 mode any` The fallback device can not be hidden from the packet receiver as a regular tunnel, but there is no need for synchronization as long as we do single assignment. Cc: David S. Miller <davem@davemloft.net> Cc: Eric Dumazet <edumazet@google.com> Signed-off-by: Alexey Andriyanov <alan@al-an.info> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/ipv6')
-rw-r--r--net/ipv6/ip6_tunnel.c32
1 files changed, 25 insertions, 7 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 9409887fb664..8c97cd1048c2 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -477,6 +477,7 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
477 int rel_msg = 0; 477 int rel_msg = 0;
478 u8 rel_type = ICMPV6_DEST_UNREACH; 478 u8 rel_type = ICMPV6_DEST_UNREACH;
479 u8 rel_code = ICMPV6_ADDR_UNREACH; 479 u8 rel_code = ICMPV6_ADDR_UNREACH;
480 u8 tproto;
480 __u32 rel_info = 0; 481 __u32 rel_info = 0;
481 __u16 len; 482 __u16 len;
482 int err = -ENOENT; 483 int err = -ENOENT;
@@ -490,7 +491,8 @@ ip6_tnl_err(struct sk_buff *skb, __u8 ipproto, struct inet6_skb_parm *opt,
490 &ipv6h->saddr)) == NULL) 491 &ipv6h->saddr)) == NULL)
491 goto out; 492 goto out;
492 493
493 if (t->parms.proto != ipproto && t->parms.proto != 0) 494 tproto = ACCESS_ONCE(t->parms.proto);
495 if (tproto != ipproto && tproto != 0)
494 goto out; 496 goto out;
495 497
496 err = 0; 498 err = 0;
@@ -791,6 +793,7 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
791{ 793{
792 struct ip6_tnl *t; 794 struct ip6_tnl *t;
793 const struct ipv6hdr *ipv6h = ipv6_hdr(skb); 795 const struct ipv6hdr *ipv6h = ipv6_hdr(skb);
796 u8 tproto;
794 int err; 797 int err;
795 798
796 rcu_read_lock(); 799 rcu_read_lock();
@@ -799,7 +802,8 @@ static int ip6_tnl_rcv(struct sk_buff *skb, __u16 protocol,
799 &ipv6h->daddr)) != NULL) { 802 &ipv6h->daddr)) != NULL) {
800 struct pcpu_sw_netstats *tstats; 803 struct pcpu_sw_netstats *tstats;
801 804
802 if (t->parms.proto != ipproto && t->parms.proto != 0) { 805 tproto = ACCESS_ONCE(t->parms.proto);
806 if (tproto != ipproto && tproto != 0) {
803 rcu_read_unlock(); 807 rcu_read_unlock();
804 goto discard; 808 goto discard;
805 } 809 }
@@ -1078,9 +1082,11 @@ ip4ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
1078 struct flowi6 fl6; 1082 struct flowi6 fl6;
1079 __u8 dsfield; 1083 __u8 dsfield;
1080 __u32 mtu; 1084 __u32 mtu;
1085 u8 tproto;
1081 int err; 1086 int err;
1082 1087
1083 if ((t->parms.proto != IPPROTO_IPIP && t->parms.proto != 0) || 1088 tproto = ACCESS_ONCE(t->parms.proto);
1089 if ((tproto != IPPROTO_IPIP && tproto != 0) ||
1084 !ip6_tnl_xmit_ctl(t)) 1090 !ip6_tnl_xmit_ctl(t))
1085 return -1; 1091 return -1;
1086 1092
@@ -1120,9 +1126,11 @@ ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev)
1120 struct flowi6 fl6; 1126 struct flowi6 fl6;
1121 __u8 dsfield; 1127 __u8 dsfield;
1122 __u32 mtu; 1128 __u32 mtu;
1129 u8 tproto;
1123 int err; 1130 int err;
1124 1131
1125 if ((t->parms.proto != IPPROTO_IPV6 && t->parms.proto != 0) || 1132 tproto = ACCESS_ONCE(t->parms.proto);
1133 if ((tproto != IPPROTO_IPV6 && tproto != 0) ||
1126 !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h)) 1134 !ip6_tnl_xmit_ctl(t) || ip6_tnl_addr_conflict(t, ipv6h))
1127 return -1; 1135 return -1;
1128 1136
@@ -1285,6 +1293,14 @@ static int ip6_tnl_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
1285 return err; 1293 return err;
1286} 1294}
1287 1295
1296static int ip6_tnl0_update(struct ip6_tnl *t, struct __ip6_tnl_parm *p)
1297{
1298 /* for default tnl0 device allow to change only the proto */
1299 t->parms.proto = p->proto;
1300 netdev_state_change(t->dev);
1301 return 0;
1302}
1303
1288static void 1304static void
1289ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u) 1305ip6_tnl_parm_from_user(struct __ip6_tnl_parm *p, const struct ip6_tnl_parm *u)
1290{ 1306{
@@ -1384,7 +1400,7 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
1384 break; 1400 break;
1385 ip6_tnl_parm_from_user(&p1, &p); 1401 ip6_tnl_parm_from_user(&p1, &p);
1386 t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL); 1402 t = ip6_tnl_locate(net, &p1, cmd == SIOCADDTUNNEL);
1387 if (dev != ip6n->fb_tnl_dev && cmd == SIOCCHGTUNNEL) { 1403 if (cmd == SIOCCHGTUNNEL) {
1388 if (t != NULL) { 1404 if (t != NULL) {
1389 if (t->dev != dev) { 1405 if (t->dev != dev) {
1390 err = -EEXIST; 1406 err = -EEXIST;
@@ -1392,8 +1408,10 @@ ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
1392 } 1408 }
1393 } else 1409 } else
1394 t = netdev_priv(dev); 1410 t = netdev_priv(dev);
1395 1411 if (dev == ip6n->fb_tnl_dev)
1396 err = ip6_tnl_update(t, &p1); 1412 err = ip6_tnl0_update(t, &p1);
1413 else
1414 err = ip6_tnl_update(t, &p1);
1397 } 1415 }
1398 if (t) { 1416 if (t) {
1399 err = 0; 1417 err = 0;