+static int
+dlil_attach_protocol_internal(
+ struct if_proto *proto,
+ const struct ddesc_head_str *demux,
+ const struct ifnet_demux_desc *demux_list,
+ u_int32_t demux_count)
+{
+ struct ddesc_head_str temp_head;
+ struct kev_dl_proto_data ev_pr_data;
+ struct ifnet *ifp = proto->ifp;
+ int retval = 0;
+ u_long hash_value = proto_hash_value(proto->protocol_family);
+ int if_using_kpi = (ifp->if_eflags & IFEF_USEKPI) != 0;
+ void* free_me = NULL;
+
+ /* setup some of the common values */
+
+ {
+ lck_mtx_lock(domain_proto_mtx);
+ struct domain *dp = domains;
+ while (dp && (protocol_family_t)dp->dom_family != proto->protocol_family)
+ dp = dp->dom_next;
+ proto->dl_domain = dp;
+ lck_mtx_unlock(domain_proto_mtx);
+ }
+
+ /*
+ * Convert the demux descriptors to a type the interface
+ * will understand. Checking e_flags should be safe, this
+ * flag won't change.
+ */
+ if (if_using_kpi && demux) {
+ /* Convert the demux linked list to a demux_list */
+ struct dlil_demux_desc *demux_entry;
+ struct ifnet_demux_desc *temp_list = NULL;
+ u_int32_t i = 0;
+
+ TAILQ_FOREACH(demux_entry, demux, next) {
+ i++;
+ }
+
+ temp_list = _MALLOC(sizeof(struct ifnet_demux_desc) * i, M_TEMP, M_WAITOK);
+ free_me = temp_list;
+
+ if (temp_list == NULL)
+ return ENOMEM;
+
+ i = 0;
+ TAILQ_FOREACH(demux_entry, demux, next) {
+ /* dlil_demux_desc types 1, 2, and 3 are obsolete and can not be translated */
+ if (demux_entry->type == 1 ||
+ demux_entry->type == 2 ||
+ demux_entry->type == 3) {
+ FREE(free_me, M_TEMP);
+ return ENOTSUP;
+ }
+
+ temp_list[i].type = demux_entry->type;
+ temp_list[i].data = demux_entry->native_type;
+ temp_list[i].datalen = demux_entry->variants.native_type_length;
+ i++;
+ }
+ demux_count = i;
+ demux_list = temp_list;
+ }
+ else if (!if_using_kpi && demux_list != NULL) {
+ struct dlil_demux_desc *demux_entry;
+ u_int32_t i = 0;
+
+ demux_entry = _MALLOC(sizeof(struct dlil_demux_desc) * demux_count, M_TEMP, M_WAITOK);
+ free_me = demux_entry;
+ if (demux_entry == NULL)
+ return ENOMEM;
+
+ TAILQ_INIT(&temp_head);
+
+ for (i = 0; i < demux_count; i++) {
+ demux_entry[i].type = demux_list[i].type;
+ demux_entry[i].native_type = demux_list[i].data;
+ demux_entry[i].variants.native_type_length = demux_list[i].datalen;
+ TAILQ_INSERT_TAIL(&temp_head, &demux_entry[i], next);
+ }
+ demux = &temp_head;
+ }
+
+ /*
+ * Take the write lock to protect readers and exclude other writers.
+ */
+ dlil_write_begin();
+
+ /* Check that the interface isn't currently detaching */
+ ifnet_lock_shared(ifp);
+ if ((ifp->if_eflags & IFEF_DETACHING) != 0) {
+ ifnet_lock_done(ifp);
+ dlil_write_end();
+ if (free_me)
+ FREE(free_me, M_TEMP);
+ return ENXIO;
+ }
+ ifnet_lock_done(ifp);
+
+ if (find_attached_proto(ifp, proto->protocol_family) != NULL) {
+ dlil_write_end();
+ if (free_me)
+ FREE(free_me, M_TEMP);
+ return EEXIST;
+ }
+
+ /*
+ * Call family module add_proto routine so it can refine the
+ * demux descriptors as it wishes.
+ */
+ if (if_using_kpi)
+ retval = ifp->if_add_proto_u.kpi(ifp, proto->protocol_family, demux_list, demux_count);
+ else {
+ retval = ifp->if_add_proto_u.original(ifp, proto->protocol_family,
+ _cast_non_const(demux));
+ }
+ if (retval) {
+ dlil_write_end();
+ if (free_me)
+ FREE(free_me, M_TEMP);
+ return retval;
+ }
+
+ /*
+ * We can't fail from this point on.
+ * Increment the number of uses (protocol attachments + interface attached).
+ */
+ ifp_use(ifp, kIfNetUseCount_MustNotBeZero);
+
+ /*
+ * Insert the protocol in the hash
+ */
+ {
+ struct if_proto* prev_proto = SLIST_FIRST(&ifp->if_proto_hash[hash_value]);
+ while (prev_proto && SLIST_NEXT(prev_proto, next_hash) != NULL)
+ prev_proto = SLIST_NEXT(prev_proto, next_hash);
+ if (prev_proto)
+ SLIST_INSERT_AFTER(prev_proto, proto, next_hash);
+ else
+ SLIST_INSERT_HEAD(&ifp->if_proto_hash[hash_value], proto, next_hash);
+ }