aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorVille Nuorvala <vnuorval@tcs.hut.fi>2006-11-24 20:05:41 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-12-03 00:30:24 -0500
commit567131a722ca064c917c0b06e4bcf07d47602103 (patch)
treed249b39f0c05aedeb3873a897151b47913632eee /net
parente94ef682053a6eeca91aefdaecf8efe7fd7e33a5 (diff)
[IPV6]: Fix SIOCCHGTUNNEL bug in IPv6 tunnels
A logic bug in tunnel lookup could result in duplicate tunnels when changing an existing device. Signed-off-by: Ville Nuorvala <vnuorval@tcs.hut.fi> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r--net/ipv6/ip6_tunnel.c111
1 files changed, 46 insertions, 65 deletions
diff --git a/net/ipv6/ip6_tunnel.c b/net/ipv6/ip6_tunnel.c
index 25bc5ed49104..fdf1a2fa3a3d 100644
--- a/net/ipv6/ip6_tunnel.c
+++ b/net/ipv6/ip6_tunnel.c
@@ -215,11 +215,10 @@ ip6ip6_tnl_unlink(struct ip6_tnl *t)
215 * Create tunnel matching given parameters. 215 * Create tunnel matching given parameters.
216 * 216 *
217 * Return: 217 * Return:
218 * 0 on success 218 * created tunnel or NULL
219 **/ 219 **/
220 220
221static int 221static struct ip6_tnl *ip6_tnl_create(struct ip6_tnl_parm *p)
222ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt)
223{ 222{
224 struct net_device *dev; 223 struct net_device *dev;
225 struct ip6_tnl *t; 224 struct ip6_tnl *t;
@@ -236,11 +235,11 @@ ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt)
236 break; 235 break;
237 } 236 }
238 if (i == IP6_TNL_MAX) 237 if (i == IP6_TNL_MAX)
239 return -ENOBUFS; 238 goto failed;
240 } 239 }
241 dev = alloc_netdev(sizeof (*t), name, ip6ip6_tnl_dev_setup); 240 dev = alloc_netdev(sizeof (*t), name, ip6ip6_tnl_dev_setup);
242 if (dev == NULL) 241 if (dev == NULL)
243 return -ENOMEM; 242 goto failed;
244 243
245 t = netdev_priv(dev); 244 t = netdev_priv(dev);
246 dev->init = ip6ip6_tnl_dev_init; 245 dev->init = ip6ip6_tnl_dev_init;
@@ -248,13 +247,13 @@ ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt)
248 247
249 if ((err = register_netdevice(dev)) < 0) { 248 if ((err = register_netdevice(dev)) < 0) {
250 free_netdev(dev); 249 free_netdev(dev);
251 return err; 250 goto failed;
252 } 251 }
253 dev_hold(dev); 252 dev_hold(dev);
254
255 ip6ip6_tnl_link(t); 253 ip6ip6_tnl_link(t);
256 *pt = t; 254 return t;
257 return 0; 255failed:
256 return NULL;
258} 257}
259 258
260/** 259/**
@@ -268,32 +267,23 @@ ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt)
268 * tunnel device is created and registered for use. 267 * tunnel device is created and registered for use.
269 * 268 *
270 * Return: 269 * Return:
271 * 0 if tunnel located or created, 270 * matching tunnel or NULL
272 * -EINVAL if parameters incorrect,
273 * -ENODEV if no matching tunnel available
274 **/ 271 **/
275 272
276static int 273static struct ip6_tnl *ip6ip6_tnl_locate(struct ip6_tnl_parm *p, int create)
277ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create)
278{ 274{
279 struct in6_addr *remote = &p->raddr; 275 struct in6_addr *remote = &p->raddr;
280 struct in6_addr *local = &p->laddr; 276 struct in6_addr *local = &p->laddr;
281 struct ip6_tnl *t; 277 struct ip6_tnl *t;
282 278
283 if (p->proto != IPPROTO_IPV6)
284 return -EINVAL;
285
286 for (t = *ip6ip6_bucket(p); t; t = t->next) { 279 for (t = *ip6ip6_bucket(p); t; t = t->next) {
287 if (ipv6_addr_equal(local, &t->parms.laddr) && 280 if (ipv6_addr_equal(local, &t->parms.laddr) &&
288 ipv6_addr_equal(remote, &t->parms.raddr)) { 281 ipv6_addr_equal(remote, &t->parms.raddr))
289 *pt = t; 282 return t;
290 return (create ? -EEXIST : 0);
291 }
292 } 283 }
293 if (!create) 284 if (!create)
294 return -ENODEV; 285 return NULL;
295 286 return ip6_tnl_create(p);
296 return ip6_tnl_create(p, pt);
297} 287}
298 288
299/** 289/**
@@ -920,26 +910,20 @@ static int
920ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) 910ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
921{ 911{
922 int err = 0; 912 int err = 0;
923 int create;
924 struct ip6_tnl_parm p; 913 struct ip6_tnl_parm p;
925 struct ip6_tnl *t = NULL; 914 struct ip6_tnl *t = NULL;
926 915
927 switch (cmd) { 916 switch (cmd) {
928 case SIOCGETTUNNEL: 917 case SIOCGETTUNNEL:
929 if (dev == ip6ip6_fb_tnl_dev) { 918 if (dev == ip6ip6_fb_tnl_dev) {
930 if (copy_from_user(&p, 919 if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) {
931 ifr->ifr_ifru.ifru_data,
932 sizeof (p))) {
933 err = -EFAULT; 920 err = -EFAULT;
934 break; 921 break;
935 } 922 }
936 if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV) 923 t = ip6ip6_tnl_locate(&p, 0);
937 t = netdev_priv(dev); 924 }
938 else if (err) 925 if (t == NULL)
939 break;
940 } else
941 t = netdev_priv(dev); 926 t = netdev_priv(dev);
942
943 memcpy(&p, &t->parms, sizeof (p)); 927 memcpy(&p, &t->parms, sizeof (p));
944 if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { 928 if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) {
945 err = -EFAULT; 929 err = -EFAULT;
@@ -948,35 +932,36 @@ ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
948 case SIOCADDTUNNEL: 932 case SIOCADDTUNNEL:
949 case SIOCCHGTUNNEL: 933 case SIOCCHGTUNNEL:
950 err = -EPERM; 934 err = -EPERM;
951 create = (cmd == SIOCADDTUNNEL);
952 if (!capable(CAP_NET_ADMIN)) 935 if (!capable(CAP_NET_ADMIN))
953 break; 936 break;
954 if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { 937 err = -EFAULT;
955 err = -EFAULT; 938 if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
956 break; 939 break;
957 } 940 err = -EINVAL;
958 if (!create && dev != ip6ip6_fb_tnl_dev) { 941 if (p.proto != IPPROTO_IPV6)
959 t = netdev_priv(dev);
960 }
961 if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) {
962 break; 942 break;
963 } 943 t = ip6ip6_tnl_locate(&p, cmd == SIOCADDTUNNEL);
964 if (cmd == SIOCCHGTUNNEL) { 944 if (dev != ip6ip6_fb_tnl_dev && cmd == SIOCCHGTUNNEL) {
965 if (t->dev != dev) { 945 if (t != NULL) {
966 err = -EEXIST; 946 if (t->dev != dev) {
967 break; 947 err = -EEXIST;
968 } 948 break;
949 }
950 } else
951 t = netdev_priv(dev);
952
969 ip6ip6_tnl_unlink(t); 953 ip6ip6_tnl_unlink(t);
970 err = ip6ip6_tnl_change(t, &p); 954 err = ip6ip6_tnl_change(t, &p);
971 ip6ip6_tnl_link(t); 955 ip6ip6_tnl_link(t);
972 netdev_state_change(dev); 956 netdev_state_change(dev);
973 } 957 }
974 if (copy_to_user(ifr->ifr_ifru.ifru_data, 958 if (t) {
975 &t->parms, sizeof (p))) {
976 err = -EFAULT;
977 } else {
978 err = 0; 959 err = 0;
979 } 960 if (copy_to_user(ifr->ifr_ifru.ifru_data, &t->parms, sizeof (p)))
961 err = -EFAULT;
962
963 } else
964 err = (cmd == SIOCADDTUNNEL ? -ENOBUFS : -ENOENT);
980 break; 965 break;
981 case SIOCDELTUNNEL: 966 case SIOCDELTUNNEL:
982 err = -EPERM; 967 err = -EPERM;
@@ -984,22 +969,18 @@ ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
984 break; 969 break;
985 970
986 if (dev == ip6ip6_fb_tnl_dev) { 971 if (dev == ip6ip6_fb_tnl_dev) {
987 if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, 972 err = -EFAULT;
988 sizeof (p))) { 973 if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p)))
989 err = -EFAULT;
990 break; 974 break;
991 } 975 err = -ENOENT;
992 err = ip6ip6_tnl_locate(&p, &t, 0); 976 if ((t = ip6ip6_tnl_locate(&p, 0)) == NULL)
993 if (err)
994 break; 977 break;
995 if (t == netdev_priv(ip6ip6_fb_tnl_dev)) { 978 err = -EPERM;
996 err = -EPERM; 979 if (t->dev == ip6ip6_fb_tnl_dev)
997 break; 980 break;
998 } 981 dev = t->dev;
999 } else {
1000 t = netdev_priv(dev);
1001 } 982 }
1002 err = unregister_netdevice(t->dev); 983 err = unregister_netdevice(dev);
1003 break; 984 break;
1004 default: 985 default:
1005 err = -EINVAL; 986 err = -EINVAL;