in_ifinit() fix for SIOCSIFADDR

Sepherosa Ziehau sepherosa at gmail.com
Sat May 24 20:31:05 PDT 2008


On Sun, May 25, 2008 at 1:42 AM, Matthew Dillon
<dillon at apollo.backplane.com> wrote:
>
> :Hi all,
> :
> :Following scenario will cause inaddr hash table contains dangling
> :reference to 'ia':
> :- ifaceX has an AF_INET ia
> :- SIOCSIFADDR is used to change address, and new address' hash value
> :is different from ia's
> :- in in_ifinit()
> :  o  ia is currently in hash bucket B1
> :...
>
>    Oooh, nice.  I think you found the corruption that has been bugging
>    me for a long time.
>
>    The patch looks good except for that XXX assumption.  I can't quite
>    follow that code sequence.

There are only three possible branches in the final switch block that
will reach the end of in_control_internal()
1) cmd is SIOCDIFADDR
2) cmd is SIOCAIFADDR, ia is newly allocated and in_ifinit() fails
3) cmd is SIOCSIFADDR, ia is newly allocated and in_ifinit() fails

After patch, in_ifinit() will remove ia from hash table if in_ifinit()
fails, so for 2) and 3) there is no need to remove ia again

For 1), ia was installed into hash table only if
ia->ia_addr.sin_family == AF_INET.  This forms the condition test
before the XXX comment.  IMHO, using sin_family to indicate, whether
ia is in hash table or not, is not a good idea, that's why I added XXX
comment.  Once I parallelize ia hash table, I will add a flag to
indicate whether ia is in hash table or not.

>
> :Old code will also leave ia in the wrong hash bucket, if the rtinit()
> :in in_ifinit() fails, is this an intended behavior?
> :
>
>    It looks like in_ifinit() assumes that the (ia) is in some hash list
>    somewhere no matter what, but it should probably not be in the wrong
>    hash table.  I think it would be appropriate to move it to the correct
>    hash table for that failure case.
>
>    That whole code section around line 749 of in.c, in the original file,
>    is very confusing:
>
>        if (ifp->if_ioctl &&
>            (error = ifp->if_ioctl(ifp, SIOCSIFADDR, (caddr_t)ia, NULL))) {
>                lwkt_serialize_exit(ifp->if_serializer);
>                /* LIST_REMOVE(ia, ia_hash) is done in in_control */
>                ia->ia_addr = oldaddr;
>                if (ia->ia_addr.sin_family == AF_INET)
>                        LIST_INSERT_HEAD(INADDR_HASH(ia->ia_addr.sin_addr.s_addr
> ),
>                            ia, ia_hash);
>                return (error);
>        }
>
>    I'm not sure what is going on with (ia) there.

I mean following code segment in in_ifinit() (~839 in.c rev1.34):
		if ((error = rtinit(&ia->ia_ifa, RTM_ADD, flags)) != 0) {
			ia->ia_addr = oldaddr;
			return (error);
		}
ia's address is restored to oldaddr, but ia itself is left in hash
bucket indexed by newaddr's hash value.

Best Regards,
sephe

-- 
Live Free or Die





More information about the Submit mailing list