X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/ff6e181ae92fc6f1e89841290f461d1f2f9badd9..99c3a10404e5d1ef94397ab4df5a8b74711fc4d3:/bsd/net/if_bond.c diff --git a/bsd/net/if_bond.c b/bsd/net/if_bond.c index 0c3ec505a..1964a5524 100644 --- a/bsd/net/if_bond.c +++ b/bsd/net/if_bond.c @@ -1,14 +1,19 @@ /* - * Copyright (c) 2004 Apple Computer, Inc. All rights reserved. + * Copyright (c) 2004-2012 Apple Inc. All rights reserved. * - * @APPLE_LICENSE_HEADER_START@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER @@ -18,7 +23,7 @@ * Please see the License for the specific language governing rights and * limitations under the License. * - * @APPLE_LICENSE_HEADER_END@ + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ */ /* @@ -60,6 +65,7 @@ #include #include #include +#include #include #include @@ -73,8 +79,6 @@ #include #include -extern int dlil_input_packet(struct ifnet *, struct mbuf *, char *); - static struct ether_addr slow_proto_multicast = { IEEE8023AD_SLOW_PROTO_MULTICAST }; @@ -99,8 +103,6 @@ my_lck_grp_alloc_init(const char * grp_name) lck_grp_attr_t * grp_attrs; grp_attrs = lck_grp_attr_alloc_init(); - lck_grp_attr_setdefault(grp_attrs); - lck_grp_attr_setdefault(grp_attrs); grp = lck_grp_alloc_init(grp_name, grp_attrs); lck_grp_attr_free(grp_attrs); return (grp); @@ -113,7 +115,6 @@ my_lck_mtx_alloc_init(lck_grp_t * lck_grp) lck_mtx_t * lck_mtx; lck_attrs = lck_attr_alloc_init(); - lck_attr_setdefault(lck_attrs); lck_mtx = lck_mtx_alloc_init(lck_grp, lck_attrs); lck_attr_free(lck_attrs); return (lck_mtx); @@ -199,7 +200,7 @@ typedef struct partner_state_s { struct ifbond_s { TAILQ_ENTRY(ifbond_s) ifb_bond_list; int ifb_flags; - UInt32 ifb_retain_count; + SInt32 ifb_retain_count; char ifb_name[IFNAMSIZ]; struct ifnet * ifb_ifp; bpf_packet_func ifb_bpf_input; @@ -214,6 +215,8 @@ struct ifbond_s { struct ifmultiaddr * ifb_ifma_slow_proto; bondport_ref * ifb_distributing_array; int ifb_distributing_count; + int ifb_last_link_event; + int ifb_mode; /* LACP, STATIC */ }; struct media_info { @@ -509,11 +512,15 @@ packet_buffer_allocate(int length) /* leave room for ethernet header */ size = length + sizeof(struct ether_header); if (size > (int)MHLEN) { - /* XXX doesn't handle large payloads */ - printf("bond: packet_buffer_allocate size %d > max %d\n", size, MHLEN); - return (NULL); + if (size > (int)MCLBYTES) { + printf("bond: packet_buffer_allocate size %d > max %u\n", + size, MCLBYTES); + return (NULL); + } + m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR); + } else { + m = m_gethdr(M_WAITOK, MT_DATA); } - m = m_gethdr(M_WAITOK, MT_DATA); if (m == NULL) { return (NULL); } @@ -554,6 +561,8 @@ bondport_periodic_transmit_machine(bondport_ref p, LAEvent event, /** ** Transmit machine **/ +#define TRANSMIT_MACHINE_TX_IMMEDIATE ((void *)1) + static void bondport_transmit_machine(bondport_ref p, LAEvent event, void * event_data); @@ -630,18 +639,21 @@ bondport_disable_distributing(bondport_ref p); static __inline__ int bondport_collecting(bondport_ref p) { - return (lacp_actor_partner_state_collecting(p->po_actor_state)); + if (p->po_bond->ifb_mode == IF_BOND_MODE_LACP) { + return (lacp_actor_partner_state_collecting(p->po_actor_state)); + } + return (TRUE); } /** ** bond interface/dlil specific routines **/ -static int bond_clone_create(struct if_clone *, int); -static void bond_clone_destroy(struct ifnet *); -static int bond_input(struct mbuf *m, char *frame_header, struct ifnet *ifp, - u_long protocol_family, int sync_ok); +static int bond_clone_create(struct if_clone *, u_int32_t, void *); +static int bond_clone_destroy(struct ifnet *); +static int bond_input(ifnet_t ifp, protocol_family_t protocol, mbuf_t m, + char *frame_header); static int bond_output(struct ifnet *ifp, struct mbuf *m); -static int bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * addr); +static int bond_ioctl(struct ifnet *ifp, u_long cmd, void * addr); static int bond_set_bpf_tap(struct ifnet * ifp, bpf_tap_mode mode, bpf_packet_func func); static int bond_attach_protocol(struct ifnet *ifp); @@ -656,7 +668,7 @@ static struct if_clone bond_cloner = IF_CLONE_INITIALIZER(BONDNAME, bond_clone_destroy, 0, BOND_MAXUNIT); -static void interface_link_event(struct ifnet * ifp, u_long event_code); +static void interface_link_event(struct ifnet * ifp, u_int32_t event_code); static int siocsifmtu(struct ifnet * ifp, int mtu) @@ -665,7 +677,7 @@ siocsifmtu(struct ifnet * ifp, int mtu) bzero(&ifr, sizeof(ifr)); ifr.ifr_mtu = mtu; - return (dlil_ioctl(0, ifp, SIOCSIFMTU, (caddr_t)&ifr)); + return (ifnet_ioctl(ifp, 0, SIOCSIFMTU, &ifr)); } static int @@ -675,7 +687,7 @@ siocgifdevmtu(struct ifnet * ifp, struct ifdevmtu * ifdm_p) int error; bzero(&ifr, sizeof(ifr)); - error = dlil_ioctl(0, ifp, SIOCGIFDEVMTU, (caddr_t)&ifr); + error = ifnet_ioctl(ifp, 0, SIOCGIFDEVMTU, &ifr); if (error == 0) { *ifdm_p = ifr.ifr_devmtu; } @@ -714,8 +726,9 @@ ifbond_release(ifbond_ref ifb) printf("ifbond_release(%s) removing multicast\n", ifb->ifb_name); } - (void)if_delmultiaddr(ifb->ifb_ifma_slow_proto, 0); - ifma_release(ifb->ifb_ifma_slow_proto); + (void) if_delmulti_anon(ifb->ifb_ifma_slow_proto->ifma_ifp, + ifb->ifb_ifma_slow_proto->ifma_addr); + IFMA_REMREF(ifb->ifb_ifma_slow_proto); } if (ifb->ifb_distributing_array != NULL) { FREE(ifb->ifb_distributing_array, M_BOND); @@ -854,7 +867,7 @@ interface_media_info(struct ifnet * ifp) bzero(&mi, sizeof(mi)); bzero(&ifmr, sizeof(ifmr)); - if (dlil_ioctl(0, ifp, SIOCGIFMEDIA, (caddr_t)&ifmr) == 0) { + if (ifnet_ioctl(ifp, 0, SIOCGIFMEDIA, &ifmr) == 0) { if (ifmr.ifm_count != 0) { mi.mi_status = ifmr.ifm_status; mi.mi_active = ifmr.ifm_active; @@ -863,33 +876,6 @@ interface_media_info(struct ifnet * ifp) return (mi); } -/** - ** interface utility functions - **/ -static __inline__ struct ifaddr * -ifindex_get_ifaddr(int i) -{ - if (i > if_index || i == 0) { - return (NULL); - } - return (ifnet_addrs[i - 1]); -} - -static __inline__ struct ifaddr * -ifp_get_ifaddr(struct ifnet * ifp) -{ - return (ifindex_get_ifaddr(ifp->if_index)); -} - -static __inline__ struct sockaddr_dl * -ifp_get_sdl(struct ifnet * ifp) -{ - struct ifaddr * ifa; - - ifa = ifp_get_ifaddr(ifp); - return ((struct sockaddr_dl *)(ifa->ifa_addr)); -} - static int if_siflladdr(struct ifnet * ifp, const struct ether_addr * ea_p) { @@ -902,11 +888,7 @@ if_siflladdr(struct ifnet * ifp, const struct ether_addr * ea_p) ifr.ifr_addr.sa_family = AF_UNSPEC; ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; ether_addr_copy(ifr.ifr_addr.sa_data, ea_p); -#if 0 - snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", ifp->if_name, - ifp->if_unit); -#endif 0 - return (dlil_ioctl(0, ifp, SIOCSIFLLADDR, (caddr_t)&ifr)); + return (ifnet_ioctl(ifp, 0, SIOCSIFLLADDR, &ifr)); } /** @@ -926,9 +908,6 @@ bond_globals_create(lacp_system_priority sys_pri, TAILQ_INIT(&b->ifbond_list); b->system = *sys; b->system_priority = sys_pri; -#if 0 - b->verbose = 1; -#endif 0 return (b); } @@ -953,7 +932,6 @@ bond_globals_init(void) for (i = 0; i < 4; i++) { char ifname[IFNAMSIZ+1]; snprintf(ifname, sizeof(ifname), "en%d", i); - /* XXX ifunit() needs to return a reference on the ifp */ ifp = ifunit(ifname); if (ifp != NULL) { break; @@ -961,8 +939,7 @@ bond_globals_init(void) } b = NULL; if (ifp != NULL) { - b = bond_globals_create(0x8000, - (lacp_system_ref)LLADDR(ifp_get_sdl(ifp))); + b = bond_globals_create(0x8000, (lacp_system_ref)ifnet_lladdr(ifp)); } bond_lock(); if (g_bond != NULL) { @@ -1061,7 +1038,7 @@ bond_setmulti(struct ifnet * ifp) bondport_ref p; bond_lock(); - ifb = ifp->if_private; + ifb = ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb) || TAILQ_EMPTY(&ifb->ifb_port_list)) { bond_unlock(); @@ -1086,25 +1063,28 @@ bond_setmulti(struct ifnet * ifp) if (error != 0) { printf("bond_setmulti(%s): " "multicast_list_program(%s%d) failed, %d\n", - ifb->ifb_name, port_ifp->if_name, - port_ifp->if_unit, error); + ifb->ifb_name, ifnet_name(port_ifp), + ifnet_unit(port_ifp), error); result = error; } } bond_lock(); signal_done: - ifbond_release(ifb); ifbond_signal(ifb, "bond_setmulti"); bond_unlock(); + ifbond_release(ifb); return (result); } -static void +static int bond_clone_attach(void) { - if_clone_attach(&bond_cloner); + int error; + + if ((error = if_clone_attach(&bond_cloner)) != 0) + return error; bond_lock_init(); - return; + return 0; } static int @@ -1123,8 +1103,7 @@ ifbond_add_slow_proto_multicast(ifbond_ref ifb) sdl.sdl_nlen = 0; sdl.sdl_alen = sizeof(slow_proto_multicast); bcopy(&slow_proto_multicast, sdl.sdl_data, sizeof(slow_proto_multicast)); - error = if_addmulti(ifb->ifb_ifp, (struct sockaddr *)&sdl, - &ifma); + error = if_addmulti_anon(ifb->ifb_ifp, (struct sockaddr *)&sdl, &ifma); if (error == 0) { ifb->ifb_ifma_slow_proto = ifma; } @@ -1132,84 +1111,90 @@ ifbond_add_slow_proto_multicast(ifbond_ref ifb) } static int -bond_clone_create(struct if_clone * ifc, int unit) +bond_clone_create(struct if_clone * ifc, u_int32_t unit, __unused void *params) { - int error; - ifbond_ref ifb; - struct ifnet * ifp; - - error = bond_globals_init(); - if (error != 0) { - return (error); - } - - ifb = _MALLOC(sizeof(ifbond), M_BOND, M_WAITOK); - if (ifb == NULL) { - return (ENOMEM); - } - bzero(ifb, sizeof(*ifb)); - - ifbond_retain(ifb); - TAILQ_INIT(&ifb->ifb_port_list); - TAILQ_INIT(&ifb->ifb_lag_list); - ifb->ifb_key = unit + 1; - - /* use the interface name as the unique id for ifp recycle */ - if ((u_long)snprintf(ifb->ifb_name, sizeof(ifb->ifb_name), "%s%d", - ifc->ifc_name, unit) >= sizeof(ifb->ifb_name)) { - ifbond_release(ifb); - return (EINVAL); - } - error = dlil_if_acquire(APPLE_IF_FAM_BOND, - ifb->ifb_name, - strlen(ifb->ifb_name), - &ifp); - if (error) { - ifbond_release(ifb); - return (error); - } - ifb->ifb_ifp = ifp; - ifp->if_name = ifc->ifc_name; - ifp->if_unit = unit; - ifp->if_family = APPLE_IF_FAM_BOND; - ifp->if_private = NULL; - ifp->if_ioctl = bond_ioctl; - ifp->if_set_bpf_tap = bond_set_bpf_tap; - ifp->if_free = bond_if_free; - ifp->if_output = bond_output; - ifp->if_hwassist = 0; - ifp->if_addrlen = ETHER_ADDR_LEN; - ifp->if_baudrate = 0; - ifp->if_type = IFT_IEEE8023ADLAG; - ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX; - ifp->if_mtu = 0; - - /* XXX ethernet specific */ - ifp->if_broadcast.length = ETHER_ADDR_LEN; - bcopy(etherbroadcastaddr, ifp->if_broadcast.u.buffer, ETHER_ADDR_LEN); - - error = dlil_if_attach(ifp); - if (error != 0) { - dlil_if_release(ifp); - ifbond_release(ifb); - return (error); - } - error = ifbond_add_slow_proto_multicast(ifb); - if (error != 0) { - printf("bond_clone_create(%s): " - "failed to add slow_proto multicast, %d\n", - ifb->ifb_name, error); - } - - /* attach as ethernet */ - bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); - - bond_lock(); - ifp->if_private = ifb; - TAILQ_INSERT_HEAD(&g_bond->ifbond_list, ifb, ifb_bond_list); - bond_unlock(); - - return (0); + int error; + ifbond_ref ifb; + ifnet_t ifp; + struct ifnet_init_params bond_init; + + error = bond_globals_init(); + if (error != 0) { + return (error); + } + + ifb = _MALLOC(sizeof(ifbond), M_BOND, M_WAITOK); + if (ifb == NULL) { + return (ENOMEM); + } + bzero(ifb, sizeof(*ifb)); + + ifbond_retain(ifb); + TAILQ_INIT(&ifb->ifb_port_list); + TAILQ_INIT(&ifb->ifb_lag_list); + ifb->ifb_key = unit + 1; + + /* use the interface name as the unique id for ifp recycle */ + if ((u_int32_t)snprintf(ifb->ifb_name, sizeof(ifb->ifb_name), "%s%d", + ifc->ifc_name, unit) >= sizeof(ifb->ifb_name)) { + ifbond_release(ifb); + return (EINVAL); + } + + bzero(&bond_init, sizeof(bond_init)); + bond_init.uniqueid = ifb->ifb_name; + bond_init.uniqueid_len = strlen(ifb->ifb_name); + bond_init.name = ifc->ifc_name; + bond_init.unit = unit; + bond_init.family = IFNET_FAMILY_BOND; + bond_init.type = IFT_IEEE8023ADLAG; + bond_init.output = bond_output; + bond_init.demux = ether_demux; + bond_init.add_proto = ether_add_proto; + bond_init.del_proto = ether_del_proto; + bond_init.check_multi = ether_check_multi; + bond_init.framer = ether_frameout; + bond_init.ioctl = bond_ioctl; + bond_init.set_bpf_tap = bond_set_bpf_tap; + bond_init.detach = bond_if_free; + bond_init.broadcast_addr = etherbroadcastaddr; + bond_init.broadcast_len = ETHER_ADDR_LEN; + bond_init.softc = ifb; + error = ifnet_allocate(&bond_init, &ifp); + + if (error) { + ifbond_release(ifb); + return (error); + } + + ifb->ifb_ifp = ifp; + ifnet_set_offload(ifp, 0); + ifnet_set_addrlen(ifp, ETHER_ADDR_LEN); /* XXX ethernet specific */ + ifnet_set_flags(ifp, IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX, 0xffff); + ifnet_set_baudrate(ifp, 0); + ifnet_set_mtu(ifp, 0); + + error = ifnet_attach(ifp, NULL); + if (error != 0) { + ifnet_release(ifp); + ifbond_release(ifb); + return (error); + } + error = ifbond_add_slow_proto_multicast(ifb); + if (error != 0) { + printf("bond_clone_create(%s): " + "failed to add slow_proto multicast, %d\n", + ifb->ifb_name, error); + } + + /* attach as ethernet */ + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); + + bond_lock(); + TAILQ_INSERT_HEAD(&g_bond->ifbond_list, ifb, ifb_bond_list); + bond_unlock(); + + return (0); } static void @@ -1244,36 +1229,34 @@ bond_if_detach(struct ifnet * ifp) { int error; - error = dlil_if_detach(ifp); - if (error != DLIL_WAIT_FOR_FREE) { - if (error) { - printf("bond_if_detach %s%d: dlil_if_detach failed, %d\n", - ifp->if_name, ifp->if_unit, error); - } - bond_if_free(ifp); + error = ifnet_detach(ifp); + if (error) { + printf("bond_if_detach %s%d: ifnet_detach failed, %d\n", + ifnet_name(ifp), ifnet_unit(ifp), error); } + return; } -static void +static int bond_clone_destroy(struct ifnet * ifp) { ifbond_ref ifb; bond_lock(); - ifb = ifp->if_private; - if (ifb == NULL || ifp->if_type != IFT_IEEE8023ADLAG) { + ifb = ifnet_softc(ifp); + if (ifb == NULL || ifnet_type(ifp) != IFT_IEEE8023ADLAG) { bond_unlock(); - return; + return 0; } if (ifbond_flags_if_detaching(ifb)) { bond_unlock(); - return; + return 0; } bond_remove(ifb); bond_unlock(); bond_if_detach(ifp); - return; + return 0; } static int @@ -1282,7 +1265,7 @@ bond_set_bpf_tap(struct ifnet * ifp, bpf_tap_mode mode, bpf_packet_func func) ifbond_ref ifb; bond_lock(); - ifb = ifp->if_private; + ifb = ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { bond_unlock(); return (ENODEV); @@ -1323,7 +1306,7 @@ ether_header_hash(struct ether_header * eh_p) } static struct mbuf * -S_mbuf_skip_to_offset(struct mbuf * m, long * offset) +S_mbuf_skip_to_offset(struct mbuf * m, int32_t * offset) { int len; @@ -1356,7 +1339,7 @@ make_uint32(u_char c0, u_char c1, u_char c2, u_char c3) #endif /* BYTE_ORDER == LITTLE_ENDIAN */ static int -S_mbuf_copy_uint32(struct mbuf * m, long offset, uint32_t * val) +S_mbuf_copy_uint32(struct mbuf * m, int32_t offset, uint32_t * val) { struct mbuf * current; u_char * current_data; @@ -1406,7 +1389,7 @@ ip_header_hash(struct mbuf * m) struct in_addr ip_dst; struct in_addr ip_src; u_char ip_p; - long offset; + int32_t offset; struct mbuf * orig_m = m; /* find the IP protocol field relative to the start of the packet */ @@ -1447,7 +1430,7 @@ ipv6_header_hash(struct mbuf * m) { u_char * data; int i; - long offset; + int32_t offset; struct mbuf * orig_m = m; uint32_t * scan; uint32_t val; @@ -1491,6 +1474,8 @@ bond_output(struct ifnet * ifp, struct mbuf * m) uint32_t h; ifbond_ref ifb; struct ifnet * port_ifp = NULL; + int err; + struct flowadv adv = { FADV_SUCCESS }; if (m == 0) { return (0); @@ -1519,7 +1504,7 @@ bond_output(struct ifnet * ifp, struct mbuf * m) } } bond_lock(); - ifb = ifp->if_private; + ifb = ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb) || ifb->ifb_distributing_count == 0) { goto done; @@ -1538,7 +1523,17 @@ bond_output(struct ifnet * ifp, struct mbuf * m) } bond_bpf_output(ifp, m, bpf_func); - return (dlil_output(port_ifp, 0, m, NULL, NULL, 1)); + err = dlil_output(port_ifp, PF_BOND, m, NULL, NULL, 1, &adv); + + if (err == 0) { + if (adv.code == FADV_FLOW_CONTROLLED) { + err = EQFULL; + } else if (adv.code == FADV_SUSPENDED) { + err = EQSUSPENDED; + } + } + + return (err); done: bond_unlock(); @@ -1577,11 +1572,12 @@ static void bond_receive_lacpdu(struct mbuf * m, struct ifnet * port_ifp) { struct ifnet * bond_ifp = NULL; + ifbond_ref ifb; int event_code = 0; bondport_ref p; bond_lock(); - if ((port_ifp->if_eflags & IFEF_BOND) == 0) { + if ((ifnet_eflags(port_ifp) & IFEF_BOND) == 0) { goto done; } p = bond_lookup_port(port_ifp); @@ -1591,13 +1587,31 @@ bond_receive_lacpdu(struct mbuf * m, struct ifnet * port_ifp) if (p->po_enabled == 0) { goto done; } + ifb = p->po_bond; + if (ifb->ifb_mode != IF_BOND_MODE_LACP) { + goto done; + } bondport_receive_lacpdu(p, (lacpdu_ref)m->m_data); - if (ifbond_selection(p->po_bond)) { - event_code = (p->po_bond->ifb_active_lag == NULL) + if (ifbond_selection(ifb)) { + event_code = (ifb->ifb_active_lag == NULL) ? KEV_DL_LINK_OFF : KEV_DL_LINK_ON; /* XXX need to take a reference on bond_ifp */ - bond_ifp = p->po_bond->ifb_ifp; + bond_ifp = ifb->ifb_ifp; + ifb->ifb_last_link_event = event_code; + } + else { + event_code = (ifb->ifb_active_lag == NULL) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + if (event_code != ifb->ifb_last_link_event) { + if (g_bond->verbose) { + timestamp_printf("%s: (receive) generating LINK event\n", + ifb->ifb_name); + } + bond_ifp = ifb->ifb_ifp; + ifb->ifb_last_link_event = event_code; + } } done: @@ -1620,12 +1634,13 @@ bond_receive_la_marker_pdu(struct mbuf * m, struct ifnet * port_ifp) goto failed; } bond_lock(); - if ((port_ifp->if_eflags & IFEF_BOND) == 0) { + if ((ifnet_eflags(port_ifp) & IFEF_BOND) == 0) { bond_unlock(); goto failed; } p = bond_lookup_port(port_ifp); - if (p == NULL || p->po_enabled == 0) { + if (p == NULL || p->po_enabled == 0 + || p->po_bond->ifb_mode != IF_BOND_MODE_LACP) { bond_unlock(); goto failed; } @@ -1641,8 +1656,8 @@ bond_receive_la_marker_pdu(struct mbuf * m, struct ifnet * port_ifp) } static int -bond_input(struct mbuf * m, char * frame_header, struct ifnet * port_ifp, - __unused u_long protocol_family, __unused int sync_ok) +bond_input(ifnet_t port_ifp, __unused protocol_family_t protocol, mbuf_t m, + char * frame_header) { bpf_packet_func bpf_func; const struct ether_header * eh_p; @@ -1703,7 +1718,7 @@ bond_input(struct mbuf * m, char * frame_header, struct ifnet * port_ifp, } } bond_lock(); - if ((port_ifp->if_eflags & IFEF_BOND) == 0) { + if ((ifnet_eflags(port_ifp) & IFEF_BOND) == 0) { goto done; } p = bond_lookup_port(port_ifp); @@ -1728,7 +1743,8 @@ bond_input(struct mbuf * m, char * frame_header, struct ifnet * port_ifp, } m->m_pkthdr.rcvif = ifp; bond_bpf_input(ifp, m, eh_p, bpf_func); - dlil_input_packet(ifp, m, frame_header); + m->m_pkthdr.header = frame_header; + dlil_input_packet_list(ifp, m); return 0; done: @@ -1746,7 +1762,7 @@ bondport_get_name(bondport_ref p) static __inline__ int bondport_get_index(bondport_ref p) { - return (p->po_ifp->if_index); + return (ifnet_index(p->po_ifp)); } static void @@ -1760,7 +1776,7 @@ bondport_slow_proto_transmit(bondport_ref p, packet_buffer_ref buf) bcopy(&slow_proto_multicast, &eh_p->ether_dhost, sizeof(eh_p->ether_dhost)); bcopy(&p->po_saved_addr, eh_p->ether_shost, sizeof(eh_p->ether_shost)); eh_p->ether_type = htons(IEEE8023AD_SLOW_PROTO_ETHERTYPE); - error = dlil_output(p->po_ifp, 0, buf, NULL, NULL, 1); + error = ifnet_output_raw(p->po_ifp, PF_BOND, buf); if (error != 0) { printf("bondport_slow_proto_transmit(%s) failed %d\n", bondport_get_name(p), error); @@ -1792,6 +1808,20 @@ bondport_timer_process_func(devtimer_ref timer, : KEV_DL_LINK_ON; /* XXX need to take a reference on bond_ifp */ bond_ifp = p->po_bond->ifb_ifp; + p->po_bond->ifb_last_link_event = event_code; + } + else { + event_code = (p->po_bond->ifb_active_lag == NULL) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + if (event_code != p->po_bond->ifb_last_link_event) { + if (g_bond->verbose) { + timestamp_printf("%s: (timer) generating LINK event\n", + p->po_bond->ifb_name); + } + bond_ifp = p->po_bond->ifb_ifp; + p->po_bond->ifb_last_link_event = event_code; + } } devtimer_release(timer); bond_unlock(); @@ -1826,8 +1856,8 @@ bondport_create(struct ifnet * port_ifp, lacp_port_priority priority, } bzero(p, sizeof(*p)); multicast_list_init(&p->po_multicast); - if ((u_long)snprintf(p->po_name, sizeof(p->po_name), "%s%d", - port_ifp->if_name, port_ifp->if_unit) + if ((u_int32_t)snprintf(p->po_name, sizeof(p->po_name), "%s%d", + ifnet_name(port_ifp), ifnet_unit(port_ifp)) >= sizeof(p->po_name)) { printf("if_bond: name too large\n"); *ret_error = EINVAL; @@ -1840,7 +1870,7 @@ bondport_create(struct ifnet * port_ifp, lacp_port_priority priority, goto failed; } /* remember the current interface MTU so it can be restored */ - p->po_devmtu.ifdm_current = port_ifp->if_mtu; + p->po_devmtu.ifdm_current = ifnet_mtu(port_ifp); p->po_ifp = port_ifp; p->po_media_info = interface_media_info(port_ifp); p->po_current_while_timer = devtimer_create(bondport_timer_process_func, p); @@ -1906,6 +1936,20 @@ bondport_invalidate_timers(bondport_ref p) devtimer_invalidate(p->po_transmit_timer); } +/* + * Function: bondport_cancel_timers + * Purpose: + * Cancel all of the timers for the bondport. + */ +static void +bondport_cancel_timers(bondport_ref p) +{ + devtimer_cancel(p->po_current_while_timer); + devtimer_cancel(p->po_periodic_timer); + devtimer_cancel(p->po_wait_while_timer); + devtimer_cancel(p->po_transmit_timer); +} + static void bondport_free(bondport_ref p) { @@ -1926,8 +1970,8 @@ bondport_free(bondport_ref p) static __inline__ int bond_device_mtu(struct ifnet * ifp, ifbond_ref ifb) { - return (((int)ifp->if_mtu > ifb->ifb_altmtu) - ? (int)ifp->if_mtu : ifb->ifb_altmtu); + return (((int)ifnet_mtu(ifp) > ifb->ifb_altmtu) + ? (int)ifnet_mtu(ifp) : ifb->ifb_altmtu); } static int @@ -1936,12 +1980,11 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) int devmtu; int error = 0; int event_code = 0; + int first = FALSE; ifbond_ref ifb; - struct sockaddr_dl * ifb_sdl; bondport_ref * new_array = NULL; bondport_ref * old_array = NULL; bondport_ref p; - struct sockaddr_dl * port_sdl; int progress = 0; /* pre-allocate space for new port */ @@ -1950,7 +1993,7 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) return (error); } bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { bond_unlock(); bondport_free(p); @@ -1985,7 +2028,7 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) goto signal_done; } ifnet_lock_exclusive(port_ifp); - if ((port_ifp->if_eflags & (IFEF_VLAN | IFEF_BOND)) != 0) { + if ((ifnet_eflags(port_ifp) & (IFEF_VLAN | IFEF_BOND)) != 0) { /* interface already has VLAN's, or is part of bond */ ifnet_lock_done(port_ifp); error = EBUSY; @@ -1993,49 +2036,60 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) } /* mark the interface busy */ + /* can't use ifnet_set_eflags because that takes the lock */ port_ifp->if_eflags |= IFEF_BOND; ifnet_lock_done(port_ifp); - port_sdl = ifp_get_sdl(port_ifp); - ifb_sdl = ifp_get_sdl(ifp); - if (TAILQ_EMPTY(&ifb->ifb_port_list)) { - ifp->if_hwassist = port_ifp->if_hwassist; - ifp->if_flags |= IFF_RUNNING; + ifnet_set_offload(ifp, ifnet_offload(port_ifp)); + ifnet_set_flags(ifp, IFF_RUNNING, IFF_RUNNING); if (ifbond_flags_lladdr(ifb) == FALSE) { - /* first port added to bond determines bond's ethernet address */ - ether_addr_copy(LLADDR(ifb_sdl), LLADDR(port_sdl)); - ifb_sdl->sdl_type = IFT_ETHER; - ifb_sdl->sdl_alen = ETHER_ADDR_LEN; + first = TRUE; } } else { - if (ifp->if_hwassist != port_ifp->if_hwassist) { + ifnet_offload_t ifp_offload; + ifnet_offload_t port_ifp_offload; + + ifp_offload = ifnet_offload(ifp); + port_ifp_offload = ifnet_offload(port_ifp); + if (ifp_offload != port_ifp_offload) { + ifnet_offload_t offload; + + offload = ifp_offload & port_ifp_offload; printf("bond_add_interface(%s, %s) " - "hwassist values don't match 0x%x != 0x%x\n", + "hwassist values don't match 0x%x != 0x%x, using 0x%x instead\n", ifb->ifb_name, bondport_get_name(p), - ifp->if_hwassist, port_ifp->if_hwassist); + ifp_offload, port_ifp_offload, offload); /* * XXX * if the bond has VLAN's, we can't simply change the hwassist * field behind its back: this needs work */ - ifp->if_hwassist = 0; + ifnet_set_offload(ifp, offload); } } p->po_bond = ifb; /* remember the port's ethernet address so it can be restored */ - ether_addr_copy(&p->po_saved_addr, LLADDR(port_sdl)); + ether_addr_copy(&p->po_saved_addr, ifnet_lladdr(port_ifp)); /* add it to the list of ports */ TAILQ_INSERT_TAIL(&ifb->ifb_port_list, p, po_port_list); ifb->ifb_port_count++; /* set the default MTU */ - if (ifp->if_mtu == 0) { - ifp->if_mtu = ETHERMTU; + if (ifnet_mtu(ifp) == 0) { + ifnet_set_mtu(ifp, ETHERMTU); } bond_unlock(); + + + /* first port added to bond determines bond's ethernet address */ + if (first) { + ifnet_set_lladdr_and_type(ifp, ifnet_lladdr(port_ifp), ETHER_ADDR_LEN, + IFT_ETHER); + } + progress |= BOND_ADD_PROGRESS_IN_LIST; /* allocate a larger distributing array */ @@ -2074,9 +2128,9 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) } /* mark the interface up */ - ifnet_set_flags(port_ifp, IFF_UP, IFF_UP); + ifnet_set_flags(port_ifp, IFF_UP, IFF_UP); - error = dlil_ioctl(0, port_ifp, SIOCSIFFLAGS, (caddr_t)NULL); + error = ifnet_ioctl(port_ifp, 0, SIOCSIFFLAGS, NULL); if (error != 0) { printf("bond_add_interface(%s, %s): SIOCSIFFLAGS failed %d\n", ifb->ifb_name, bondport_get_name(p), error); @@ -2085,7 +2139,7 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) /* re-program the port's ethernet address */ error = if_siflladdr(port_ifp, - (const struct ether_addr *)LLADDR(ifb_sdl)); + (const struct ether_addr *)ifnet_lladdr(ifp)); if (error != 0) { /* port doesn't support setting the link address */ printf("bond_add_interface(%s, %s): if_siflladdr failed %d\n", @@ -2107,16 +2161,31 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) old_array = ifb->ifb_distributing_array; ifb->ifb_distributing_array = new_array; - /* clear the busy state, and wakeup anyone waiting */ - ifbond_signal(ifb, "bond_add_interface"); - bondport_start(p); + if (ifb->ifb_mode == IF_BOND_MODE_LACP) { + bondport_start(p); - /* check if we need to generate a link status event */ - if (ifbond_selection(ifb)) { - event_code = (ifb->ifb_active_lag == NULL) - ? KEV_DL_LINK_OFF - : KEV_DL_LINK_ON; + /* check if we need to generate a link status event */ + if (ifbond_selection(ifb)) { + event_code = (ifb->ifb_active_lag == NULL) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + ifb->ifb_last_link_event = event_code; + } + } + else { + /* are we adding the first distributing interface? */ + if (media_active(&p->po_media_info)) { + if (ifb->ifb_distributing_count == 0) { + ifb->ifb_last_link_event = event_code = KEV_DL_LINK_ON; + } + bondport_enable_distributing(p); + } + else { + bondport_disable_distributing(p); + } } + /* clear the busy state, and wakeup anyone waiting */ + ifbond_signal(ifb, "bond_add_interface"); bond_unlock(); if (event_code != 0) { interface_link_event(ifp, event_code); @@ -2129,6 +2198,11 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) failed: bond_assert_lock_not_held(); + /* if this was the first port to be added, clear our address */ + if (first) { + ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_IEEE8023ADLAG); + } + if (new_array != NULL) { FREE(new_array, M_BOND); } @@ -2150,8 +2224,8 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) error1 = siocsifmtu(port_ifp, p->po_devmtu.ifdm_current); if (error1 != 0) { printf("bond_add_interface(%s, %s): SIOCSIFMTU %d failed %d\n", - ifb->ifb_name, bondport_get_name(p), p->po_devmtu.ifdm_current, - error1); + ifb->ifb_name, bondport_get_name(p), + p->po_devmtu.ifdm_current, error1); } } bond_lock(); @@ -2159,24 +2233,17 @@ bond_add_interface(struct ifnet * ifp, struct ifnet * port_ifp) TAILQ_REMOVE(&ifb->ifb_port_list, p, po_port_list); ifb->ifb_port_count--; } - ifnet_lock_exclusive(port_ifp); - port_ifp->if_eflags &= ~IFEF_BOND; - ifnet_lock_done(port_ifp); + ifnet_set_eflags(ifp, 0, IFEF_BOND); if (TAILQ_EMPTY(&ifb->ifb_port_list)) { ifb->ifb_altmtu = 0; - ifp->if_mtu = 0; - ifp->if_hwassist = 0; - if (ifbond_flags_lladdr(ifb) == FALSE) { - bzero(LLADDR(ifb_sdl), ETHER_ADDR_LEN); - ifb_sdl->sdl_type = IFT_IEEE8023ADLAG; - ifb_sdl->sdl_alen = 0; - } + ifnet_set_mtu(ifp, 0); + ifnet_set_offload(ifp, 0); } signal_done: - ifbond_release(ifb); ifbond_signal(ifb, "bond_add_interface"); bond_unlock(); + ifbond_release(ifb); bondport_free(p); return (error); } @@ -2188,11 +2255,12 @@ bond_remove_interface(ifbond_ref ifb, struct ifnet * port_ifp) int error = 0; int event_code = 0; bondport_ref head_port; - struct sockaddr_dl * ifb_sdl; struct ifnet * ifp; - int new_link_address = 0; + int last = FALSE; + int new_link_address = FALSE; bondport_ref p; lacp_actor_partner_state s; + int was_distributing; bond_assert_lock_held(); @@ -2207,68 +2275,82 @@ bond_remove_interface(ifbond_ref ifb, struct ifnet * port_ifp) } /* de-select it and remove it from the lists */ + was_distributing = bondport_flags_distributing(p); bondport_disable_distributing(p); - bondport_set_selected(p, SelectedState_UNSELECTED); - active_lag = bondport_remove_from_LAG(p); - TAILQ_REMOVE(&ifb->ifb_port_list, p, po_port_list); - ifb->ifb_port_count--; + if (ifb->ifb_mode == IF_BOND_MODE_LACP) { + bondport_set_selected(p, SelectedState_UNSELECTED); + active_lag = bondport_remove_from_LAG(p); + /* invalidate timers here while holding the bond_lock */ + bondport_invalidate_timers(p); - /* invalidate timers here while holding the bond_lock */ - bondport_invalidate_timers(p); + /* announce that we're Individual now */ + s = p->po_actor_state; + s = lacp_actor_partner_state_set_individual(s); + s = lacp_actor_partner_state_set_not_collecting(s); + s = lacp_actor_partner_state_set_not_distributing(s); + s = lacp_actor_partner_state_set_out_of_sync(s); + p->po_actor_state = s; + bondport_flags_set_ntt(p); + } - /* announce that we're Individual now */ - s = p->po_actor_state; - s = lacp_actor_partner_state_set_individual(s); - s = lacp_actor_partner_state_set_not_collecting(s); - s = lacp_actor_partner_state_set_not_distributing(s); - s = lacp_actor_partner_state_set_out_of_sync(s); - p->po_actor_state = s; - bondport_flags_set_ntt(p); + TAILQ_REMOVE(&ifb->ifb_port_list, p, po_port_list); + ifb->ifb_port_count--; ifp = ifb->ifb_ifp; - ifb_sdl = ifp_get_sdl(ifp); head_port = TAILQ_FIRST(&ifb->ifb_port_list); if (head_port == NULL) { - ifp->if_flags &= ~IFF_RUNNING; + ifnet_set_flags(ifp, 0, IFF_RUNNING); if (ifbond_flags_lladdr(ifb) == FALSE) { - ifb_sdl->sdl_type = IFT_IEEE8023ADLAG; - ifb_sdl->sdl_alen = 0; - bzero(LLADDR(ifb_sdl), ETHER_ADDR_LEN); + last = TRUE; } - ifp->if_hwassist = 0; - ifp->if_mtu = 0; + ifnet_set_offload(ifp, 0); + ifnet_set_mtu(ifp, 0); ifb->ifb_altmtu = 0; } else if (ifbond_flags_lladdr(ifb) == FALSE - && bcmp(&p->po_saved_addr, LLADDR(ifb_sdl), + && bcmp(&p->po_saved_addr, ifnet_lladdr(ifp), ETHER_ADDR_LEN) == 0) { - /* this port gave the bond its ethernet address, switch to new one */ - ether_addr_copy(LLADDR(ifb_sdl), &head_port->po_saved_addr); - ifb_sdl->sdl_type = IFT_ETHER; - ifb_sdl->sdl_alen = ETHER_ADDR_LEN; - new_link_address = 1; + new_link_address = TRUE; } /* check if we need to generate a link status event */ - if (ifbond_selection(ifb) || active_lag) { - event_code = (ifb->ifb_active_lag == NULL) - ? KEV_DL_LINK_OFF - : KEV_DL_LINK_ON; + if (ifb->ifb_mode == IF_BOND_MODE_LACP ) { + if (ifbond_selection(ifb) || active_lag) { + event_code = (ifb->ifb_active_lag == NULL) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + ifb->ifb_last_link_event = event_code; + } + bondport_transmit_machine(p, LAEventStart, + TRANSMIT_MACHINE_TX_IMMEDIATE); + } + else { + /* are we removing the last distributing interface? */ + if (was_distributing && ifb->ifb_distributing_count == 0) { + ifb->ifb_last_link_event = event_code = KEV_DL_LINK_OFF; + } } - bond_unlock(); - bondport_transmit_machine(p, LAEventStart, (void *)1); + bond_unlock(); - if (new_link_address) { + if (last) { + ifnet_set_lladdr_and_type(ifp, NULL, 0, IFT_IEEE8023ADLAG); + } + else if (new_link_address) { struct ifnet * scan_ifp; bondport_ref scan_port; /* ifbond_wait() allows port list traversal without holding the lock */ + /* this port gave the bond its ethernet address, switch to new one */ + ifnet_set_lladdr_and_type(ifp, + &head_port->po_saved_addr, ETHER_ADDR_LEN, + IFT_ETHER); + /* re-program each port with the new link address */ TAILQ_FOREACH(scan_port, &ifb->ifb_port_list, po_port_list) { scan_ifp = scan_port->po_ifp; error = if_siflladdr(scan_ifp, - (const struct ether_addr *) LLADDR(ifb_sdl)); + (const struct ether_addr *) ifnet_lladdr(ifp)); if (error != 0) { printf("bond_remove_interface(%s, %s): " "if_siflladdr (%s) failed %d\n", @@ -2302,15 +2384,118 @@ bond_remove_interface(ifbond_ref ifb, struct ifnet * port_ifp) } bond_lock(); - ifbond_release(ifb); bondport_free(p); - ifnet_lock_exclusive(port_ifp); - port_ifp->if_eflags &= ~IFEF_BOND; - ifnet_lock_done(port_ifp); + ifnet_set_eflags(port_ifp, 0, IFEF_BOND); + /* release this bondport's reference to the ifbond */ + ifbond_release(ifb); signal_done: ifbond_signal(ifb, "bond_remove_interface"); - ifbond_release(ifb); /* a second release for the second reference */ + ifbond_release(ifb); + return (error); +} + +static void +bond_set_lacp_mode(ifbond_ref ifb) +{ + bondport_ref p; + + TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) { + bondport_disable_distributing(p); + bondport_start(p); + } + return; +} + +static void +bond_set_static_mode(ifbond_ref ifb) +{ + bondport_ref p; + lacp_actor_partner_state s; + + TAILQ_FOREACH(p, &ifb->ifb_port_list, po_port_list) { + bondport_disable_distributing(p); + bondport_set_selected(p, SelectedState_UNSELECTED); + (void)bondport_remove_from_LAG(p); + bondport_cancel_timers(p); + + /* announce that we're Individual now */ + s = p->po_actor_state; + s = lacp_actor_partner_state_set_individual(s); + s = lacp_actor_partner_state_set_not_collecting(s); + s = lacp_actor_partner_state_set_not_distributing(s); + s = lacp_actor_partner_state_set_out_of_sync(s); + p->po_actor_state = s; + bondport_flags_set_ntt(p); + bondport_transmit_machine(p, LAEventStart, + TRANSMIT_MACHINE_TX_IMMEDIATE); + /* clear state */ + p->po_actor_state = 0; + bzero(&p->po_partner_state, sizeof(p->po_partner_state)); + + if (media_active(&p->po_media_info)) { + bondport_enable_distributing(p); + } + else { + bondport_disable_distributing(p); + } + } + return; +} + +static int +bond_set_mode(struct ifnet * ifp, int mode) +{ + int error = 0; + int event_code = 0; + ifbond_ref ifb; + + bond_lock(); + ifb = (ifbond_ref)ifnet_softc(ifp); + if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { + bond_unlock(); + return ((ifb == NULL) ? EOPNOTSUPP : EBUSY); + } + if (ifb->ifb_mode == mode) { + bond_unlock(); + return (0); + } + + ifbond_retain(ifb); + ifbond_wait(ifb, "bond_set_mode"); + + /* verify (again) that the mode is actually different */ + if (ifb->ifb_mode == mode) { + /* nothing to do */ + goto signal_done; + } + + ifb->ifb_mode = mode; + if (mode == IF_BOND_MODE_LACP) { + bond_set_lacp_mode(ifb); + + /* check if we need to generate a link status event */ + if (ifbond_selection(ifb)) { + event_code = (ifb->ifb_active_lag == NULL) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + } + } else { + bond_set_static_mode(ifb); + event_code = (ifb->ifb_distributing_count == 0) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + } + ifb->ifb_last_link_event = event_code; + + signal_done: + ifbond_signal(ifb, "bond_set_mode"); + bond_unlock(); + ifbond_release(ifb); + + if (event_code != 0) { + interface_link_event(ifp, event_code); + } return (error); } @@ -2329,10 +2514,11 @@ bond_get_status(ifbond_ref ifb, struct if_bond_req * ibr_p, user_addr_t datap) return (EINVAL); } ibsr->ibsr_key = ifb->ifb_key; + ibsr->ibsr_mode = ifb->ifb_mode; ibsr->ibsr_total = ifb->ifb_port_count; dst = proc_is64bit(current_proc()) ? ibsr->ibsr_ibsru.ibsru_buffer64 - : CAST_USER_ADDR_T(ibsr->ibsr_ibsru.ibsru_buffer32); + : CAST_USER_ADDR_T(ibsr->ibsr_ibsru.ibsru_buffer); if (dst == USER_ADDR_NULL) { /* just want to know how many there are */ goto done; @@ -2352,16 +2538,23 @@ bond_get_status(ifbond_ref ifb, struct if_bond_req * ibr_p, user_addr_t datap) bzero(&ibs, sizeof(ibs)); strncpy(ibs.ibs_if_name, port->po_name, sizeof(ibs.ibs_if_name)); ibs.ibs_port_priority = port->po_priority; - ibs.ibs_state = port->po_actor_state; - ibs.ibs_selected_state = port->po_selected; - ps = &port->po_partner_state; - ibps_p = &ibs.ibs_partner_state; - ibps_p->ibps_system = ps->ps_lag_info.li_system; - ibps_p->ibps_system_priority = ps->ps_lag_info.li_system_priority; - ibps_p->ibps_key = ps->ps_lag_info.li_key; - ibps_p->ibps_port = ps->ps_port; - ibps_p->ibps_port_priority = ps->ps_port_priority; - ibps_p->ibps_state = ps->ps_state; + if (ifb->ifb_mode == IF_BOND_MODE_LACP) { + ibs.ibs_state = port->po_actor_state; + ibs.ibs_selected_state = port->po_selected; + ps = &port->po_partner_state; + ibps_p = &ibs.ibs_partner_state; + ibps_p->ibps_system = ps->ps_lag_info.li_system; + ibps_p->ibps_system_priority = ps->ps_lag_info.li_system_priority; + ibps_p->ibps_key = ps->ps_lag_info.li_key; + ibps_p->ibps_port = ps->ps_port; + ibps_p->ibps_port_priority = ps->ps_port_priority; + ibps_p->ibps_state = ps->ps_state; + } + else { + /* fake the selected information */ + ibs.ibs_selected_state = bondport_flags_distributing(port) + ? SelectedState_SELECTED : SelectedState_UNSELECTED; + } error = copyout(&ibs, dst, sizeof(ibs)); if (error != 0) { break; @@ -2384,24 +2577,6 @@ static int bond_set_promisc(__unused struct ifnet *ifp) { int error = 0; -#if 0 - ifbond_ref ifb = ifp->if_private; - - - if ((ifp->if_flags & IFF_PROMISC) != 0) { - if ((ifb->ifb_flags & IFBF_PROMISC) == 0) { - error = ifnet_set_promiscuous(ifb->ifb_p, 1); - if (error == 0) - ifb->ifb_flags |= IFBF_PROMISC; - } - } else { - if ((ifb->ifb_flags & IFBF_PROMISC) != 0) { - error = ifnet_set_promiscuous(ifb->ifb_p, 0); - if (error == 0) - ifb->ifb_flags &= ~IFBF_PROMISC; - } - } -#endif 0 return (error); } @@ -2458,7 +2633,7 @@ bond_set_mtu(struct ifnet * ifp, int mtu, int isdevmtu) int old_max; bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { error = (ifb == NULL) ? EOPNOTSUPP : EBUSY; goto done; @@ -2467,7 +2642,7 @@ bond_set_mtu(struct ifnet * ifp, int mtu, int isdevmtu) ifbond_wait(ifb, "bond_set_mtu"); /* check again */ - if (ifp->if_private == NULL || ifbond_flags_if_detaching(ifb)) { + if (ifnet_softc(ifp) == NULL || ifbond_flags_if_detaching(ifb)) { error = EBUSY; goto signal_done; } @@ -2482,13 +2657,13 @@ bond_set_mtu(struct ifnet * ifp, int mtu, int isdevmtu) goto signal_done; } if (isdevmtu) { - new_max = (mtu > (int)ifp->if_mtu) ? mtu : (int)ifp->if_mtu; + new_max = (mtu > (int)ifnet_mtu(ifp)) ? mtu : (int)ifnet_mtu(ifp); } else { new_max = (mtu > ifb->ifb_altmtu) ? mtu : ifb->ifb_altmtu; } - old_max = ((int)ifp->if_mtu > ifb->ifb_altmtu) - ? (int)ifp->if_mtu : ifb->ifb_altmtu; + old_max = ((int)ifnet_mtu(ifp) > ifb->ifb_altmtu) + ? (int)ifnet_mtu(ifp) : ifb->ifb_altmtu; if (new_max != old_max) { /* we can safely walk the list of port without the lock held */ bond_unlock(); @@ -2504,7 +2679,7 @@ bond_set_mtu(struct ifnet * ifp, int mtu, int isdevmtu) ifb->ifb_altmtu = mtu; } else { - ifp->if_mtu = mtu; + ifnet_set_mtu(ifp, mtu); } } @@ -2518,18 +2693,18 @@ bond_set_mtu(struct ifnet * ifp, int mtu, int isdevmtu) } static int -bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) +bond_ioctl(struct ifnet *ifp, u_long cmd, void * data) { int error = 0; struct if_bond_req ibr; struct ifaddr * ifa; ifbond_ref ifb; struct ifreq * ifr; - struct ifmediareq64 *ifmr; + struct ifmediareq *ifmr; struct ifnet * port_ifp = NULL; user_addr_t user_addr; - if (ifp->if_type != IFT_IEEE8023ADLAG) { + if (ifnet_type(ifp) != IFT_IEEE8023ADLAG) { return (EOPNOTSUPP); } ifr = (struct ifreq *)data; @@ -2540,28 +2715,35 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) ifnet_set_flags(ifp, IFF_UP, IFF_UP); break; + case SIOCGIFMEDIA32: case SIOCGIFMEDIA64: - case SIOCGIFMEDIA: bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { bond_unlock(); return (ifb == NULL ? EOPNOTSUPP : EBUSY); } - ifmr = (struct ifmediareq64 *)data; + ifmr = (struct ifmediareq *)data; ifmr->ifm_current = IFM_ETHER; ifmr->ifm_mask = 0; ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; ifmr->ifm_count = 1; - if (ifb->ifb_active_lag != NULL) { - ifmr->ifm_active = ifb->ifb_active_lag->lag_active_media; + if (ifb->ifb_mode == IF_BOND_MODE_LACP) { + if (ifb->ifb_active_lag != NULL) { + ifmr->ifm_active = ifb->ifb_active_lag->lag_active_media; + ifmr->ifm_status |= IFM_ACTIVE; + } + } + else if (ifb->ifb_distributing_count > 0) { + ifmr->ifm_active + = ifb->ifb_distributing_array[0]->po_media_info.mi_active; ifmr->ifm_status |= IFM_ACTIVE; } bond_unlock(); - user_addr = (cmd == SIOCGIFMEDIA64) - ? ifmr->ifm_ifmu.ifmu_ulist64 - : CAST_USER_ADDR_T(ifmr->ifm_ifmu.ifmu_ulist32); + user_addr = (cmd == SIOCGIFMEDIA64) ? + ((struct ifmediareq64 *)ifmr)->ifmu_ulist : + CAST_USER_ADDR_T(((struct ifmediareq32 *)ifmr)->ifmu_ulist); if (user_addr != USER_ADDR_NULL) { error = copyout(&ifmr->ifm_current, user_addr, @@ -2576,7 +2758,7 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) case SIOCGIFDEVMTU: bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { bond_unlock(); error = (ifb == NULL) ? EOPNOTSUPP : EBUSY; @@ -2590,7 +2772,7 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) case SIOCGIFALTMTU: bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { bond_unlock(); error = (ifb == NULL) ? EOPNOTSUPP : EBUSY; @@ -2618,18 +2800,18 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) switch (ibr.ibr_op) { case IF_BOND_OP_ADD_INTERFACE: case IF_BOND_OP_REMOVE_INTERFACE: - /* XXX ifunit() needs to return a reference on the ifp */ port_ifp = ifunit(ibr.ibr_ibru.ibru_if_name); if (port_ifp == NULL) { error = ENXIO; break; } - if (port_ifp->if_type != IFT_ETHER) { + if (ifnet_type(port_ifp) != IFT_ETHER) { error = EPROTONOSUPPORT; break; } break; case IF_BOND_OP_SET_VERBOSE: + case IF_BOND_OP_SET_MODE: break; default: error = EOPNOTSUPP; @@ -2644,7 +2826,7 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) break; case IF_BOND_OP_REMOVE_INTERFACE: bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { bond_unlock(); return (ifb == NULL ? EOPNOTSUPP : EBUSY); @@ -2662,8 +2844,22 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) g_bond->verbose = ibr.ibr_ibru.ibru_int_val; bond_unlock(); break; + case IF_BOND_OP_SET_MODE: + switch (ibr.ibr_ibru.ibru_int_val) { + case IF_BOND_MODE_LACP: + case IF_BOND_MODE_STATIC: + break; + default: + error = EINVAL; + break; + } + if (error != 0) { + break; + } + error = bond_set_mode(ifp, ibr.ibr_ibru.ibru_int_val); + break; } - break; + break; /* SIOCSIFBOND */ case SIOCGIFBOND: user_addr = proc_is64bit(current_proc()) @@ -2683,7 +2879,7 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) break; } bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL || ifbond_flags_if_detaching(ifb)) { bond_unlock(); return (ifb == NULL ? EOPNOTSUPP : EBUSY); @@ -2694,7 +2890,7 @@ bond_ioctl(struct ifnet *ifp, u_int32_t cmd, void * data) break; } bond_unlock(); - break; + break; /* SIOCGIFBOND */ case SIOCSIFLLADDR: error = EOPNOTSUPP; @@ -2726,33 +2922,28 @@ bond_if_free(struct ifnet * ifp) return; } bond_lock(); - ifb = (ifbond_ref)ifp->if_private; + ifb = (ifbond_ref)ifnet_softc(ifp); if (ifb == NULL) { bond_unlock(); return; } - ifp->if_private = NULL; ifbond_release(ifb); bond_unlock(); - dlil_if_release(ifp); + ifnet_release(ifp); return; } static void -bond_event(struct ifnet * port_ifp, struct kev_msg * event) +bond_handle_event(struct ifnet * port_ifp, int event_code) { struct ifnet * bond_ifp = NULL; - int event_code = 0; + ifbond_ref ifb; + int old_distributing_count; bondport_ref p; - struct media_info media_info; + struct media_info media_info = { 0, 0}; - if (event->vendor_code != KEV_VENDOR_APPLE - || event->kev_class != KEV_NETWORK_CLASS - || event->kev_subclass != KEV_DL_SUBCLASS) { - return; - } - switch (event->event_code) { - case KEV_DL_IF_DETACHING: + switch (event_code) { + case KEV_DL_IF_DETACHED: break; case KEV_DL_LINK_OFF: case KEV_DL_LINK_ON: @@ -2767,9 +2958,11 @@ bond_event(struct ifnet * port_ifp, struct kev_msg * event) bond_unlock(); return; } - switch (event->event_code) { - case KEV_DL_IF_DETACHING: - bond_remove_interface(p->po_bond, p->po_ifp); + ifb = p->po_bond; + old_distributing_count = ifb->ifb_distributing_count; + switch (event_code) { + case KEV_DL_IF_DETACHED: + bond_remove_interface(ifb, p->po_ifp); break; case KEV_DL_LINK_OFF: case KEV_DL_LINK_ON: @@ -2780,13 +2973,48 @@ bond_event(struct ifnet * port_ifp, struct kev_msg * event) break; } /* generate a link-event */ - if (ifbond_selection(p->po_bond)) { - event_code = (p->po_bond->ifb_active_lag == NULL) - ? KEV_DL_LINK_OFF - : KEV_DL_LINK_ON; - /* XXX need to take a reference on bond_ifp */ - bond_ifp = p->po_bond->ifb_ifp; + if (ifb->ifb_mode == IF_BOND_MODE_LACP) { + if (ifbond_selection(ifb)) { + event_code = (ifb->ifb_active_lag == NULL) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + /* XXX need to take a reference on bond_ifp */ + bond_ifp = ifb->ifb_ifp; + ifb->ifb_last_link_event = event_code; + } + else { + event_code = (ifb->ifb_active_lag == NULL) + ? KEV_DL_LINK_OFF + : KEV_DL_LINK_ON; + if (event_code != ifb->ifb_last_link_event) { + if (g_bond->verbose) { + timestamp_printf("%s: (event) generating LINK event\n", + ifb->ifb_name); + } + bond_ifp = ifb->ifb_ifp; + ifb->ifb_last_link_event = event_code; + } + } } + else { + /* + * if the distributing array membership changed from 0 <-> !0 + * generate a link event + */ + if (old_distributing_count == 0 + && ifb->ifb_distributing_count != 0) { + event_code = KEV_DL_LINK_ON; + } + else if (old_distributing_count != 0 + && ifb->ifb_distributing_count == 0) { + event_code = KEV_DL_LINK_OFF; + } + if (event_code != 0 && event_code != ifb->ifb_last_link_event) { + bond_ifp = ifb->ifb_ifp; + ifb->ifb_last_link_event = event_code; + } + } + bond_unlock(); if (bond_ifp != NULL) { interface_link_event(bond_ifp, event_code); @@ -2795,23 +3023,55 @@ bond_event(struct ifnet * port_ifp, struct kev_msg * event) } static void -interface_link_event(struct ifnet * ifp, u_long event_code) +bond_event(struct ifnet * port_ifp, __unused protocol_family_t protocol, + const struct kev_msg * event) +{ + int event_code; + + if (event->vendor_code != KEV_VENDOR_APPLE + || event->kev_class != KEV_NETWORK_CLASS + || event->kev_subclass != KEV_DL_SUBCLASS) { + return; + } + event_code = event->event_code; + switch (event_code) { + case KEV_DL_LINK_OFF: + case KEV_DL_LINK_ON: + /* we only care about link status changes */ + bond_handle_event(port_ifp, event_code); + break; + default: + break; + } + return; +} + +static errno_t +bond_detached(ifnet_t port_ifp, __unused protocol_family_t protocol) +{ + bond_handle_event(port_ifp, KEV_DL_IF_DETACHED); + return (0); +} + +static void +interface_link_event(struct ifnet * ifp, u_int32_t event_code) { struct { struct kern_event_msg header; - u_long unit; + u_int32_t unit; char if_name[IFNAMSIZ]; } event; + bzero(&event, sizeof(event)); event.header.total_size = sizeof(event); event.header.vendor_code = KEV_VENDOR_APPLE; event.header.kev_class = KEV_NETWORK_CLASS; event.header.kev_subclass = KEV_DL_SUBCLASS; event.header.event_code = event_code; - event.header.event_data[0] = ifp->if_family; - event.unit = (u_long) ifp->if_unit; - strncpy(event.if_name, ifp->if_name, IFNAMSIZ); - dlil_event(ifp, &event.header); + event.header.event_data[0] = ifnet_family(ifp); + event.unit = (u_int32_t) ifnet_unit(ifp); + strncpy(event.if_name, ifnet_name(ifp), IFNAMSIZ); + ifnet_event(ifp, &event.header); return; } @@ -2828,21 +3088,18 @@ interface_link_event(struct ifnet * ifp, u_long event_code) static int bond_attach_protocol(struct ifnet *ifp) { - int error; - struct dlil_proto_reg_str reg; + int error; + struct ifnet_attach_proto_param reg; bzero(®, sizeof(reg)); - TAILQ_INIT(®.demux_desc_head); - reg.interface_family = ifp->if_family; - reg.unit_number = ifp->if_unit; reg.input = bond_input; reg.event = bond_event; - reg.protocol_family = PF_BOND; + reg.detached = bond_detached; - error = dlil_attach_protocol(®); + error = ifnet_attach_protocol(ifp, PF_BOND, ®); if (error) { - printf("bond over %s%d: dlil_attach_protocol failed, %d\n", - ifp->if_name, ifp->if_unit, error); + printf("bond over %s%d: ifnet_attach_protocol failed, %d\n", + ifnet_name(ifp), ifnet_unit(ifp), error); } return (error); } @@ -2857,10 +3114,10 @@ bond_detach_protocol(struct ifnet *ifp) { int error; - error = dlil_detach_protocol(ifp, PF_BOND); + error = ifnet_detach_protocol(ifp, PF_BOND); if (error) { - printf("bond over %s%d: dlil_detach_protocol failed, %d\n", - ifp->if_name, ifp->if_unit, error); + printf("bond over %s%d: ifnet_detach_protocol failed, %d\n", + ifnet_name(ifp), ifnet_unit(ifp), error); } return (error); } @@ -2868,57 +3125,52 @@ bond_detach_protocol(struct ifnet *ifp) /* * DLIL interface family functions */ -extern int ether_add_if(struct ifnet *ifp); -extern int ether_del_if(struct ifnet *ifp); -extern int ether_init_if(struct ifnet *ifp); -extern int ether_add_proto_old(struct ifnet *ifp, u_long protocol_family, - struct ddesc_head_str *desc_head); - -extern int ether_attach_inet(struct ifnet *ifp, u_long protocol_family); -extern int ether_detach_inet(struct ifnet *ifp, u_long protocol_family); -extern int ether_attach_inet6(struct ifnet *ifp, u_long protocol_family); -extern int ether_detach_inet6(struct ifnet *ifp, u_long protocol_family); +extern int ether_attach_inet(ifnet_t ifp, protocol_family_t protocol_family); +extern void ether_detach_inet(ifnet_t ifp, protocol_family_t protocol_family); +extern int ether_attach_inet6(ifnet_t ifp, protocol_family_t protocol_family); +extern void ether_detach_inet6(ifnet_t ifp, protocol_family_t protocol_family); +extern int ether_attach_at(ifnet_t ifp, protocol_family_t protocol_family); +extern void ether_detach_at(ifnet_t ifp, protocol_family_t protocol_family); __private_extern__ int bond_family_init(void) { int error=0; - struct dlil_ifmod_reg_str ifmod_reg; - - bzero(&ifmod_reg, sizeof(ifmod_reg)); - ifmod_reg.add_if = ether_add_if; - ifmod_reg.del_if = ether_del_if; - ifmod_reg.init_if = NULL; - ifmod_reg.add_proto = ether_add_proto_old; - ifmod_reg.del_proto = ether_del_proto; - ifmod_reg.ifmod_ioctl = ether_ioctl; - ifmod_reg.shutdown = NULL; - - if (dlil_reg_if_modules(APPLE_IF_FAM_BOND, &ifmod_reg)) { - printf("WARNING: bond_family_init -- " - "Can't register if family modules\n"); - error = EIO; - goto done; - } - error = dlil_reg_proto_module(PF_INET, APPLE_IF_FAM_BOND, + error = proto_register_plumber(PF_INET, APPLE_IF_FAM_BOND, ether_attach_inet, ether_detach_inet); if (error != 0) { - printf("bond: dlil_reg_proto_module failed for AF_INET6 error=%d\n", + printf("bond: proto_register_plumber failed for AF_INET error=%d\n", error); goto done; } - - error = dlil_reg_proto_module(PF_INET6, APPLE_IF_FAM_BOND, +#if INET6 + error = proto_register_plumber(PF_INET6, APPLE_IF_FAM_BOND, ether_attach_inet6, ether_detach_inet6); if (error != 0) { - printf("bond: dlil_reg_proto_module failed for AF_INET6 error=%d\n", + printf("bond: proto_register_plumber failed for AF_INET6 error=%d\n", + error); + goto done; + } +#endif +#if NETAT + error = proto_register_plumber(PF_APPLETALK, APPLE_IF_FAM_BOND, + ether_attach_at, + ether_detach_at); + if (error != 0) { + printf("bond: proto_register_plumber failed for AppleTalk error=%d\n", error); goto done; } - bond_clone_attach(); +#endif + error = bond_clone_attach(); + if (error != 0) { + printf("bond: proto_register_plumber failed bond_clone_attach error=%d\n", + error); + goto done; + } done: return (error); @@ -3179,7 +3431,7 @@ ifbond_set_max_active(ifbond_ref bond, int max_active) } return; } -#endif 0 +#endif static int ifbond_all_ports_ready(ifbond_ref bond) @@ -3287,24 +3539,33 @@ bondport_link_status_changed(bondport_ref p) timestamp_printf("[%s] Link DOWN\n", bondport_get_name(p)); } } - if (media_active(&p->po_media_info) - && bond->ifb_active_lag != NULL - && p->po_lag == bond->ifb_active_lag - && p->po_selected != SelectedState_UNSELECTED) { - if (media_speed(&p->po_media_info) != p->po_lag->lag_active_media) { - if (g_bond->verbose) { - timestamp_printf("[%s] Port speed %d differs from LAG %d\n", - bondport_get_name(p), - media_speed(&p->po_media_info), - link_speed(p->po_lag->lag_active_media)); + if (bond->ifb_mode == IF_BOND_MODE_LACP) { + if (media_active(&p->po_media_info) + && bond->ifb_active_lag != NULL + && p->po_lag == bond->ifb_active_lag + && p->po_selected != SelectedState_UNSELECTED) { + if (media_speed(&p->po_media_info) != p->po_lag->lag_active_media) { + if (g_bond->verbose) { + timestamp_printf("[%s] Port speed %d differs from LAG %d\n", + bondport_get_name(p), + media_speed(&p->po_media_info), + link_speed(p->po_lag->lag_active_media)); + } + bondport_set_selected(p, SelectedState_UNSELECTED); } - bondport_set_selected(p, SelectedState_UNSELECTED); + } + bondport_receive_machine(p, LAEventMediaChange, NULL); + bondport_mux_machine(p, LAEventMediaChange, NULL); + bondport_periodic_transmit_machine(p, LAEventMediaChange, NULL); + } + else { + if (media_active(&p->po_media_info)) { + bondport_enable_distributing(p); + } + else { + bondport_disable_distributing(p); } } - bondport_receive_machine(p, LAEventMediaChange, NULL); - bondport_mux_machine(p, LAEventMediaChange, NULL); - bondport_periodic_transmit_machine(p, LAEventMediaChange, NULL); - return; } @@ -4117,7 +4378,7 @@ bondport_periodic_transmit_machine(bondport_ref p, LAEvent event, **/ static int bondport_can_transmit(bondport_ref p, int32_t current_secs, - long * next_secs) + __darwin_time_t * next_secs) { if (p->po_last_transmit_secs != current_secs) { p->po_last_transmit_secs = current_secs; @@ -4151,7 +4412,7 @@ bondport_transmit_machine(bondport_ref p, LAEvent event, if (p->po_periodic_interval == 0 || bondport_flags_ntt(p) == 0) { break; } - if (event_data != NULL) { + if (event_data == TRANSMIT_MACHINE_TX_IMMEDIATE) { /* we're going away, transmit the packet no matter what */ } else if (bondport_can_transmit(p, devtimer_current_secs(), @@ -4170,7 +4431,7 @@ bondport_transmit_machine(bondport_ref p, LAEvent event, if (g_bond->verbose > 0) { timestamp_printf("[%s] Transmit Timer Deadline %d secs\n", bondport_get_name(p), - next_tick_time.tv_sec); + (int)next_tick_time.tv_sec); } } break;