Subject: Re: Strange UDP binding behavior (SO_BINDTODEVICE)

From: Casey Carter <[email-masked]>
Date: Tue, 07 Oct 2003 15:51:57 -0500

Kevin Dwyer wrote:

>On Mon, 06 Oct 2003 17:50:53 -0500
>Casey Carter <[email-masked]> wrote:
>
>  
>
>>This is not a bug, it's a feature!  It is possible to use multiple 
>>sockets with the same port number bound to different interfaces (where
>>"no interface" is one possible choice).  The UDP packet delivery code 
>>favors the socket that is bound to the interface through which packets
>>are received. So, if I am only interested in packets for port 6666 on 
>>eth1, then I am prioritized ahead of the socket bound to "no
>>interface" port 6666.  
>>    
>>
>
>Well, I would buy that as reasonable, acceptable behavior, but I think
>the reverse is true.
>
>We are binding specifically to say, eth0 on port 694.  Someone comes
>along and binds to "no interface" on port 694 and trumps our socket. 
>Isn't that the opposite of what you describe?
>  
>
Taking a look at the UDP delivery code in net/ipv4/udp.c (version 
2.6.0-test6) we see:

struct sock *udp_v4_lookup_longway(u32 saddr, u16 sport, u32 daddr, u16 dport, int dif)

{

    struct sock *sk, *result = NULL;

    struct hlist_node *node;

    unsigned short hnum = ntohs(dport);

    int badness = -1;

    sk_for_each(sk, node, &udp_hash[hnum & (UDP_HTABLE_SIZE - 1)]) {

        struct inet_opt *inet = inet_sk(sk);

        if (inet->num == hnum && !ipv6_only_sock(sk)) {

            int score = (sk->sk_family == PF_INET ? 1 : 0);

            if (inet->rcv_saddr) {

                if (inet->rcv_saddr != daddr)

                    continue;

                score+=2;

            }

            if (inet->daddr) {

                if (inet->daddr != saddr)

                    continue;

                score+=2;

            }

            if (inet->dport) {

                if (inet->dport != sport)

                    continue;

                score+=2;

            }

            if (sk->sk_bound_dev_if) {

                if (sk->sk_bound_dev_if != dif)

                    continue;

                score+=2;

            }

            if(score == 9) {

                result = sk;

                break;

            } else if(score > badness) {

                result = sk;

                badness = score;

            }

        }

    }

    return result;

}

This code picks a UDP socket to deliver to, amongst those with the 
correct destination port number.  It does so by ranking each socket on a 
scale from 0 to 9 and picking the first socket with the best score.   
Since the score increments are all 2, this code weights equally a socket 
that is bound to the correct interface and (for example) a socket bound 
to no interface but bound to the specific IP address the packet is 
addressed to.  IMHO, the delivery should weigh sk_bound_dev_if much more 
strongly (7 instead of 2), so that if-bound sockets are always favored 
over non-if-bound.  I would be happy to submit the (trivial) patch to do 
so if the networking gurus agree?

-- 
Casey Carter
[email-masked]
[email-masked]
AIM: cartec69