net: arp_ioctl() must hold RTNL
[pandora-kernel.git] / net / ipv4 / arp.c
index d8e540c..7927589 100644 (file)
@@ -433,8 +433,8 @@ static int arp_ignore(struct in_device *in_dev, __be32 sip, __be32 tip)
 
 static int arp_filter(__be32 sip, __be32 tip, struct net_device *dev)
 {
-       struct flowi fl = { .nl_u = { .ip4_u = { .daddr = sip,
-                                                .saddr = tip } } };
+       struct flowi fl = { .fl4_dst = sip,
+                           .fl4_src = tip };
        struct rtable *rt;
        int flag = 0;
        /*unsigned long now; */
@@ -883,7 +883,7 @@ static int arp_process(struct sk_buff *skb)
 
                        dont_send = arp_ignore(in_dev, sip, tip);
                        if (!dont_send && IN_DEV_ARPFILTER(in_dev))
-                               dont_send |= arp_filter(sip, tip, dev);
+                               dont_send = arp_filter(sip, tip, dev);
                        if (!dont_send) {
                                n = neigh_event_ns(&arp_tbl, sha, &sip, dev);
                                if (n) {
@@ -1033,7 +1033,7 @@ static int arp_req_set_public(struct net *net, struct arpreq *r,
        if (mask && mask != htonl(0xFFFFFFFF))
                return -EINVAL;
        if (!dev && (r->arp_flags & ATF_COM)) {
-               dev = dev_getbyhwaddr(net, r->arp_ha.sa_family,
+               dev = dev_getbyhwaddr_rcu(net, r->arp_ha.sa_family,
                                      r->arp_ha.sa_data);
                if (!dev)
                        return -ENODEV;
@@ -1061,8 +1061,8 @@ static int arp_req_set(struct net *net, struct arpreq *r,
        if (r->arp_flags & ATF_PERM)
                r->arp_flags |= ATF_COM;
        if (dev == NULL) {
-               struct flowi fl = { .nl_u.ip4_u = { .daddr = ip,
-                                                   .tos = RTO_ONLINK } };
+               struct flowi fl = { .fl4_dst = ip,
+                                   .fl4_tos = RTO_ONLINK };
                struct rtable *rt;
                err = ip_route_output_key(net, &rt, &fl);
                if (err != 0)
@@ -1142,6 +1142,23 @@ static int arp_req_get(struct arpreq *r, struct net_device *dev)
        return err;
 }
 
+int arp_invalidate(struct net_device *dev, __be32 ip)
+{
+       struct neighbour *neigh = neigh_lookup(&arp_tbl, &ip, dev);
+       int err = -ENXIO;
+
+       if (neigh) {
+               if (neigh->nud_state & ~NUD_NOARP)
+                       err = neigh_update(neigh, NULL, NUD_FAILED,
+                                          NEIGH_UPDATE_F_OVERRIDE|
+                                          NEIGH_UPDATE_F_ADMIN);
+               neigh_release(neigh);
+       }
+
+       return err;
+}
+EXPORT_SYMBOL(arp_invalidate);
+
 static int arp_req_delete_public(struct net *net, struct arpreq *r,
                struct net_device *dev)
 {
@@ -1162,15 +1179,14 @@ static int arp_req_delete(struct net *net, struct arpreq *r,
 {
        int err;
        __be32 ip;
-       struct neighbour *neigh;
 
        if (r->arp_flags & ATF_PUBL)
                return arp_req_delete_public(net, r, dev);
 
        ip = ((struct sockaddr_in *)&r->arp_pa)->sin_addr.s_addr;
        if (dev == NULL) {
-               struct flowi fl = { .nl_u.ip4_u = { .daddr = ip,
-                                                   .tos = RTO_ONLINK } };
+               struct flowi fl = { .fl4_dst = ip,
+                                   .fl4_tos = RTO_ONLINK };
                struct rtable *rt;
                err = ip_route_output_key(net, &rt, &fl);
                if (err != 0)
@@ -1180,16 +1196,7 @@ static int arp_req_delete(struct net *net, struct arpreq *r,
                if (!dev)
                        return -EINVAL;
        }
-       err = -ENXIO;
-       neigh = neigh_lookup(&arp_tbl, &ip, dev);
-       if (neigh) {
-               if (neigh->nud_state & ~NUD_NOARP)
-                       err = neigh_update(neigh, NULL, NUD_FAILED,
-                                          NEIGH_UPDATE_F_OVERRIDE|
-                                          NEIGH_UPDATE_F_ADMIN);
-               neigh_release(neigh);
-       }
-       return err;
+       return arp_invalidate(dev, ip);
 }
 
 /*
@@ -1252,12 +1259,12 @@ int arp_ioctl(struct net *net, unsigned int cmd, void __user *arg)
                break;
        case SIOCGARP:
                err = arp_req_get(&r, dev);
-               if (!err && copy_to_user(arg, &r, sizeof(r)))
-                       err = -EFAULT;
                break;
        }
 out:
        rtnl_unlock();
+       if (cmd == SIOCGARP && !err && copy_to_user(arg, &r, sizeof(r)))
+               err = -EFAULT;
        return err;
 }