+static errno_t
+ipsec_ctl_setup(u_int32_t *unit, void **unitinfo)
+{
+ if (unit == NULL || unitinfo == NULL) {
+ return EINVAL;
+ }
+
+ lck_mtx_lock(&ipsec_lock);
+
+ /* Find next available unit */
+ if (*unit == 0) {
+ *unit = 1;
+ while (*unit != ctl_maxunit) {
+ if (ipsec_find_by_unit(*unit)) {
+ (*unit)++;
+ } else {
+ break;
+ }
+ }
+ if (*unit == ctl_maxunit) {
+ lck_mtx_unlock(&ipsec_lock);
+ return EBUSY;
+ }
+ } else if (ipsec_find_by_unit(*unit)) {
+ lck_mtx_unlock(&ipsec_lock);
+ return EBUSY;
+ }
+
+ /* Find some open interface id */
+ u_int32_t chosen_unique_id = 1;
+ struct ipsec_pcb *next_pcb = TAILQ_LAST(&ipsec_head, ipsec_list);
+ if (next_pcb != NULL) {
+ /* List was not empty, add one to the last item */
+ chosen_unique_id = next_pcb->ipsec_unique_id + 1;
+ next_pcb = NULL;
+
+ /*
+ * If this wrapped the id number, start looking at
+ * the front of the list for an unused id.
+ */
+ if (chosen_unique_id == 0) {
+ /* Find the next unused ID */
+ chosen_unique_id = 1;
+ TAILQ_FOREACH(next_pcb, &ipsec_head, ipsec_chain) {
+ if (next_pcb->ipsec_unique_id > chosen_unique_id) {
+ /* We found a gap */
+ break;
+ }
+
+ chosen_unique_id = next_pcb->ipsec_unique_id + 1;
+ }
+ }
+ }
+
+ struct ipsec_pcb *pcb = zalloc_flags(ipsec_pcb_zone, Z_WAITOK | Z_ZERO);
+
+ *unitinfo = pcb;
+ pcb->ipsec_unit = *unit;
+ pcb->ipsec_unique_id = chosen_unique_id;
+
+ if (next_pcb != NULL) {
+ TAILQ_INSERT_BEFORE(next_pcb, pcb, ipsec_chain);
+ } else {
+ TAILQ_INSERT_TAIL(&ipsec_head, pcb, ipsec_chain);
+ }
+
+ lck_mtx_unlock(&ipsec_lock);
+
+ return 0;
+}
+