diff options
author | bms <bms@FreeBSD.org> | 2009-03-09 17:53:05 +0000 |
---|---|---|
committer | bms <bms@FreeBSD.org> | 2009-03-09 17:53:05 +0000 |
commit | 71233409ea6a2f4d751847c05e7aad9375278d94 (patch) | |
tree | 3d95180b6661648cd67dba62e6daefc7c8661793 /sys/netinet/in_var.h | |
parent | 2173e9258f5b3400a58f42bd91fd01c1efc4441d (diff) | |
download | FreeBSD-src-71233409ea6a2f4d751847c05e7aad9375278d94.zip FreeBSD-src-71233409ea6a2f4d751847c05e7aad9375278d94.tar.gz |
Merge IGMPv3 and Source-Specific Multicast (SSM) to the FreeBSD
IPv4 stack.
Diffs are minimized against p4.
PCS has been used for some protocol verification, more widespread
testing of recorded sources in Group-and-Source queries is needed.
sizeof(struct igmpstat) has changed.
__FreeBSD_version is bumped to 800070.
Diffstat (limited to 'sys/netinet/in_var.h')
-rw-r--r-- | sys/netinet/in_var.h | 330 |
1 files changed, 223 insertions, 107 deletions
diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index 73868ad..39964cf 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -35,6 +35,20 @@ #include <sys/queue.h> #include <sys/fnv_hash.h> +#include <sys/tree.h> + +struct igmp_ifinfo; +struct in_multi; +struct lltable; + +/* + * IPv4 per-interface state. + */ +struct in_ifinfo { + struct lltable *ii_llt; /* ARP state */ + struct igmp_ifinfo *ii_igmp; /* IGMP state */ + struct in_multi *ii_allhosts; /* 224.0.0.1 membership */ +}; /* * Interface address, Internet version. One of these structures @@ -151,77 +165,163 @@ do { \ (((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK) /* - * This information should be part of the ifnet structure but we don't wish - * to change that - as it might break a number of things + * Legacy IPv4 IGMP per-link structure. */ - struct router_info { struct ifnet *rti_ifp; int rti_type; /* type of router which is querier on this interface */ int rti_time; /* # of slow timeouts since last old query */ SLIST_ENTRY(router_info) rti_list; -#ifdef notyet - int rti_timev1; /* IGMPv1 querier present */ - int rti_timev2; /* IGMPv2 querier present */ - int rti_timer; /* report to general query */ - int rti_qrv; /* querier robustness */ -#endif }; /* - * Internet multicast address structure. There is one of these for each IP - * multicast group to which this host belongs on a given network interface. - * For every entry on the interface's if_multiaddrs list which represents - * an IP multicast group, there is one of these structures. They are also - * kept on a system-wide list to make it easier to keep our legacy IGMP code - * compatible with the rest of the world (see IN_FIRST_MULTI et al, below). + * Per-interface IGMP router version information. + */ +struct igmp_ifinfo { + LIST_ENTRY(igmp_ifinfo) igi_link; + struct ifnet *igi_ifp; /* interface this instance belongs to */ + uint32_t igi_version; /* IGMPv3 Host Compatibility Mode */ + uint32_t igi_v1_timer; /* IGMPv1 Querier Present timer (s) */ + uint32_t igi_v2_timer; /* IGMPv2 Querier Present timer (s) */ + uint32_t igi_v3_timer; /* IGMPv3 General Query (interface) timer (s)*/ + uint32_t igi_flags; /* IGMP per-interface flags */ + uint32_t igi_rv; /* IGMPv3 Robustness Variable */ + uint32_t igi_qi; /* IGMPv3 Query Interval (s) */ + uint32_t igi_qri; /* IGMPv3 Query Response Interval (s) */ + uint32_t igi_uri; /* IGMPv3 Unsolicited Report Interval (s) */ + SLIST_HEAD(,in_multi) igi_relinmhead; /* released groups */ + struct ifqueue igi_gq; /* queue of general query responses */ +}; + +#define IGIF_SILENT 0x00000001 /* Do not use IGMP on this ifp */ +#define IGIF_LOOPBACK 0x00000002 /* Send IGMP reports to loopback */ + +/* + * IPv4 multicast IGMP-layer source entry. + */ +struct ip_msource { + RB_ENTRY(ip_msource) ims_link; /* RB tree links */ + in_addr_t ims_haddr; /* host byte order */ + struct ims_st { + uint16_t ex; /* # of exclusive members */ + uint16_t in; /* # of inclusive members */ + } ims_st[2]; /* state at t0, t1 */ + uint8_t ims_stp; /* pending query */ +}; + +/* + * IPv4 multicast PCB-layer source entry. + */ +struct in_msource { + RB_ENTRY(ip_msource) ims_link; /* RB tree links */ + in_addr_t ims_haddr; /* host byte order */ + uint8_t imsl_st[2]; /* state before/at commit */ +}; + +RB_HEAD(ip_msource_tree, ip_msource); /* define struct ip_msource_tree */ + +static __inline int +ip_msource_cmp(const struct ip_msource *a, const struct ip_msource *b) +{ + + if (a->ims_haddr < b->ims_haddr) + return (-1); + if (a->ims_haddr == b->ims_haddr) + return (0); + return (1); +} +RB_PROTOTYPE(ip_msource_tree, ip_msource, ims_link, ip_msource_cmp); + +/* + * IPv4 multicast PCB-layer group filter descriptor. + */ +struct in_mfilter { + struct ip_msource_tree imf_sources; /* source list for (S,G) */ + u_long imf_nsrc; /* # of source entries */ + uint8_t imf_st[2]; /* state before/at commit */ +}; + +/* + * IPv4 group descriptor. + * + * For every entry on an ifnet's if_multiaddrs list which represents + * an IP multicast group, there is one of these structures. + * + * If any source filters are present, then a node will exist in the RB-tree + * to permit fast lookup by source whenever an operation takes place. + * This permits pre-order traversal when we issue reports. + * Source filter trees are kept separately from the socket layer to + * greatly simplify locking. + * + * When IGMPv3 is active, inm_timer is the response to group query timer. + * The state-change timer inm_sctimer is separate; whenever state changes + * for the group the state change record is generated and transmitted, + * and kept if retransmissions are necessary. + * + * FUTURE: inm_link is now only used when groups are being purged + * on a detaching ifnet. It could be demoted to a SLIST_ENTRY, but + * because it is at the very start of the struct, we can't do this + * w/o breaking the ABI for ifmcstat. */ struct in_multi { - LIST_ENTRY(in_multi) inm_link; /* queue macro glue */ + LIST_ENTRY(in_multi) inm_link; /* to-be-released by in_ifdetach */ struct in_addr inm_addr; /* IP multicast address, convenience */ struct ifnet *inm_ifp; /* back pointer to ifnet */ struct ifmultiaddr *inm_ifma; /* back pointer to ifmultiaddr */ - u_int inm_timer; /* IGMP membership report timer */ - u_int inm_state; /* state of the membership */ - struct router_info *inm_rti; /* router info*/ + u_int inm_timer; /* IGMPv1/v2 group / v3 query timer */ + u_int inm_state; /* state of the membership */ + void *inm_rti; /* unused, legacy field */ u_int inm_refcount; /* reference count */ -#ifdef notyet /* IGMPv3 source-specific multicast fields */ - TAILQ_HEAD(, in_msfentry) inm_msf; /* all active source filters */ - TAILQ_HEAD(, in_msfentry) inm_msf_record; /* recorded sources */ - TAILQ_HEAD(, in_msfentry) inm_msf_exclude; /* exclude sources */ - TAILQ_HEAD(, in_msfentry) inm_msf_include; /* include sources */ - /* XXX: should this lot go to the router_info structure? */ - /* XXX: can/should these be callouts? */ - /* IGMP protocol timers */ - int32_t inm_ti_curstate; /* current state timer */ - int32_t inm_ti_statechg; /* state change timer */ - /* IGMP report timers */ - uint16_t inm_rpt_statechg; /* state change report timer */ - uint16_t inm_rpt_toxx; /* fmode change report timer */ - /* IGMP protocol state */ - uint16_t inm_fmode; /* filter mode */ - uint32_t inm_recsrc_count; /* # of recorded sources */ - uint16_t inm_exclude_sock_count; /* # of exclude-mode sockets */ - uint16_t inm_gass_count; /* # of g-a-s queries */ -#endif + + /* New fields for IGMPv3 follow. */ + struct igmp_ifinfo *inm_igi; /* IGMP info */ + SLIST_ENTRY(in_multi) inm_nrele; /* to-be-released by IGMP */ + struct ip_msource_tree inm_srcs; /* tree of sources */ + u_long inm_nsrc; /* # of tree entries */ + + struct ifqueue inm_scq; /* queue of pending + * state-change packets */ + struct timeval inm_lastgsrtv; /* Time of last G-S-R query */ + uint16_t inm_sctimer; /* state-change timer */ + uint16_t inm_scrv; /* state-change rexmit count */ + + /* + * SSM state counters which track state at T0 (the time the last + * state-change report's RV timer went to zero) and T1 + * (time of pending report, i.e. now). + * Used for computing IGMPv3 state-change reports. Several refcounts + * are maintained here to optimize for common use-cases. + */ + struct inm_st { + uint16_t iss_fmode; /* IGMP filter mode */ + uint16_t iss_asm; /* # of ASM listeners */ + uint16_t iss_ex; /* # of exclusive members */ + uint16_t iss_in; /* # of inclusive members */ + uint16_t iss_rec; /* # of recorded sources */ + } inm_st[2]; /* state at t0, t1 */ }; -#ifdef notyet /* - * Internet multicast source filter list. This list is used to store - * IP multicast source addresses for each membership on an interface. - * TODO: Allocate these structures using UMA. - * TODO: Find an easier way of linking the struct into two lists at once. + * Helper function to derive the filter mode on a source entry + * from its internal counters. Predicates are: + * A source is only excluded if all listeners exclude it. + * A source is only included if no listeners exclude it, + * and at least one listener includes it. + * May be used by ifmcstat(8). */ -struct in_msfentry { - TAILQ_ENTRY(in_msfentry) isf_link; /* next filter in all-list */ - TAILQ_ENTRY(in_msfentry) isf_next; /* next filter in queue */ - struct in_addr isf_addr; /* the address of this source */ - uint16_t isf_refcount; /* reference count */ - uint16_t isf_reporttag; /* what to report to the IGMP router */ - uint16_t isf_rexmit; /* retransmission state/count */ -}; -#endif +static __inline uint8_t +ims_get_mode(const struct in_multi *inm, const struct ip_msource *ims, + uint8_t t) +{ + + t = !!t; + if (inm->inm_st[t].iss_ex > 0 && + inm->inm_st[t].iss_ex == ims->ims_st[t].ex) + return (MCAST_EXCLUDE); + else if (ims->ims_st[t].in > 0 && ims->ims_st[t].ex == 0) + return (MCAST_INCLUDE); + return (MCAST_UNDEFINED); +} #ifdef _KERNEL @@ -231,10 +331,10 @@ SYSCTL_DECL(_net_inet_ip); SYSCTL_DECL(_net_inet_raw); #endif -LIST_HEAD(in_multihead, in_multi); +LIST_HEAD(in_multihead, in_multi); /* XXX unused */ #ifdef VIMAGE_GLOBALS extern struct in_multihead in_multihead; -#endif +#endif /* BURN_BRIDGES */ /* * Lock macros for IPv4 layer multicast address lists. IPv4 lock goes @@ -246,74 +346,90 @@ extern struct mtx in_multi_mtx; #define IN_MULTI_LOCK() mtx_lock(&in_multi_mtx) #define IN_MULTI_UNLOCK() mtx_unlock(&in_multi_mtx) #define IN_MULTI_LOCK_ASSERT() mtx_assert(&in_multi_mtx, MA_OWNED) +#define IN_MULTI_UNLOCK_ASSERT() mtx_assert(&in_multi_mtx, MA_NOTOWNED) /* - * Structure used by macros below to remember position when stepping through - * all of the in_multi records. + * Function for looking up an in_multi record for an IPv4 multicast address + * on a given interface. ifp must be valid. If no record found, return NULL. + * The IN_MULTI_LOCK and IF_ADDR_LOCK on ifp must be held. */ -struct in_multistep { - struct in_multi *i_inm; -}; +static __inline struct in_multi * +inm_lookup_locked(struct ifnet *ifp, const struct in_addr ina) +{ + struct ifmultiaddr *ifma; + struct in_multi *inm; + + IN_MULTI_LOCK_ASSERT(); + IF_ADDR_LOCK_ASSERT(ifp); + + inm = NULL; + TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { + if (ifma->ifma_addr->sa_family == AF_INET) { + inm = (struct in_multi *)ifma->ifma_protospec; + if (inm->inm_addr.s_addr == ina.s_addr) + break; + inm = NULL; + } + } + return (inm); +} /* - * Macro for looking up the in_multi record for a given IP multicast address - * on a given interface. If no matching record is found, "inm" is set null. + * Wrapper for inm_lookup_locked(). + * The IF_ADDR_LOCK will be taken on ifp and released on return. */ -#define IN_LOOKUP_MULTI(addr, ifp, inm) \ - /* struct in_addr addr; */ \ - /* struct ifnet *ifp; */ \ - /* struct in_multi *inm; */ \ -do { \ - struct ifmultiaddr *ifma; \ -\ - IN_MULTI_LOCK_ASSERT(); \ - IF_ADDR_LOCK(ifp); \ - TAILQ_FOREACH(ifma, &((ifp)->if_multiaddrs), ifma_link) { \ - if (ifma->ifma_addr->sa_family == AF_INET \ - && ((struct sockaddr_in *)ifma->ifma_addr)->sin_addr.s_addr == \ - (addr).s_addr) \ - break; \ - } \ - (inm) = ifma ? ifma->ifma_protospec : 0; \ - IF_ADDR_UNLOCK(ifp); \ -} while(0) +static __inline struct in_multi * +inm_lookup(struct ifnet *ifp, const struct in_addr ina) +{ + struct in_multi *inm; + + IN_MULTI_LOCK_ASSERT(); + IF_ADDR_LOCK(ifp); + inm = inm_lookup_locked(ifp, ina); + IF_ADDR_UNLOCK(ifp); + + return (inm); +} + +/* Acquire an in_multi record. */ +static __inline void +inm_acquire_locked(struct in_multi *inm) +{ + + IN_MULTI_LOCK_ASSERT(); + ++inm->inm_refcount; +} /* - * Macro to step through all of the in_multi records, one at a time. - * The current position is remembered in "step", which the caller must - * provide. IN_FIRST_MULTI(), below, must be called to initialize "step" - * and get the first record. Both macros return a NULL "inm" when there - * are no remaining records. + * Return values for imo_multi_filter(). */ -#define IN_NEXT_MULTI(step, inm) \ - /* struct in_multistep step; */ \ - /* struct in_multi *inm; */ \ -do { \ - IN_MULTI_LOCK_ASSERT(); \ - if (((inm) = (step).i_inm) != NULL) \ - (step).i_inm = LIST_NEXT((step).i_inm, inm_link); \ -} while(0) - -#define IN_FIRST_MULTI(step, inm) \ - /* struct in_multistep step; */ \ - /* struct in_multi *inm; */ \ -do { \ - IN_MULTI_LOCK_ASSERT(); \ - (step).i_inm = LIST_FIRST(&V_in_multihead); \ - IN_NEXT_MULTI((step), (inm)); \ -} while(0) +#define MCAST_PASS 0 /* Pass */ +#define MCAST_NOTGMEMBER 1 /* This host not a member of group */ +#define MCAST_NOTSMEMBER 2 /* This host excluded source */ +#define MCAST_MUTED 3 /* [deprecated] */ struct rtentry; struct route; struct ip_moptions; -size_t imo_match_group(struct ip_moptions *, struct ifnet *, - struct sockaddr *); -struct in_msource *imo_match_source(struct ip_moptions *, size_t, - struct sockaddr *); -struct in_multi *in_addmulti(struct in_addr *, struct ifnet *); +int imo_multi_filter(const struct ip_moptions *, const struct ifnet *, + const struct sockaddr *, const struct sockaddr *); +void inm_commit(struct in_multi *); +void inm_clear_recorded(struct in_multi *); +void inm_print(const struct in_multi *); +int inm_record_source(struct in_multi *inm, const in_addr_t); +void inm_release(struct in_multi *); +void inm_release_locked(struct in_multi *); +struct in_multi * + in_addmulti(struct in_addr *, struct ifnet *); void in_delmulti(struct in_multi *); -void in_delmulti_locked(struct in_multi *); +int in_joingroup(struct ifnet *, const struct in_addr *, + /*const*/ struct in_mfilter *, struct in_multi **); +int in_joingroup_locked(struct ifnet *, const struct in_addr *, + /*const*/ struct in_mfilter *, struct in_multi **); +int in_leavegroup(struct in_multi *, /*const*/ struct in_mfilter *); +int in_leavegroup_locked(struct in_multi *, + /*const*/ struct in_mfilter *); int in_control(struct socket *, u_long, caddr_t, struct ifnet *, struct thread *); void in_rtqdrain(void); |