+
+ /* first port added to bond determines bond's ethernet address */
+ if (first) {
+ ifnet_set_lladdr_and_type(ifp, IF_LLADDR(port_ifp), ETHER_ADDR_LEN,
+ IFT_ETHER);
+ }
+
+ control_flags |= PORT_CONTROL_FLAGS_IN_LIST;
+
+ /* allocate a larger distributing array */
+ new_array = (bondport_ref *)
+ _MALLOC(sizeof(*new_array) * ifb->ifb_port_count, M_BOND, M_WAITOK);
+ if (new_array == NULL) {
+ error = ENOMEM;
+ goto failed;
+ }
+
+ /* attach our BOND "protocol" to the interface */
+ error = bond_attach_protocol(port_ifp);
+ if (error) {
+ goto failed;
+ }
+ control_flags |= PORT_CONTROL_FLAGS_PROTO_ATTACHED;
+
+ /* attach our BOND interface filter */
+ error = bond_attach_filter(port_ifp, &filter);
+ if (error != 0) {
+ goto failed;
+ }
+ control_flags |= PORT_CONTROL_FLAGS_FILTER_ATTACHED;
+
+ /* set the interface MTU */
+ devmtu = bond_device_mtu(ifp, ifb);
+ error = siocsifmtu(port_ifp, devmtu);
+ if (error != 0) {
+ printf("%s(%s, %s):"
+ " SIOCSIFMTU %d failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p), devmtu, error);
+ goto failed;
+ }
+ control_flags |= PORT_CONTROL_FLAGS_MTU_SET;
+
+ /* program the port with our multicast addresses */
+ error = multicast_list_program(&p->po_multicast, ifp, port_ifp);
+ if (error) {
+ printf("%s(%s, %s): multicast_list_program failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p), error);
+ goto failed;
+ }
+
+ /* mark the interface up */
+ ifnet_set_flags(port_ifp, IFF_UP, IFF_UP);
+
+ error = ifnet_ioctl(port_ifp, 0, SIOCSIFFLAGS, NULL);
+ if (error != 0) {
+ printf("%s(%s, %s): SIOCSIFFLAGS failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p), error);
+ goto failed;
+ }
+
+ /* re-program the port's ethernet address */
+ error = if_siflladdr(port_ifp,
+ (const struct ether_addr *)IF_LLADDR(ifp));
+ if (error == 0) {
+ if (memcmp(IF_LLADDR(ifp), IF_LLADDR(port_ifp), ETHER_ADDR_LEN)
+ != 0) {
+ /* it lied, it really doesn't support setting lladdr */
+ error = EOPNOTSUPP;
+ }
+ }
+ if (error != 0) {
+ /* port doesn't support setting the link address */
+ printf("%s(%s, %s): if_siflladdr failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p), error);
+ error = ifnet_set_promiscuous(port_ifp, 1);
+ if (error != 0) {
+ /* port doesn't support setting promiscuous mode */
+ printf("%s(%s, %s): set promiscuous failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p), error);
+ goto failed;
+ }
+ control_flags |= PORT_CONTROL_FLAGS_PROMISCUOUS_SET;
+ } else {
+ control_flags |= PORT_CONTROL_FLAGS_LLADDR_SET;
+ }
+
+ bond_lock();
+
+ /* no failures past this point */
+ p->po_enabled = 1;
+ p->po_control_flags = control_flags;
+
+ /* copy the contents of the existing distributing array */
+ if (ifb->ifb_distributing_count) {
+ bcopy(ifb->ifb_distributing_array, new_array,
+ sizeof(*new_array) * ifb->ifb_distributing_count);
+ }
+ old_array = ifb->ifb_distributing_array;
+ ifb->ifb_distributing_array = new_array;
+
+ 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;
+ 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);
+ }
+ }
+ p->po_filter = filter;
+
+ /* clear the busy state, and wakeup anyone waiting */
+ ifbond_signal(ifb, __func__);
+ bond_unlock();
+ if (event_code != 0) {
+ interface_link_event(ifp, event_code);
+ }
+ if (old_array != NULL) {
+ FREE(old_array, M_BOND);
+ }
+ return 0;
+
+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);
+ }
+ if ((control_flags & PORT_CONTROL_FLAGS_LLADDR_SET) != 0) {
+ int error1;
+
+ error1 = if_siflladdr(port_ifp, &p->po_saved_addr);
+ if (error1 != 0) {
+ printf("%s(%s, %s): if_siflladdr restore failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p), error1);
+ }
+ }
+ if ((control_flags & PORT_CONTROL_FLAGS_PROMISCUOUS_SET) != 0) {
+ int error1;
+
+ error1 = ifnet_set_promiscuous(port_ifp, 0);
+ if (error1 != 0) {
+ printf("%s(%s, %s): promiscous mode disable failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p), error1);
+ }
+ }
+ if ((control_flags & PORT_CONTROL_FLAGS_PROTO_ATTACHED) != 0) {
+ (void)bond_detach_protocol(port_ifp);
+ }
+ if ((control_flags & PORT_CONTROL_FLAGS_FILTER_ATTACHED) != 0) {
+ iflt_detach(filter);
+ }
+ if ((control_flags & PORT_CONTROL_FLAGS_MTU_SET) != 0) {
+ int error1;
+
+ error1 = siocsifmtu(port_ifp, p->po_devmtu.ifdm_current);
+ if (error1 != 0) {
+ printf("%s(%s, %s): SIOCSIFMTU %d failed %d\n",
+ __func__,
+ ifb->ifb_name, bondport_get_name(p),
+ p->po_devmtu.ifdm_current, error1);
+ }
+ }
+ bond_lock();
+ if ((control_flags & PORT_CONTROL_FLAGS_IN_LIST) != 0) {
+ TAILQ_REMOVE(&ifb->ifb_port_list, p, po_port_list);
+ ifb->ifb_port_count--;
+ }
+ if_clear_eflags(ifp, IFEF_BOND);
+ if (TAILQ_EMPTY(&ifb->ifb_port_list)) {
+ ifb->ifb_altmtu = 0;
+ ifnet_set_mtu(ifp, ETHERMTU);
+ ifnet_set_offload(ifp, 0);
+ }
+
+signal_done:
+ ifbond_signal(ifb, __func__);