Index: share/man/man9/if_get.9 =================================================================== RCS file: /cvs/src/share/man/man9/if_get.9,v retrieving revision 1.2 diff -u -p -r1.2 if_get.9 --- share/man/man9/if_get.9 8 Dec 2015 18:35:51 -0000 1.2 +++ share/man/man9/if_get.9 30 Mar 2016 12:52:25 -0000 @@ -19,7 +19,9 @@ .Os .Sh NAME .Nm if_get , -.Nm if_put +.Nm if_put , +.Nm if_enter , +.Nm if_leave .Nd get an interface pointer from an interface index .Sh SYNOPSIS .In net/if.h @@ -27,36 +29,89 @@ .Fn if_get "unsigned int ifidx" .Ft void .Fn if_put "struct ifnet *ifp" +.Ft struct ifnet * +.Fn if_enter "struct ifref *ref" "unsigned int ifidx" +.Ft void +.Fn if_leave "struct ifref *ref" "struct ifnet *ifp" .Sh DESCRIPTION -The +The kernel maintains a unique identifier for each network interface +present in the system called the interface index. +This interface index can be used as an indirect reference to a +network interface, and can then be translated into a reference to +the actual ifnet structure as needed. +.Pp +If an interface is removed from the system, subsequent lookups using +it's interface index will no longer return a valid pointer to an +an ifnet structure. +.Pp +The index value +.Dv 0 +is never associated with an interface descriptor and can be used +to determine if an interface index is valid or not. +This supports the use of the interface index as a scope identifier +for IPv6 link local addresses, where scope 0 means no scope has +been specified. +It also supports the use of the interface index as the unique +identifier for network interfaces in SNMP applications as per RFC +2863. +.Pp .Fn if_get -function returns a pointer to the interface descriptor corresponding to the +returns a pointer to the interface descriptor corresponding to the unique index .Fa ifidx . This descriptor is guaranteed to be valid until .Fn if_put is called on the returned pointer. .Pp -The index value -.Dv 0 -is never associated with an interface descriptor and can be used to determine if -an interface index is valid or not. -.Pp -The .Fn if_put -function releases a reference on the interface descriptor pointed by +releases a reference on the interface descriptor pointed by .Fa ifp . If .Fa ifp is a .Dv NULL pointer, no action occurs. +.Pp +.Fn if_enter +returns a pointer to the interface descriptor corresponding to the unique index +.Fa ifidx . +The reference is stored in the ifref structure +.Fa ref +provided by the caller. +The descriptor is guaranteed to be valid until +.Fn if_leave +is called. +.Pp +.Fn if_leave +releases the reference to the interface descriptor +.Fa ifp +which was stored in +.Fa ref . +.Pp +All calls to +.Fn if_enter +must have a corresponding call to +.Fn if_leave , +even if the pointer to the interface descriptor is +.Dv NULL . .Sh CONTEXT -.Fn if_get , +.Fn if_get and .Fn if_put can be called during autoconf, from process context, or from interrupt context. +.Pp +.Fn if_enter +and +.Fn if_leave +can be called during autoconf, from process context, or from interrupt context. +Calling +.Fn if_leave +from a different context or on a different CPU to the preceding +.Fn if_enter +call will lead to undefined behaviour. .Sh RETURN VALUES .Fn if_get -returns a pointer to an interface descriptor if the index is valid, otherwise +and +.Fn if_enter +return a pointer to an interface descriptor if the index is valid, otherwise .Dv NULL . Index: sys/net/if.c =================================================================== RCS file: /cvs/src/sys/net/if.c,v retrieving revision 1.429 diff -u -p -r1.429 if.c --- sys/net/if.c 16 Mar 2016 12:08:09 -0000 1.429 +++ sys/net/if.c 30 Mar 2016 12:52:25 -0000 @@ -770,6 +770,7 @@ if_input_process(void *xmq) struct mbuf *m; struct ifnet *ifp; struct ifih *ifih; + struct ifref ref; struct srpl_iter i; int s; @@ -781,10 +782,10 @@ if_input_process(void *xmq) s = splnet(); while ((m = ml_dequeue(&ml)) != NULL) { - ifp = if_get(m->m_pkthdr.ph_ifidx); + ifp = if_enter(&ref, m->m_pkthdr.ph_ifidx); if (ifp == NULL) { m_freem(m); - continue; + goto next; } /* @@ -800,7 +801,8 @@ if_input_process(void *xmq) if (ifih == NULL) m_freem(m); - if_put(ifp); +next: + if_leave(&ref, ifp); } splx(s); } @@ -1482,7 +1484,7 @@ ifunit(const char *name) * Map interface index to interface structure pointer. */ struct ifnet * -if_get(unsigned int index) +if_enter(struct ifref *ref, unsigned int index) { struct if_map *if_map; struct srp *map; @@ -1492,19 +1494,33 @@ if_get(unsigned int index) if (index < if_map->limit) { map = (struct srp *)(if_map + 1); - ifp = srp_follow(&if_idxmap.map, if_map, &map[index]); - if (ifp != NULL) { - KASSERT(ifp->if_index == index); - if_ref(ifp); - } - srp_leave(&map[index], ifp); - } else + ref->srp = &map[index]; + ifp = srp_follow(&if_idxmap.map, if_map, ref->srp); + } else { + ref->srp = NULL; srp_leave(&if_idxmap.map, if_map); + } return (ifp); } struct ifnet * +if_get(unsigned int index) +{ + struct ifref ref; + struct ifnet *ifp; + + ifp = if_enter(&ref, index); + if (ifp != NULL) { + KASSERT(ifp->if_index == index); + if_ref(ifp); + } + if_leave(&ref, ifp); + + return (ifp); +} + +struct ifnet * if_ref(struct ifnet *ifp) { refcnt_take(&ifp->if_refcnt); @@ -1519,6 +1535,15 @@ if_put(struct ifnet *ifp) return; refcnt_rele_wake(&ifp->if_refcnt); +} + +void +if_leave(struct ifref *ref, struct ifnet *ifp) +{ + if (ref->srp == NULL) + return; + + srp_leave(ref->srp, ifp); } int Index: sys/net/if.h =================================================================== RCS file: /cvs/src/sys/net/if.h,v retrieving revision 1.176 diff -u -p -r1.176 if.h --- sys/net/if.h 2 Mar 2016 00:00:16 -0000 1.176 +++ sys/net/if.h 30 Mar 2016 12:52:25 -0000 @@ -455,6 +455,11 @@ struct if_parent { #ifdef _KERNEL struct socket; struct ifnet; +struct srp; + +struct ifref { + struct srp *srp; +}; void if_alloc_sadl(struct ifnet *); void if_free_sadl(struct ifnet *); @@ -477,6 +482,8 @@ int if_addgroup(struct ifnet *, const ch int if_delgroup(struct ifnet *, const char *); void if_group_routechange(struct sockaddr *, struct sockaddr *); struct ifnet *ifunit(const char *); +struct ifnet *if_enter(struct ifref *, unsigned int); +void if_leave(struct ifref *, struct ifnet *); struct ifnet *if_get(unsigned int); void if_put(struct ifnet *); void ifnewlladdr(struct ifnet *);