X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/b7266188b87f3620ec3f9f717e57194a7dd989fe..490019cf9519204c5fb36b2fba54ceb983bb6b72:/bsd/netinet/ip_dummynet.c diff --git a/bsd/netinet/ip_dummynet.c b/bsd/netinet/ip_dummynet.c index 54fceaef4..c24935fd9 100644 --- a/bsd/netinet/ip_dummynet.c +++ b/bsd/netinet/ip_dummynet.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000-2009 Apple Inc. All rights reserved. + * Copyright (c) 2000-2013 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ * @@ -92,6 +92,9 @@ #include #include #include +#if DUMMYNET +#include +#endif /* DUMMYNET */ #include #include #include @@ -100,6 +103,11 @@ #include #include +#include /* for ip6_input, ip6_output prototypes */ +#include + +static struct ip_fw default_rule; + /* * We keep a private variable for the simulation time, but we could * probably use an existing one ("softticks" in sys/kern/kern_timer.c) @@ -121,6 +129,8 @@ static int red_lookup_depth = 256; /* RED - default lookup table depth */ static int red_avg_pkt_size = 512; /* RED - default medium packet size */ static int red_max_pkt_size = 1500; /* RED - default max packet size */ +static int serialize = 0; + /* * Three heaps contain queues and pipes that the scheduler handles: * @@ -152,9 +162,6 @@ static void ready_event_wfq(struct dn_pipe *p, struct mbuf **head, */ static void dummynet_send(struct mbuf *m); -/* Flag to signify the existance of a dequeued packet chain */ -static int serialize = 0; - #define HASHSIZE 16 #define HASH(num) ((((num) >> 8) ^ ((num) >> 4) ^ (num)) & 0x0f) static struct dn_pipe_head pipehash[HASHSIZE]; /* all pipes */ @@ -163,36 +170,36 @@ static struct dn_flow_set_head flowsethash[HASHSIZE]; /* all flowsets */ #ifdef SYSCTL_NODE SYSCTL_NODE(_net_inet_ip, OID_AUTO, dummynet, - CTLFLAG_RW, 0, "Dummynet"); + CTLFLAG_RW | CTLFLAG_LOCKED, 0, "Dummynet"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, hash_size, - CTLFLAG_RW, &dn_hash_size, 0, "Default hash table size"); + CTLFLAG_RW | CTLFLAG_LOCKED, &dn_hash_size, 0, "Default hash table size"); SYSCTL_QUAD(_net_inet_ip_dummynet, OID_AUTO, curr_time, - CTLFLAG_RD, &curr_time, "Current tick"); + CTLFLAG_RD | CTLFLAG_LOCKED, &curr_time, "Current tick"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, ready_heap, - CTLFLAG_RD, &ready_heap.size, 0, "Size of ready heap"); + CTLFLAG_RD | CTLFLAG_LOCKED, &ready_heap.size, 0, "Size of ready heap"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, extract_heap, - CTLFLAG_RD, &extract_heap.size, 0, "Size of extract heap"); + CTLFLAG_RD | CTLFLAG_LOCKED, &extract_heap.size, 0, "Size of extract heap"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, searches, - CTLFLAG_RD, &searches, 0, "Number of queue searches"); + CTLFLAG_RD | CTLFLAG_LOCKED, &searches, 0, "Number of queue searches"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, search_steps, - CTLFLAG_RD, &search_steps, 0, "Number of queue search steps"); + CTLFLAG_RD | CTLFLAG_LOCKED, &search_steps, 0, "Number of queue search steps"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, expire, - CTLFLAG_RW, &pipe_expire, 0, "Expire queue if empty"); + CTLFLAG_RW | CTLFLAG_LOCKED, &pipe_expire, 0, "Expire queue if empty"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, max_chain_len, - CTLFLAG_RW, &dn_max_ratio, 0, + CTLFLAG_RW | CTLFLAG_LOCKED, &dn_max_ratio, 0, "Max ratio between dynamic queues and buckets"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_lookup_depth, - CTLFLAG_RD, &red_lookup_depth, 0, "Depth of RED lookup table"); + CTLFLAG_RD | CTLFLAG_LOCKED, &red_lookup_depth, 0, "Depth of RED lookup table"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_avg_pkt_size, - CTLFLAG_RD, &red_avg_pkt_size, 0, "RED Medium packet size"); + CTLFLAG_RD | CTLFLAG_LOCKED, &red_avg_pkt_size, 0, "RED Medium packet size"); SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, red_max_pkt_size, - CTLFLAG_RD, &red_max_pkt_size, 0, "RED Max packet size"); + CTLFLAG_RD | CTLFLAG_LOCKED, &red_max_pkt_size, 0, "RED Max packet size"); #endif #ifdef DUMMYNET_DEBUG int dummynet_debug = 0; #ifdef SYSCTL_NODE -SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, debug, CTLFLAG_RW, &dummynet_debug, +SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, debug, CTLFLAG_RW | CTLFLAG_LOCKED, &dummynet_debug, 0, "control debugging printfs"); #endif #define DPRINTF(X) if (dummynet_debug) printf X @@ -211,7 +218,8 @@ SYSCTL_INT(_net_inet_ip_dummynet, OID_AUTO, debug, CTLFLAG_RW, &dummynet_debug, static lck_grp_t *dn_mutex_grp; static lck_grp_attr_t *dn_mutex_grp_attr; static lck_attr_t *dn_mutex_attr; -static lck_mtx_t *dn_mutex; +decl_lck_mtx_data(static, dn_mutex_data); +static lck_mtx_t *dn_mutex = &dn_mutex_data; static int config_pipe(struct dn_pipe *p); static int ip_dn_ctl(struct sockopt *sopt); @@ -220,7 +228,6 @@ static void dummynet(void *); static void dummynet_flush(void); void dummynet_drain(void); static ip_dn_io_t dummynet_io; -static void dn_rule_delete(void *); int if_tx_rdy(struct ifnet *ifp); @@ -457,6 +464,7 @@ char *cp_pipe_to_32_user(struct dn_pipe *p, struct dn_pipe_32 *pipe_bp) pipe_bp->pipe_nr = p->pipe_nr; pipe_bp->bandwidth = p->bandwidth; + pipe_bp->delay = p->delay; bcopy( &(p->scheduler_heap), &(pipe_bp->scheduler_heap), sizeof(struct dn_heap_32)); pipe_bp->scheduler_heap.p = CAST_DOWN_EXPLICIT(user32_addr_t, pipe_bp->scheduler_heap.p); bcopy( &(p->not_eligible_heap), &(pipe_bp->not_eligible_heap), sizeof(struct dn_heap_32)); @@ -497,6 +505,7 @@ char *cp_pipe_to_64_user(struct dn_pipe *p, struct dn_pipe_64 *pipe_bp) pipe_bp->pipe_nr = p->pipe_nr; pipe_bp->bandwidth = p->bandwidth; + pipe_bp->delay = p->delay; bcopy( &(p->scheduler_heap), &(pipe_bp->scheduler_heap), sizeof(struct dn_heap_64)); pipe_bp->scheduler_heap.p = CAST_DOWN(user64_addr_t, pipe_bp->scheduler_heap.p); bcopy( &(p->not_eligible_heap), &(pipe_bp->not_eligible_heap), sizeof(struct dn_heap_64)); @@ -614,7 +623,8 @@ heap_extract(struct dn_heap *h, void *obj) int child, father, maxelt = h->elements - 1 ; if (maxelt < 0) { - printf("dummynet: warning, extract from empty heap 0x%p\n", h); + printf("dummynet: warning, extract from empty heap 0x%llx\n", + (uint64_t)VM_KERNEL_ADDRPERM(h)); return ; } father = 0 ; /* default: move up smallest child */ @@ -648,47 +658,6 @@ heap_extract(struct dn_heap *h, void *obj) } } -#if 0 -/* - * change object position and update references - * XXX this one is never used! - */ -static void -heap_move(struct dn_heap *h, dn_key new_key, void *object) -{ - int temp; - int i ; - int maxelt = h->elements-1 ; - struct dn_heap_entry buf ; - - if (h->offset <= 0) - panic("cannot move items on this heap"); - - i = *((int *)((char *)object + h->offset)); - if (DN_KEY_LT(new_key, h->p[i].key) ) { /* must move up */ - h->p[i].key = new_key ; - for (; i>0 && DN_KEY_LT(new_key, h->p[(temp = HEAP_FATHER(i))].key) ; - i = temp ) { /* bubble up */ - HEAP_SWAP(h->p[i], h->p[temp], buf) ; - SET_OFFSET(h, i); - } - } else { /* must move down */ - h->p[i].key = new_key ; - while ( (temp = HEAP_LEFT(i)) <= maxelt ) { /* found left child */ - if ((temp != maxelt) && DN_KEY_GT(h->p[temp].key, h->p[temp+1].key)) - temp++ ; /* select child with min key */ - if (DN_KEY_GT(new_key, h->p[temp].key)) { /* go down */ - HEAP_SWAP(h->p[i], h->p[temp], buf) ; - SET_OFFSET(h, i); - } else - break ; - i = temp ; - } - } - SET_OFFSET(h, i); -} -#endif /* heap_move, unused */ - /* * heapify() will reorganize data inside an array to maintain the * heap property. It is needed when we delete a bunch of entries. @@ -726,11 +695,13 @@ static struct dn_pkt_tag * dn_tag_get(struct mbuf *m) { struct m_tag *mtag = m_tag_first(m); -/* KASSERT(mtag != NULL && - mtag->m_tag_id == KERNEL_MODULE_TAG_ID && - mtag->m_tag_type == KERNEL_TAG_TYPE_DUMMYNET, - ("packet on dummynet queue w/o dummynet tag!")); -*/ + + if (!(mtag != NULL && + mtag->m_tag_id == KERNEL_MODULE_TAG_ID && + mtag->m_tag_type == KERNEL_TAG_TYPE_DUMMYNET)) + panic("packet on dummynet queue w/o dummynet tag: 0x%llx", + (uint64_t)VM_KERNEL_ADDRPERM(m)); + return (struct dn_pkt_tag *)(mtag+1); } @@ -755,16 +726,16 @@ dn_tag_get(struct mbuf *m) static void transmit_event(struct dn_pipe *pipe, struct mbuf **head, struct mbuf **tail) { - struct mbuf *m ; - struct dn_pkt_tag *pkt ; + struct mbuf *m ; + struct dn_pkt_tag *pkt = NULL; + u_int64_t schedule_time; lck_mtx_assert(dn_mutex, LCK_MTX_ASSERT_OWNED); - - /* Extract packets only if no pending chain is being currently processed */ + ASSERT(serialize >= 0); if (serialize == 0) { while ((m = pipe->head) != NULL) { pkt = dn_tag_get(m); - if (!DN_KEY_LEQ(pkt->output_time, curr_time)) + if (!DN_KEY_LEQ(pkt->dn_output_time, curr_time)) break; pipe->head = m->m_nextpkt; @@ -774,18 +745,22 @@ transmit_event(struct dn_pipe *pipe, struct mbuf **head, struct mbuf **tail) *head = m; *tail = m; } + if (*tail != NULL) (*tail)->m_nextpkt = NULL; } - /* if there are leftover packets, put the pipe into the heap for next ready event */ - if ((m = pipe->head) != NULL) { + schedule_time = pkt == NULL || DN_KEY_LEQ(pkt->dn_output_time, curr_time) ? + curr_time + 1 : pkt->dn_output_time; + + /* if there are leftover packets, put the pipe into the heap for next ready event */ + if ((m = pipe->head) != NULL) { pkt = dn_tag_get(m); /* XXX should check errors on heap_insert, by draining the * whole pipe p and hoping in the future we are more successful */ - heap_insert(&extract_heap, pkt->output_time, pipe); - } + heap_insert(&extract_heap, schedule_time, pipe); + } } /* @@ -818,7 +793,7 @@ move_pkt(struct mbuf *pkt, struct dn_flow_queue *q, q->len-- ; q->len_bytes -= len ; - dt->output_time = curr_time + p->delay ; + dt->dn_output_time = curr_time + p->delay ; if (p->head == NULL) p->head = pkt; @@ -910,11 +885,11 @@ ready_event_wfq(struct dn_pipe *p, struct mbuf **head, struct mbuf **tail) int64_t p_numbytes = p->numbytes; lck_mtx_assert(dn_mutex, LCK_MTX_ASSERT_OWNED); - + if (p->if_name[0] == 0) /* tx clock is simulated */ p_numbytes += ( curr_time - p->sched_time ) * p->bandwidth; else { /* tx clock is for real, the ifq must be empty or this is a NOP */ - if (p->ifp && p->ifp->if_snd.ifq_head != NULL) + if (p->ifp && !IFCQ_IS_EMPTY(&p->ifp->if_snd)) return ; else { DPRINTF(("dummynet: pipe %d ready from %s --\n", @@ -1003,7 +978,7 @@ ready_event_wfq(struct dn_pipe *p, struct mbuf **head, struct mbuf **tail) if (p->bandwidth > 0) t = ( p->bandwidth -1 - p_numbytes) / p->bandwidth ; - dn_tag_get(p->tail)->output_time += t ; + dn_tag_get(p->tail)->dn_output_time += t ; p->sched_time = curr_time ; heap_insert(&wfq_ready_heap, curr_time + t, (void *)p); /* XXX should check errors on heap_insert, and drain the whole @@ -1090,7 +1065,7 @@ dummynet(__unused void * unused) q->S = q->F + 1 ; /* mark timestamp as invalid */ pe->sum -= q->fs->weight ; } - + /* check the heaps to see if there's still stuff in there, and * only set the timer if there are packets to process */ @@ -1105,21 +1080,17 @@ dummynet(__unused void * unused) break; } } - - /* - * If a packet chain has been dequeued, set serialize=1 so that new - * packets don't get dispatched out of turn - */ + if (head != NULL) - serialize = 1; - - lck_mtx_unlock(dn_mutex); + serialize++; + + lck_mtx_unlock(dn_mutex); /* Send out the de-queued list of ready-to-send packets */ if (head != NULL) { dummynet_send(head); lck_mtx_lock(dn_mutex); - serialize = 0; + serialize--; lck_mtx_unlock(dn_mutex); } } @@ -1135,21 +1106,37 @@ dummynet_send(struct mbuf *m) n = m->m_nextpkt; m->m_nextpkt = NULL; pkt = dn_tag_get(m); - + + DPRINTF(("dummynet_send m: 0x%llx dn_dir: %d dn_flags: 0x%x\n", + (uint64_t)VM_KERNEL_ADDRPERM(m), pkt->dn_dir, + pkt->dn_flags)); + switch (pkt->dn_dir) { case DN_TO_IP_OUT: { - struct route tmp_rt = pkt->ro; - (void)ip_output(m, NULL, &tmp_rt, pkt->flags, NULL, NULL); - if (tmp_rt.ro_rt) { - rtfree(tmp_rt.ro_rt); - tmp_rt.ro_rt = NULL; - } + struct route tmp_rt; + + /* route is already in the packet's dn_ro */ + bzero(&tmp_rt, sizeof (tmp_rt)); + + /* Force IP_RAWOUTPUT as the IP header is fully formed */ + pkt->dn_flags |= IP_RAWOUTPUT | IP_FORWARDING; + (void)ip_output(m, NULL, &tmp_rt, pkt->dn_flags, NULL, NULL); + ROUTE_RELEASE(&tmp_rt); break ; } case DN_TO_IP_IN : proto_inject(PF_INET, m); break ; - +#ifdef INET6 + case DN_TO_IP6_OUT: { + /* routes already in the packet's dn_{ro6,pmtu} */ + ip6_output(m, NULL, NULL, IPV6_FORWARDING, NULL, NULL, NULL); + break; + } + case DN_TO_IP6_IN: + proto_inject(PF_INET6, m); + break; +#endif /* INET6 */ default: printf("dummynet: bad switch %d!\n", pkt->dn_dir); m_freem(m); @@ -1178,7 +1165,7 @@ if_tx_rdy(struct ifnet *ifp) break ; if (p == NULL) { char buf[32]; - snprintf(buf, sizeof(buf), "%s%d",ifp->if_name, ifp->if_unit); + snprintf(buf, sizeof(buf), "%s", if_name(ifp)); for (i = 0; i < HASHSIZE; i++) SLIST_FOREACH(p, &pipehash[i], next) if (!strcmp(p->if_name, buf) ) { @@ -1188,18 +1175,25 @@ if_tx_rdy(struct ifnet *ifp) } } if (p != NULL) { - DPRINTF(("dummynet: ++ tx rdy from %s%d - qlen %d\n", ifp->if_name, - ifp->if_unit, ifp->if_snd.ifq_len)); + DPRINTF(("dummynet: ++ tx rdy from %s - qlen %d\n", if_name(ifp), + IFCQ_LEN(&ifp->if_snd))); p->numbytes = 0 ; /* mark ready for I/O */ ready_event_wfq(p, &head, &tail); } + + if (head != NULL) { + serialize++; + } + lck_mtx_unlock(dn_mutex); - /* Send out the de-queued list of ready-to-send packets */ - if (head != NULL) + if (head != NULL) { dummynet_send(head); - + lck_mtx_lock(dn_mutex); + serialize--; + lck_mtx_unlock(dn_mutex); + } return 0; } @@ -1214,6 +1208,7 @@ expire_queues(struct dn_flow_set *fs) int i, initial_elements = fs->rq_elements ; struct timeval timenow; + /* reviewed for getmicrotime usage */ getmicrotime(&timenow); if (fs->last_expired == timenow.tv_sec) @@ -1275,41 +1270,84 @@ create_queue(struct dn_flow_set *fs, int i) * so that further searches take less time. */ static struct dn_flow_queue * -find_queue(struct dn_flow_set *fs, struct ipfw_flow_id *id) +find_queue(struct dn_flow_set *fs, struct ip_flow_id *id) { int i = 0 ; /* we need i and q for new allocations */ struct dn_flow_queue *q, *prev; + int is_v6 = IS_IP6_FLOW_ID(id); if ( !(fs->flags_fs & DN_HAVE_FLOW_MASK) ) q = fs->rq[0] ; else { - /* first, do the masking */ - id->dst_ip &= fs->flow_mask.dst_ip ; - id->src_ip &= fs->flow_mask.src_ip ; + /* first, do the masking, then hash */ id->dst_port &= fs->flow_mask.dst_port ; id->src_port &= fs->flow_mask.src_port ; id->proto &= fs->flow_mask.proto ; id->flags = 0 ; /* we don't care about this one */ - /* then, hash function */ - i = ( (id->dst_ip) & 0xffff ) ^ - ( (id->dst_ip >> 15) & 0xffff ) ^ - ( (id->src_ip << 1) & 0xffff ) ^ - ( (id->src_ip >> 16 ) & 0xffff ) ^ - (id->dst_port << 1) ^ (id->src_port) ^ - (id->proto ); + if (is_v6) { + APPLY_MASK(&id->dst_ip6, &fs->flow_mask.dst_ip6); + APPLY_MASK(&id->src_ip6, &fs->flow_mask.src_ip6); + id->flow_id6 &= fs->flow_mask.flow_id6; + + i = ((id->dst_ip6.__u6_addr.__u6_addr32[0]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[1]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[2]) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[3]) & 0xffff)^ + + ((id->dst_ip6.__u6_addr.__u6_addr32[0] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[1] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[2] >> 15) & 0xffff)^ + ((id->dst_ip6.__u6_addr.__u6_addr32[3] >> 15) & 0xffff)^ + + ((id->src_ip6.__u6_addr.__u6_addr32[0] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[1] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[2] << 1) & 0xfffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[3] << 1) & 0xfffff)^ + + ((id->src_ip6.__u6_addr.__u6_addr32[0] >> 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[1] >> 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[2] >> 16) & 0xffff)^ + ((id->src_ip6.__u6_addr.__u6_addr32[3] >> 16) & 0xffff)^ + + (id->dst_port << 1) ^ (id->src_port) ^ + (id->proto ) ^ + (id->flow_id6); + } else { + id->dst_ip &= fs->flow_mask.dst_ip ; + id->src_ip &= fs->flow_mask.src_ip ; + + i = ( (id->dst_ip) & 0xffff ) ^ + ( (id->dst_ip >> 15) & 0xffff ) ^ + ( (id->src_ip << 1) & 0xffff ) ^ + ( (id->src_ip >> 16 ) & 0xffff ) ^ + (id->dst_port << 1) ^ (id->src_port) ^ + (id->proto ); + } i = i % fs->rq_size ; /* finally, scan the current list for a match */ searches++ ; for (prev=NULL, q = fs->rq[i] ; q ; ) { search_steps++; - if (id->dst_ip == q->id.dst_ip && - id->src_ip == q->id.src_ip && - id->dst_port == q->id.dst_port && - id->src_port == q->id.src_port && - id->proto == q->id.proto && - id->flags == q->id.flags) - break ; /* found */ - else if (pipe_expire && q->head == NULL && q->S == q->F+1 ) { + if (is_v6 && + IN6_ARE_ADDR_EQUAL(&id->dst_ip6,&q->id.dst_ip6) && + IN6_ARE_ADDR_EQUAL(&id->src_ip6,&q->id.src_ip6) && + id->dst_port == q->id.dst_port && + id->src_port == q->id.src_port && + id->proto == q->id.proto && + id->flags == q->id.flags && + id->flow_id6 == q->id.flow_id6) + break ; /* found */ + + if (!is_v6 && id->dst_ip == q->id.dst_ip && + id->src_ip == q->id.src_ip && + id->dst_port == q->id.dst_port && + id->src_port == q->id.src_port && + id->proto == q->id.proto && + id->flags == q->id.flags) + break ; /* found */ + + /* No match. Check if we can expire the entry */ + if (pipe_expire && q->head == NULL && q->S == q->F+1 ) { /* entry is idle and not in any heap, expire it */ struct dn_flow_queue *old_q = q ; @@ -1483,28 +1521,41 @@ locate_pipe(int pipe_nr) * */ static int -dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa) +dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa, int client) { - struct mbuf *head = NULL, *tail = NULL; + struct mbuf *head = NULL, *tail = NULL; struct dn_pkt_tag *pkt; struct m_tag *mtag; struct dn_flow_set *fs = NULL; struct dn_pipe *pipe ; u_int64_t len = m->m_pkthdr.len ; struct dn_flow_queue *q = NULL ; - int is_pipe; + int is_pipe = 0; struct timespec ts; struct timeval tv; - + + DPRINTF(("dummynet_io m: 0x%llx pipe: %d dir: %d client: %d\n", + (uint64_t)VM_KERNEL_ADDRPERM(m), pipe_nr, dir, client)); + +#if IPFIREWALL #if IPFW2 - ipfw_insn *cmd = fwa->rule->cmd + fwa->rule->act_ofs; + if (client == DN_CLIENT_IPFW) { + ipfw_insn *cmd = fwa->fwa_ipfw_rule->cmd + fwa->fwa_ipfw_rule->act_ofs; - if (cmd->opcode == O_LOG) - cmd += F_LEN(cmd); - is_pipe = (cmd->opcode == O_PIPE); + if (cmd->opcode == O_LOG) + cmd += F_LEN(cmd); + is_pipe = (cmd->opcode == O_PIPE); + } #else - is_pipe = (fwa->rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_PIPE; + if (client == DN_CLIENT_IPFW) + is_pipe = (fwa->fwa_ipfw_rule->fw_flg & IP_FW_F_COMMAND) == IP_FW_F_PIPE; #endif +#endif /* IPFIREWALL */ + +#if DUMMYNET + if (client == DN_CLIENT_PF) + is_pipe = fwa->fwa_flags == DN_IS_PIPE ? 1 : 0; +#endif /* DUMMYNET */ pipe_nr &= 0xffff ; @@ -1514,7 +1565,7 @@ dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa) * here we convert secs and usecs to msecs (just divide the * usecs and take the closest whole number). */ - microuptime(&tv); + microuptime(&tv); curr_time = (tv.tv_sec * 1000) + (tv.tv_usec / 1000); /* @@ -1543,7 +1594,7 @@ dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa) goto dropit ; } } - q = find_queue(fs, &(fwa->f_id)); + q = find_queue(fs, &(fwa->fwa_id)); if ( q == NULL ) goto dropit ; /* cannot allocate queue */ /* @@ -1564,8 +1615,8 @@ dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa) goto dropit ; /* XXX expensive to zero, see if we can remove it*/ - mtag = m_tag_alloc(KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_DUMMYNET, - sizeof(struct dn_pkt_tag), M_NOWAIT); + mtag = m_tag_create(KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_DUMMYNET, + sizeof(struct dn_pkt_tag), M_NOWAIT, m); if ( mtag == NULL ) goto dropit ; /* cannot allocate packet header */ m_tag_prepend(m, mtag); /* attach to mbuf chain */ @@ -1574,28 +1625,66 @@ dummynet_io(struct mbuf *m, int pipe_nr, int dir, struct ip_fw_args *fwa) bzero(pkt, sizeof(struct dn_pkt_tag)); /* ok, i can handle the pkt now... */ /* build and enqueue packet + parameters */ - pkt->rule = fwa->rule ; + /* + * PF is checked before ipfw so remember ipfw rule only when + * the caller is ipfw. When the caller is PF, fwa_ipfw_rule + * is a fake rule just used for convenience + */ + if (client == DN_CLIENT_IPFW) + pkt->dn_ipfw_rule = fwa->fwa_ipfw_rule; + pkt->dn_pf_rule = fwa->fwa_pf_rule; pkt->dn_dir = dir ; + pkt->dn_client = client; - pkt->ifp = fwa->oif; + pkt->dn_ifp = fwa->fwa_oif; if (dir == DN_TO_IP_OUT) { - /* - * We need to copy *ro because for ICMP pkts (and maybe others) - * the caller passed a pointer into the stack; dst might also be - * a pointer into *ro so it needs to be updated. - */ - pkt->ro = *(fwa->ro); - if (fwa->ro->ro_rt) - RT_ADDREF(fwa->ro->ro_rt); - - if (fwa->dst == (struct sockaddr_in *)&fwa->ro->ro_dst) /* dst points into ro */ - fwa->dst = (struct sockaddr_in *)&(pkt->ro.ro_dst) ; - - pkt->dn_dst = fwa->dst; - pkt->flags = fwa->flags; - if (fwa->ipoa != NULL) - pkt->ipoa = *(fwa->ipoa); - } + /* + * We need to copy *ro because for ICMP pkts (and maybe others) + * the caller passed a pointer into the stack; dst might also be + * a pointer into *ro so it needs to be updated. + */ + if (fwa->fwa_ro) { + route_copyout(&pkt->dn_ro, fwa->fwa_ro, sizeof (pkt->dn_ro)); + } + if (fwa->fwa_dst) { + if (fwa->fwa_dst == (struct sockaddr_in *)&fwa->fwa_ro->ro_dst) /* dst points into ro */ + fwa->fwa_dst = (struct sockaddr_in *)&(pkt->dn_ro.ro_dst) ; + + bcopy (fwa->fwa_dst, &pkt->dn_dst, sizeof(pkt->dn_dst)); + } + } else if (dir == DN_TO_IP6_OUT) { + if (fwa->fwa_ro6) { + route_copyout((struct route *)&pkt->dn_ro6, + (struct route *)fwa->fwa_ro6, sizeof (pkt->dn_ro6)); + } + if (fwa->fwa_ro6_pmtu) { + route_copyout((struct route *)&pkt->dn_ro6_pmtu, + (struct route *)fwa->fwa_ro6_pmtu, sizeof (pkt->dn_ro6_pmtu)); + } + if (fwa->fwa_dst6) { + if (fwa->fwa_dst6 == (struct sockaddr_in6 *)&fwa->fwa_ro6->ro_dst) /* dst points into ro */ + fwa->fwa_dst6 = (struct sockaddr_in6 *)&(pkt->dn_ro6.ro_dst) ; + + bcopy (fwa->fwa_dst6, &pkt->dn_dst6, sizeof(pkt->dn_dst6)); + } + pkt->dn_origifp = fwa->fwa_origifp; + pkt->dn_mtu = fwa->fwa_mtu; + pkt->dn_alwaysfrag = fwa->fwa_alwaysfrag; + pkt->dn_unfragpartlen = fwa->fwa_unfragpartlen; + if (fwa->fwa_exthdrs) { + bcopy (fwa->fwa_exthdrs, &pkt->dn_exthdrs, sizeof(pkt->dn_exthdrs)); + /* + * Need to zero out the source structure so the mbufs + * won't be freed by ip6_output() + */ + bzero(fwa->fwa_exthdrs, sizeof(struct ip6_exthdrs)); + } + } + if (dir == DN_TO_IP_OUT || dir == DN_TO_IP6_OUT) { + pkt->dn_flags = fwa->fwa_oflags; + if (fwa->fwa_ipoa != NULL) + pkt->dn_ipoa = *(fwa->fwa_ipoa); + } if (q->head == NULL) q->head = m; else @@ -1682,11 +1771,13 @@ done: ts.tv_nsec = 1 * 1000000; // 1ms timer_enabled = 1; bsd_timeout(dummynet, NULL, &ts); - } + } lck_mtx_unlock(dn_mutex); - if (head != NULL) + + if (head != NULL) { dummynet_send(head); + } return 0; @@ -1699,17 +1790,14 @@ dropit: } /* - * Below, the rtfree is only needed when (pkt->dn_dir == DN_TO_IP_OUT) + * Below, the ROUTE_RELEASE is only needed when (pkt->dn_dir == DN_TO_IP_OUT) * Doing this would probably save us the initial bzero of dn_pkt */ #define DN_FREE_PKT(_m) do { \ struct m_tag *tag = m_tag_locate(m, KERNEL_MODULE_TAG_ID, KERNEL_TAG_TYPE_DUMMYNET, NULL); \ if (tag) { \ struct dn_pkt_tag *n = (struct dn_pkt_tag *)(tag+1); \ - if (n->ro.ro_rt != NULL) { \ - rtfree(n->ro.ro_rt); \ - n->ro.ro_rt = NULL; \ - } \ + ROUTE_RELEASE(&n->dn_ro); \ } \ m_tag_delete(_m, tag); \ m_freem(_m); \ @@ -1792,9 +1880,11 @@ dummynet_flush(void) lck_mtx_lock(dn_mutex); - /* remove all references to pipes ...*/ - flush_pipe_ptrs(NULL); - +#if IPFW2 + /* remove all references to pipes ...*/ + flush_pipe_ptrs(NULL); +#endif /* IPFW2 */ + /* Free heaps so we don't have unwanted events. */ heap_free(&ready_heap); heap_free(&wfq_ready_heap); @@ -1820,9 +1910,8 @@ dummynet_flush(void) } -extern struct ip_fw *ip_fw_default_rule ; static void -dn_rule_delete_fs(struct dn_flow_set *fs, void *r) +dn_ipfw_rule_delete_fs(struct dn_flow_set *fs, void *r) { int i ; struct dn_flow_queue *q ; @@ -1832,8 +1921,8 @@ dn_rule_delete_fs(struct dn_flow_set *fs, void *r) for (q = fs->rq[i] ; q ; q = q->next ) for (m = q->head ; m ; m = m->m_nextpkt ) { struct dn_pkt_tag *pkt = dn_tag_get(m) ; - if (pkt->rule == r) - pkt->rule = ip_fw_default_rule ; + if (pkt->dn_ipfw_rule == r) + pkt->dn_ipfw_rule = &default_rule ; } } /* @@ -1841,7 +1930,7 @@ dn_rule_delete_fs(struct dn_flow_set *fs, void *r) * from packets matching this rule. */ void -dn_rule_delete(void *r) +dn_ipfw_rule_delete(void *r) { struct dn_pipe *p ; struct dn_flow_set *fs ; @@ -1858,16 +1947,16 @@ dn_rule_delete(void *r) */ for (i = 0; i < HASHSIZE; i++) SLIST_FOREACH(fs, &flowsethash[i], next) - dn_rule_delete_fs(fs, r); + dn_ipfw_rule_delete_fs(fs, r); for (i = 0; i < HASHSIZE; i++) SLIST_FOREACH(p, &pipehash[i], next) { fs = &(p->fs); - dn_rule_delete_fs(fs, r); + dn_ipfw_rule_delete_fs(fs, r); for (m = p->head ; m ; m = m->m_nextpkt ) { pkt = dn_tag_get(m); - if (pkt->rule == r) - pkt->rule = ip_fw_default_rule; + if (pkt->dn_ipfw_rule == r) + pkt->dn_ipfw_rule = &default_rule; } } lck_mtx_unlock(dn_mutex); @@ -2192,8 +2281,10 @@ delete_pipe(struct dn_pipe *p) /* Unlink from list of pipes. */ SLIST_REMOVE(&pipehash[HASH(b->pipe_nr)], b, dn_pipe, next); +#if IPFW2 /* remove references to this pipe from the ip_fw rules. */ flush_pipe_ptrs(&(b->fs)); +#endif /* IPFW2 */ /* Remove all references to this pipe from flow_sets. */ for (i = 0; i < HASHSIZE; i++) @@ -2224,8 +2315,10 @@ delete_pipe(struct dn_pipe *p) return EINVAL ; /* not found */ } +#if IPFW2 /* remove references to this flow_set from the ip_fw rules. */ flush_pipe_ptrs(b); +#endif /* IPFW2 */ /* Unlink from list of flowsets. */ SLIST_REMOVE( &flowsethash[HASH(b->fs_nr)], b, dn_flow_set, next); @@ -2263,8 +2356,10 @@ char* dn_copy_set_32(struct dn_flow_set *set, char *bp) printf("dummynet: ++ at %d: wrong slot (have %d, " "should be %d)\n", copied, q->hash_slot, i); if (q->fs != set) - printf("dummynet: ++ at %d: wrong fs ptr (have %p, should be %p)\n", - i, q->fs, set); + printf("dummynet: ++ at %d: wrong fs ptr " + "(have 0x%llx, should be 0x%llx)\n", i, + (uint64_t)VM_KERNEL_ADDRPERM(q->fs), + (uint64_t)VM_KERNEL_ADDRPERM(set)); copied++ ; cp_queue_to_32_user( q, qp ); /* cleanup pointers */ @@ -2293,8 +2388,10 @@ char* dn_copy_set_64(struct dn_flow_set *set, char *bp) printf("dummynet: ++ at %d: wrong slot (have %d, " "should be %d)\n", copied, q->hash_slot, i); if (q->fs != set) - printf("dummynet: ++ at %d: wrong fs ptr (have %p, should be %p)\n", - i, q->fs, set); + printf("dummynet: ++ at %d: wrong fs ptr " + "(have 0x%llx, should be 0x%llx)\n", i, + (uint64_t)VM_KERNEL_ADDRPERM(q->fs), + (uint64_t)VM_KERNEL_ADDRPERM(set)); copied++ ; //bcopy(q, qp, sizeof(*q)); cp_queue_to_64_user( q, qp ); @@ -2474,21 +2571,30 @@ ip_dn_init(void) dn_mutex_grp_attr = lck_grp_attr_alloc_init(); dn_mutex_grp = lck_grp_alloc_init("dn", dn_mutex_grp_attr); dn_mutex_attr = lck_attr_alloc_init(); - - if ((dn_mutex = lck_mtx_alloc_init(dn_mutex_grp, dn_mutex_attr)) == NULL) { - printf("ip_dn_init: can't alloc dn_mutex\n"); - return; - } + lck_mtx_init(dn_mutex, dn_mutex_grp, dn_mutex_attr); ready_heap.size = ready_heap.elements = 0 ; - ready_heap.offset = 0 ; + ready_heap.offset = 0 ; - wfq_ready_heap.size = wfq_ready_heap.elements = 0 ; - wfq_ready_heap.offset = 0 ; + wfq_ready_heap.size = wfq_ready_heap.elements = 0 ; + wfq_ready_heap.offset = 0 ; - extract_heap.size = extract_heap.elements = 0 ; - extract_heap.offset = 0 ; - ip_dn_ctl_ptr = ip_dn_ctl; - ip_dn_io_ptr = dummynet_io; - ip_dn_ruledel_ptr = dn_rule_delete; + extract_heap.size = extract_heap.elements = 0 ; + extract_heap.offset = 0 ; + ip_dn_ctl_ptr = ip_dn_ctl; + ip_dn_io_ptr = dummynet_io; + + bzero(&default_rule, sizeof default_rule); + + default_rule.act_ofs = 0; + default_rule.rulenum = IPFW_DEFAULT_RULE; + default_rule.cmd_len = 1; + default_rule.set = RESVD_SET; + + default_rule.cmd[0].len = 1; + default_rule.cmd[0].opcode = +#ifdef IPFIREWALL_DEFAULT_TO_ACCEPT + (1) ? O_ACCEPT : +#endif + O_DENY; }