]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/ipv4/arp.c
arp: always override existing neigh entries with gratuitous ARP
[karo-tx-linux.git] / net / ipv4 / arp.c
index 0937b34c27cacb2dec73a67a76ff11fe26722500..ae96e6f3e0cb048e6a9273421343daf3582e9463 100644 (file)
@@ -641,6 +641,32 @@ void arp_xmit(struct sk_buff *skb)
 }
 EXPORT_SYMBOL(arp_xmit);
 
+static bool arp_is_garp(struct net *net, struct net_device *dev,
+                       int *addr_type, __be16 ar_op,
+                       __be32 sip, __be32 tip,
+                       unsigned char *sha, unsigned char *tha)
+{
+       bool is_garp = tip == sip;
+
+       /* Gratuitous ARP _replies_ also require target hwaddr to be
+        * the same as source.
+        */
+       if (is_garp && ar_op == htons(ARPOP_REPLY))
+               is_garp =
+                       /* IPv4 over IEEE 1394 doesn't provide target
+                        * hardware address field in its ARP payload.
+                        */
+                       tha &&
+                       !memcmp(tha, sha, dev->addr_len);
+
+       if (is_garp) {
+               *addr_type = inet_addr_type_dev_table(net, dev, sip);
+               if (*addr_type != RTN_UNICAST)
+                       is_garp = false;
+       }
+       return is_garp;
+}
+
 /*
  *     Process an arp request.
  */
@@ -653,6 +679,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
        unsigned char *arp_ptr;
        struct rtable *rt;
        unsigned char *sha;
+       unsigned char *tha = NULL;
        __be32 sip, tip;
        u16 dev_type = dev->type;
        int addr_type;
@@ -724,6 +751,7 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
                break;
 #endif
        default:
+               tha = arp_ptr;
                arp_ptr += dev->addr_len;
        }
        memcpy(&tip, arp_ptr, 4);
@@ -835,19 +863,25 @@ static int arp_process(struct net *net, struct sock *sk, struct sk_buff *skb)
 
        n = __neigh_lookup(&arp_tbl, &sip, dev, 0);
 
-       if (IN_DEV_ARP_ACCEPT(in_dev)) {
-               unsigned int addr_type = inet_addr_type_dev_table(net, dev, sip);
+       if (n || IN_DEV_ARP_ACCEPT(in_dev)) {
+               addr_type = -1;
+               is_garp = arp_is_garp(net, dev, &addr_type, arp->ar_op,
+                                     sip, tip, sha, tha);
+       }
 
+       if (IN_DEV_ARP_ACCEPT(in_dev)) {
                /* Unsolicited ARP is not accepted by default.
                   It is possible, that this option should be enabled for some
                   devices (strip is candidate)
                 */
-               is_garp = arp->ar_op == htons(ARPOP_REQUEST) && tip == sip &&
-                         addr_type == RTN_UNICAST;
-
                if (!n &&
-                   ((arp->ar_op == htons(ARPOP_REPLY)  &&
-                               addr_type == RTN_UNICAST) || is_garp))
+                   (is_garp ||
+                    (arp->ar_op == htons(ARPOP_REPLY) &&
+                     (addr_type == RTN_UNICAST ||
+                      (addr_type < 0 &&
+                       /* postpone calculation to as late as possible */
+                       inet_addr_type_dev_table(net, dev, sip) ==
+                               RTN_UNICAST)))))
                        n = __neigh_lookup(&arp_tbl, &sip, dev, 1);
        }