diff options
-rw-r--r-- | sys/netgraph/ng_pptpgre.c | 521 | ||||
-rw-r--r-- | sys/netgraph/ng_pptpgre.h | 4 |
2 files changed, 284 insertions, 241 deletions
diff --git a/sys/netgraph/ng_pptpgre.c b/sys/netgraph/ng_pptpgre.c index 5a6bf8b..900ccbc 100644 --- a/sys/netgraph/ng_pptpgre.c +++ b/sys/netgraph/ng_pptpgre.c @@ -139,8 +139,19 @@ typedef u_int64_t pptptime_t; #define PPTP_SEQ_DIFF(x,y) ((int32_t)(x) - (int32_t)(y)) +#define SESSHASHSIZE 0x0020 +#define SESSHASH(x) (((x) ^ ((x) >> 8)) & (SESSHASHSIZE - 1)) + /* We keep packet retransmit and acknowlegement state in this struct */ -struct ng_pptpgre_ackp { +struct ng_pptpgre_sess { + node_p node; /* this node pointer */ + hook_p hook; /* hook to upper layers */ + struct ng_pptpgre_conf conf; /* configuration info */ + struct mtx mtx; /* session mutex */ + u_int32_t recvSeq; /* last seq # we rcv'd */ + u_int32_t xmitSeq; /* last seq # we sent */ + u_int32_t recvAck; /* last seq # peer ack'd */ + u_int32_t xmitAck; /* last seq # we ack'd */ int32_t ato; /* adaptive time-out value */ int32_t rtt; /* round trip time estimate */ int32_t dev; /* deviation estimate */ @@ -149,25 +160,17 @@ struct ng_pptpgre_ackp { struct callout rackTimer; /* recv ack timer */ u_int32_t winAck; /* seq when xmitWin will grow */ pptptime_t timeSent[PPTP_XMIT_WIN]; -#ifdef DEBUG_RAT - pptptime_t timerStart; /* when rackTimer started */ - pptptime_t timerLength; /* rackTimer duration */ -#endif + LIST_ENTRY(ng_pptpgre_sess) sessions; }; +typedef struct ng_pptpgre_sess *hpriv_p; /* Node private data */ struct ng_pptpgre_private { hook_p upper; /* hook to upper layers */ hook_p lower; /* hook to lower layers */ - struct ng_pptpgre_conf conf; /* configuration info */ - struct ng_pptpgre_ackp ackp; /* packet transmit ack state */ - u_int32_t recvSeq; /* last seq # we rcv'd */ - u_int32_t xmitSeq; /* last seq # we sent */ - u_int32_t recvAck; /* last seq # peer ack'd */ - u_int32_t xmitAck; /* last seq # we ack'd */ - struct timeval startTime; /* time node was created */ + struct ng_pptpgre_sess uppersess; /* default session for compat */ + LIST_HEAD(, ng_pptpgre_sess) sesshash[SESSHASHSIZE]; struct ng_pptpgre_stats stats; /* node statistics */ - struct mtx mtx; /* node mutex */ }; typedef struct ng_pptpgre_private *priv_p; @@ -177,21 +180,22 @@ static ng_rcvmsg_t ng_pptpgre_rcvmsg; static ng_shutdown_t ng_pptpgre_shutdown; static ng_newhook_t ng_pptpgre_newhook; static ng_rcvdata_t ng_pptpgre_rcvdata; +static ng_rcvdata_t ng_pptpgre_rcvdata_lower; static ng_disconnect_t ng_pptpgre_disconnect; /* Helper functions */ -static int ng_pptpgre_xmit(node_p node, item_p item); -static int ng_pptpgre_recv(node_p node, item_p item); -static void ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout); -static void ng_pptpgre_stop_send_ack_timer(node_p node); -static void ng_pptpgre_start_recv_ack_timer(node_p node); -static void ng_pptpgre_stop_recv_ack_timer(node_p node); +static int ng_pptpgre_xmit(hpriv_p hpriv, item_p item); +static void ng_pptpgre_start_send_ack_timer(hpriv_p hpriv, int ackTimeout); +static void ng_pptpgre_stop_send_ack_timer(hpriv_p hpriv); +static void ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv); +static void ng_pptpgre_stop_recv_ack_timer(hpriv_p hpriv); static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2); static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2); -static void ng_pptpgre_reset(node_p node); -static pptptime_t ng_pptpgre_time(node_p node); +static hpriv_p ng_pptpgre_find_session(priv_p privp, u_int16_t cid); +static void ng_pptpgre_reset(hpriv_p hpriv); +static pptptime_t ng_pptpgre_time(void); /* Parse type for struct ng_pptpgre_conf */ static const struct ng_parse_struct_field ng_pptpgre_conf_type_fields[] @@ -222,7 +226,7 @@ static const struct ng_cmdlist ng_pptpgre_cmdlist[] = { NGM_PPTPGRE_COOKIE, NGM_PPTPGRE_GET_CONFIG, "getconfig", - NULL, + &ng_parse_hint16_type, &ng_pptpgre_conf_type }, { @@ -276,6 +280,7 @@ static int ng_pptpgre_constructor(node_p node) { priv_p priv; + int i; /* Allocate private structure */ MALLOC(priv, priv_p, sizeof(*priv), M_NETGRAPH, M_NOWAIT | M_ZERO); @@ -285,9 +290,15 @@ ng_pptpgre_constructor(node_p node) NG_NODE_SET_PRIVATE(node, priv); /* Initialize state */ - mtx_init(&priv->mtx, "ng_pptp", NULL, MTX_DEF); - ng_callout_init(&priv->ackp.sackTimer); - ng_callout_init(&priv->ackp.rackTimer); + mtx_init(&priv->uppersess.mtx, "ng_pptp", NULL, MTX_DEF); + ng_callout_init(&priv->uppersess.sackTimer); + ng_callout_init(&priv->uppersess.rackTimer); + priv->uppersess.node = node; + + for (i = 0; i < SESSHASHSIZE; i++) + LIST_INIT(&priv->sesshash[i]); + + LIST_INSERT_HEAD(&priv->sesshash[0], &priv->uppersess, sessions); /* Done */ return (0); @@ -300,22 +311,53 @@ static int ng_pptpgre_newhook(node_p node, hook_p hook, const char *name) { const priv_p priv = NG_NODE_PRIVATE(node); - hook_p *hookPtr; /* Check hook name */ - if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) - hookPtr = &priv->upper; - else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) - hookPtr = &priv->lower; - else - return (EINVAL); - - /* See if already connected */ - if (*hookPtr != NULL) - return (EISCONN); - - /* OK */ - *hookPtr = hook; + if (strcmp(name, NG_PPTPGRE_HOOK_UPPER) == 0) { + priv->upper = hook; + priv->uppersess.hook = hook; + NG_HOOK_SET_PRIVATE(hook, &priv->uppersess); + } else if (strcmp(name, NG_PPTPGRE_HOOK_LOWER) == 0) { + priv->lower = hook; + NG_HOOK_SET_RCVDATA(hook, ng_pptpgre_rcvdata_lower); + } else { + static const char hexdig[16] = "0123456789abcdef"; + const char *hex; + hpriv_p hpriv; + int i, j; + uint16_t cid, hash; + + /* Parse hook name to get session ID */ + if (strncmp(name, NG_PPTPGRE_HOOK_SESSION_P, + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1) != 0) + return (EINVAL); + hex = name + sizeof(NG_PPTPGRE_HOOK_SESSION_P) - 1; + for (cid = i = 0; i < 4; i++) { + for (j = 0; j < 16 && hex[i] != hexdig[j]; j++); + if (j == 16) + return (EINVAL); + cid = (cid << 4) | j; + } + if (hex[i] != '\0') + return (EINVAL); + + hpriv = malloc(sizeof(*hpriv), M_NETGRAPH, M_NOWAIT | M_ZERO); + if (hpriv == NULL) + return (ENOMEM); + + /* Initialize state */ + mtx_init(&hpriv->mtx, "ng_pptp", NULL, MTX_DEF); + ng_callout_init(&hpriv->sackTimer); + ng_callout_init(&hpriv->rackTimer); + hpriv->conf.cid = cid; + hpriv->node = node; + hpriv->hook = hook; + NG_HOOK_SET_PRIVATE(hook, hpriv); + + hash = SESSHASH(cid); + LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, sessions); + } + return (0); } @@ -338,20 +380,47 @@ ng_pptpgre_rcvmsg(node_p node, item_p item, hook_p lasthook) { struct ng_pptpgre_conf *const newConf = (struct ng_pptpgre_conf *) msg->data; + hpriv_p hpriv; + uint16_t hash; /* Check for invalid or illegal config */ if (msg->header.arglen != sizeof(*newConf)) ERROUT(EINVAL); - ng_pptpgre_reset(node); /* reset on configure */ - priv->conf = *newConf; + /* Try to find session by cid. */ + hpriv = ng_pptpgre_find_session(priv, newConf->cid); + /* If not present - use upper. */ + if (hpriv == NULL) { + hpriv = &priv->uppersess; + LIST_REMOVE(hpriv, sessions); + hash = SESSHASH(newConf->cid); + LIST_INSERT_HEAD(&priv->sesshash[hash], hpriv, + sessions); + } + ng_pptpgre_reset(hpriv); /* reset on configure */ + hpriv->conf = *newConf; break; } case NGM_PPTPGRE_GET_CONFIG: - NG_MKRESPONSE(resp, msg, sizeof(priv->conf), M_NOWAIT); + { + hpriv_p hpriv; + + if (msg->header.arglen == 2) { + /* Try to find session by cid. */ + hpriv = ng_pptpgre_find_session(priv, + *((uint16_t *)msg->data)); + if (hpriv == NULL) + ERROUT(EINVAL); + } else if (msg->header.arglen == 0) { + /* Use upper. */ + hpriv = &priv->uppersess; + } else + ERROUT(EINVAL); + NG_MKRESPONSE(resp, msg, sizeof(hpriv->conf), M_NOWAIT); if (resp == NULL) ERROUT(ENOMEM); - bcopy(&priv->conf, resp->data, sizeof(priv->conf)); + bcopy(&hpriv->conf, resp->data, sizeof(hpriv->conf)); break; + } case NGM_PPTPGRE_GET_STATS: case NGM_PPTPGRE_CLR_STATS: case NGM_PPTPGRE_GETCLR_STATS: @@ -389,72 +458,74 @@ done: static int ng_pptpgre_rcvdata(hook_p hook, item_p item) { - const node_p node = NG_HOOK_NODE(hook); - const priv_p priv = NG_NODE_PRIVATE(node); + const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); int rval; /* If not configured, reject */ - if (!priv->conf.enabled) { + if (!hpriv->conf.enabled) { NG_FREE_ITEM(item); return (ENXIO); } - mtx_lock(&priv->mtx); + mtx_lock(&hpriv->mtx); - /* Treat as xmit or recv data */ - if (hook == priv->upper) - rval = ng_pptpgre_xmit(node, item); - else if (hook == priv->lower) - rval = ng_pptpgre_recv(node, item); - else - panic("%s: weird hook", __func__); + rval = ng_pptpgre_xmit(hpriv, item); - mtx_assert(&priv->mtx, MA_NOTOWNED); + mtx_assert(&hpriv->mtx, MA_NOTOWNED); return (rval); } /* - * Destroy node + * Hook disconnection */ static int -ng_pptpgre_shutdown(node_p node) +ng_pptpgre_disconnect(hook_p hook) { + const node_p node = NG_HOOK_NODE(hook); const priv_p priv = NG_NODE_PRIVATE(node); + const hpriv_p hpriv = NG_HOOK_PRIVATE(hook); - /* Reset node (stops timers) */ - ng_pptpgre_reset(node); - - mtx_destroy(&priv->mtx); + /* Zero out hook pointer */ + if (hook == priv->upper) { + priv->upper = NULL; + priv->uppersess.hook = NULL; + } else if (hook == priv->lower) { + priv->lower = NULL; + } else { + /* Reset node (stops timers) */ + ng_pptpgre_reset(hpriv); - FREE(priv, M_NETGRAPH); + LIST_REMOVE(hpriv, sessions); + mtx_destroy(&hpriv->mtx); + free(hpriv, M_NETGRAPH); + } - /* Decrement ref count */ - NG_NODE_UNREF(node); + /* Go away if no longer connected to anything */ + if ((NG_NODE_NUMHOOKS(node) == 0) + && (NG_NODE_IS_VALID(node))) + ng_rmnode_self(node); return (0); } /* - * Hook disconnection + * Destroy node */ static int -ng_pptpgre_disconnect(hook_p hook) +ng_pptpgre_shutdown(node_p node) { - const node_p node = NG_HOOK_NODE(hook); const priv_p priv = NG_NODE_PRIVATE(node); - /* Zero out hook pointer */ - if (hook == priv->upper) - priv->upper = NULL; - else if (hook == priv->lower) - priv->lower = NULL; - else - panic("%s: unknown hook", __func__); + /* Reset node (stops timers) */ + ng_pptpgre_reset(&priv->uppersess); - /* Go away if no longer connected to anything */ - if ((NG_NODE_NUMHOOKS(node) == 0) - && (NG_NODE_IS_VALID(node))) - ng_rmnode_self(node); + LIST_REMOVE(&priv->uppersess, sessions); + mtx_destroy(&priv->uppersess.mtx); + + FREE(priv, M_NETGRAPH); + + /* Decrement ref count */ + NG_NODE_UNREF(node); return (0); } @@ -466,16 +537,15 @@ ng_pptpgre_disconnect(hook_p hook) * Transmit an outgoing frame, or just an ack if m is NULL. */ static int -ng_pptpgre_xmit(node_p node, item_p item) +ng_pptpgre_xmit(hpriv_p hpriv, item_p item) { - const priv_p priv = NG_NODE_PRIVATE(node); - struct ng_pptpgre_ackp *const a = &priv->ackp; + const priv_p priv = NG_NODE_PRIVATE(hpriv->node); u_char buf[sizeof(struct greheader) + 2 * sizeof(u_int32_t)]; struct greheader *const gre = (struct greheader *)buf; int grelen, error; struct mbuf *m; - mtx_assert(&priv->mtx, MA_OWNED); + mtx_assert(&hpriv->mtx, MA_OWNED); if (item) { NGI_GET_M(item, m); @@ -486,10 +556,10 @@ ng_pptpgre_xmit(node_p node, item_p item) if (m != NULL) { /* Check if windowing is enabled */ - if (priv->conf.enableWindowing) { + if (hpriv->conf.enableWindowing) { /* Is our transmit window full? */ - if ((u_int32_t)PPTP_SEQ_DIFF(priv->xmitSeq, - priv->recvAck) >= a->xmitWin) { + if ((u_int32_t)PPTP_SEQ_DIFF(hpriv->xmitSeq, + hpriv->recvAck) >= hpriv->xmitWin) { priv->stats.xmitDrops++; ERROUT(ENOBUFS); } @@ -507,25 +577,25 @@ ng_pptpgre_xmit(node_p node, item_p item) /* Build GRE header */ ((u_int32_t *)gre)[0] = htonl(PPTP_INIT_VALUE); gre->length = (m != NULL) ? htons((u_short)m->m_pkthdr.len) : 0; - gre->cid = htons(priv->conf.peerCid); + gre->cid = htons(hpriv->conf.peerCid); /* Include sequence number if packet contains any data */ if (m != NULL) { gre->hasSeq = 1; - if (priv->conf.enableWindowing) { - a->timeSent[priv->xmitSeq - priv->recvAck] - = ng_pptpgre_time(node); + if (hpriv->conf.enableWindowing) { + hpriv->timeSent[hpriv->xmitSeq - hpriv->recvAck] + = ng_pptpgre_time(); } - priv->xmitSeq++; - gre->data[0] = htonl(priv->xmitSeq); + hpriv->xmitSeq++; + gre->data[0] = htonl(hpriv->xmitSeq); } /* Include acknowledgement (and stop send ack timer) if needed */ - if (priv->conf.enableAlwaysAck || priv->xmitAck != priv->recvSeq) { + if (hpriv->conf.enableAlwaysAck || hpriv->xmitAck != hpriv->recvSeq) { gre->hasAck = 1; - gre->data[gre->hasSeq] = htonl(priv->recvSeq); - priv->xmitAck = priv->recvSeq; - ng_pptpgre_stop_send_ack_timer(node); + gre->data[gre->hasSeq] = htonl(hpriv->recvSeq); + hpriv->xmitAck = hpriv->recvSeq; + ng_pptpgre_stop_send_ack_timer(hpriv); } /* Prepend GRE header to outgoing frame */ @@ -556,10 +626,11 @@ ng_pptpgre_xmit(node_p node, item_p item) * XXX: we should reset timer only after an item has been sent * successfully. */ - if (gre->hasSeq && priv->xmitSeq == priv->recvAck + 1) - ng_pptpgre_start_recv_ack_timer(node); + if (hpriv->conf.enableWindowing && + gre->hasSeq && hpriv->xmitSeq == hpriv->recvAck + 1) + ng_pptpgre_start_recv_ack_timer(hpriv); - mtx_unlock(&priv->mtx); + mtx_unlock(&hpriv->mtx); /* Deliver packet */ if (item) { @@ -571,7 +642,7 @@ ng_pptpgre_xmit(node_p node, item_p item) return (error); done: - mtx_unlock(&priv->mtx); + mtx_unlock(&hpriv->mtx); NG_FREE_M(m); if (item) NG_FREE_ITEM(item); @@ -582,8 +653,10 @@ done: * Handle an incoming packet. The packet includes the IP header. */ static int -ng_pptpgre_recv(node_p node, item_p item) +ng_pptpgre_rcvdata_lower(hook_p hook, item_p item) { + hpriv_p hpriv; + node_p node = NG_HOOK_NODE(hook); const priv_p priv = NG_NODE_PRIVATE(node); int iphlen, grelen, extralen; const struct greheader *gre; @@ -591,8 +664,6 @@ ng_pptpgre_recv(node_p node, item_p item) int error = 0; struct mbuf *m; - mtx_assert(&priv->mtx, MA_OWNED); - NGI_GET_M(item, m); /* Update stats */ priv->stats.recvPackets++; @@ -646,109 +717,111 @@ ng_pptpgre_recv(node_p node, item_p item) priv->stats.recvBadGRE++; ERROUT(EINVAL); } - if (ntohs(gre->cid) != priv->conf.cid) { + + hpriv = ng_pptpgre_find_session(priv, ntohs(gre->cid)); + if (hpriv == NULL || hpriv->hook == NULL || !hpriv->conf.enabled) { priv->stats.recvBadCID++; ERROUT(EINVAL); } + mtx_lock(&hpriv->mtx); /* Look for peer ack */ if (gre->hasAck) { - struct ng_pptpgre_ackp *const a = &priv->ackp; const u_int32_t ack = ntohl(gre->data[gre->hasSeq]); - const int index = ack - priv->recvAck - 1; + const int index = ack - hpriv->recvAck - 1; long sample; long diff; /* Sanity check ack value */ - if (PPTP_SEQ_DIFF(ack, priv->xmitSeq) > 0) { + if (PPTP_SEQ_DIFF(ack, hpriv->xmitSeq) > 0) { priv->stats.recvBadAcks++; goto badAck; /* we never sent it! */ } - if (PPTP_SEQ_DIFF(ack, priv->recvAck) <= 0) + if (PPTP_SEQ_DIFF(ack, hpriv->recvAck) <= 0) goto badAck; /* ack already timed out */ - priv->recvAck = ack; + hpriv->recvAck = ack; /* Update adaptive timeout stuff */ - if (priv->conf.enableWindowing) { - sample = ng_pptpgre_time(node) - a->timeSent[index]; - diff = sample - a->rtt; - a->rtt += PPTP_ACK_ALPHA(diff); + if (hpriv->conf.enableWindowing) { + sample = ng_pptpgre_time() - hpriv->timeSent[index]; + diff = sample - hpriv->rtt; + hpriv->rtt += PPTP_ACK_ALPHA(diff); if (diff < 0) diff = -diff; - a->dev += PPTP_ACK_BETA(diff - a->dev); + hpriv->dev += PPTP_ACK_BETA(diff - hpriv->dev); /* +2 to compensate low precision of int math */ - a->ato = a->rtt + PPTP_ACK_CHI(a->dev + 2); - if (a->ato > PPTP_MAX_TIMEOUT) - a->ato = PPTP_MAX_TIMEOUT; - if (a->ato < PPTP_MIN_TIMEOUT) - a->ato = PPTP_MIN_TIMEOUT; + hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev + 2); + if (hpriv->ato > PPTP_MAX_TIMEOUT) + hpriv->ato = PPTP_MAX_TIMEOUT; + else if (hpriv->ato < PPTP_MIN_TIMEOUT) + hpriv->ato = PPTP_MIN_TIMEOUT; /* Shift packet transmit times in our transmit window */ - bcopy(a->timeSent + index + 1, a->timeSent, - sizeof(*a->timeSent) + bcopy(hpriv->timeSent + index + 1, hpriv->timeSent, + sizeof(*hpriv->timeSent) * (PPTP_XMIT_WIN - (index + 1))); /* If we sent an entire window, increase window size */ - if (PPTP_SEQ_DIFF(ack, a->winAck) >= 0 - && a->xmitWin < PPTP_XMIT_WIN) { - a->xmitWin++; - a->winAck = ack + a->xmitWin; + if (PPTP_SEQ_DIFF(ack, hpriv->winAck) >= 0 + && hpriv->xmitWin < PPTP_XMIT_WIN) { + hpriv->xmitWin++; + hpriv->winAck = ack + hpriv->xmitWin; } /* Stop/(re)start receive ACK timer as necessary */ - ng_pptpgre_stop_recv_ack_timer(node); - if (priv->recvAck != priv->xmitSeq) - ng_pptpgre_start_recv_ack_timer(node); + ng_pptpgre_stop_recv_ack_timer(hpriv); + if (hpriv->recvAck != hpriv->xmitSeq) + ng_pptpgre_start_recv_ack_timer(hpriv); } } badAck: /* See if frame contains any data */ if (gre->hasSeq) { - struct ng_pptpgre_ackp *const a = &priv->ackp; const u_int32_t seq = ntohl(gre->data[0]); /* Sanity check sequence number */ - if (PPTP_SEQ_DIFF(seq, priv->recvSeq) <= 0) { - if (seq == priv->recvSeq) + if (PPTP_SEQ_DIFF(seq, hpriv->recvSeq) <= 0) { + if (seq == hpriv->recvSeq) priv->stats.recvDuplicates++; else priv->stats.recvOutOfOrder++; + mtx_unlock(&hpriv->mtx); ERROUT(EINVAL); } - priv->recvSeq = seq; + hpriv->recvSeq = seq; /* We need to acknowledge this packet; do it soon... */ - if (!(callout_pending(&a->sackTimer))) { - int maxWait; - - /* Take 1/4 of the estimated round trip time */ - maxWait = (a->rtt >> 2); - + if (!(callout_pending(&hpriv->sackTimer))) { /* If delayed ACK is disabled, send it now */ - if (!priv->conf.enableDelayedAck) { /* ack now */ - ng_pptpgre_xmit(node, NULL); - mtx_lock(&priv->mtx); + if (!hpriv->conf.enableDelayedAck) { /* ack now */ + ng_pptpgre_xmit(hpriv, NULL); + /* ng_pptpgre_xmit() drops the mutex */ } else { /* ack later */ + /* Take 1/4 of the estimated round trip time */ + int maxWait = (hpriv->rtt >> 2); if (maxWait < PPTP_MIN_ACK_DELAY) maxWait = PPTP_MIN_ACK_DELAY; - if (maxWait > PPTP_MAX_ACK_DELAY) + else if (maxWait > PPTP_MAX_ACK_DELAY) maxWait = PPTP_MAX_ACK_DELAY; - ng_pptpgre_start_send_ack_timer(node, maxWait); + ng_pptpgre_start_send_ack_timer(hpriv, maxWait); + mtx_unlock(&hpriv->mtx); } - } + } else + mtx_unlock(&hpriv->mtx); /* Trim mbuf down to internal payload */ m_adj(m, iphlen + grelen); if (extralen > 0) m_adj(m, -extralen); - mtx_unlock(&priv->mtx); + mtx_assert(&hpriv->mtx, MA_NOTOWNED); + /* Deliver frame to upper layers */ - NG_FWD_NEW_DATA(error, item, priv->upper, m); + NG_FWD_NEW_DATA(error, item, hpriv->hook, m); } else { priv->stats.recvLoneAcks++; - mtx_unlock(&priv->mtx); + mtx_unlock(&hpriv->mtx); NG_FREE_ITEM(item); NG_FREE_M(m); /* no data to deliver */ } @@ -756,7 +829,6 @@ badAck: return (error); done: - mtx_unlock(&priv->mtx); NG_FREE_ITEM(item); NG_FREE_M(m); return (error); @@ -773,44 +845,29 @@ done: * recv ack timer, if any. */ static void -ng_pptpgre_start_recv_ack_timer(node_p node) +ng_pptpgre_start_recv_ack_timer(hpriv_p hpriv) { - const priv_p priv = NG_NODE_PRIVATE(node); - struct ng_pptpgre_ackp *const a = &priv->ackp; int remain, ticks; - if (!priv->conf.enableWindowing) - return; - /* Compute how long until oldest unack'd packet times out, and reset the timer to that time. */ - remain = (a->timeSent[0] + a->ato) - ng_pptpgre_time(node); + remain = (hpriv->timeSent[0] + hpriv->ato) - ng_pptpgre_time(); if (remain < 0) remain = 0; -#ifdef DEBUG_RAT - a->timerLength = remain; - a->timerStart = ng_pptpgre_time(node); -#endif /* Be conservative: timeout can happen up to 1 tick early */ ticks = (((remain * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE) + 1; - ng_callout(&a->rackTimer, node, NULL, ticks, - ng_pptpgre_recv_ack_timeout, NULL, 0); + ng_callout(&hpriv->rackTimer, hpriv->node, hpriv->hook, + ticks, ng_pptpgre_recv_ack_timeout, hpriv, 0); } /* * Stop receive ack timer. */ static void -ng_pptpgre_stop_recv_ack_timer(node_p node) +ng_pptpgre_stop_recv_ack_timer(hpriv_p hpriv) { - const priv_p priv = NG_NODE_PRIVATE(node); - struct ng_pptpgre_ackp *const a = &priv->ackp; - - if (!priv->conf.enableWindowing) - return; - - ng_uncallout(&a->rackTimer, node); + ng_uncallout(&hpriv->rackTimer, hpriv->node); } /* @@ -822,32 +879,21 @@ static void ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) { const priv_p priv = NG_NODE_PRIVATE(node); - struct ng_pptpgre_ackp *const a = &priv->ackp; - - mtx_lock(&priv->mtx); + const hpriv_p hpriv = arg1; /* Update adaptive timeout stuff */ priv->stats.recvAckTimeouts++; - a->rtt = PPTP_ACK_DELTA(a->rtt) + 1; /* +1 to avoid delta*0 case */ - a->ato = a->rtt + PPTP_ACK_CHI(a->dev); - if (a->ato > PPTP_MAX_TIMEOUT) - a->ato = PPTP_MAX_TIMEOUT; - if (a->ato < PPTP_MIN_TIMEOUT) - a->ato = PPTP_MIN_TIMEOUT; - -#ifdef DEBUG_RAT - log(LOG_DEBUG, - "RAT now=%d seq=0x%x sent=%d tstart=%d tlen=%d ato=%d\n", - (int)ng_pptpgre_time(node), priv->recvAck + 1, - (int)a->timeSent[0], (int)a->timerStart, (int)a->timerLength, a->ato); -#endif + hpriv->rtt = PPTP_ACK_DELTA(hpriv->rtt) + 1; /* +1 to avoid delta*0 case */ + hpriv->ato = hpriv->rtt + PPTP_ACK_CHI(hpriv->dev); + if (hpriv->ato > PPTP_MAX_TIMEOUT) + hpriv->ato = PPTP_MAX_TIMEOUT; + else if (hpriv->ato < PPTP_MIN_TIMEOUT) + hpriv->ato = PPTP_MIN_TIMEOUT; /* Reset ack and sliding window */ - priv->recvAck = priv->xmitSeq; /* pretend we got the ack */ - a->xmitWin = (a->xmitWin + 1) / 2; /* shrink transmit window */ - a->winAck = priv->recvAck + a->xmitWin; /* reset win expand time */ - - mtx_unlock(&priv->mtx); + hpriv->recvAck = hpriv->xmitSeq; /* pretend we got the ack */ + hpriv->xmitWin = (hpriv->xmitWin + 1) / 2; /* shrink transmit window */ + hpriv->winAck = hpriv->recvAck + hpriv->xmitWin; /* reset win expand time */ } /* @@ -855,28 +901,23 @@ ng_pptpgre_recv_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) * already running. */ static void -ng_pptpgre_start_send_ack_timer(node_p node, int ackTimeout) +ng_pptpgre_start_send_ack_timer(hpriv_p hpriv, int ackTimeout) { - const priv_p priv = NG_NODE_PRIVATE(node); - struct ng_pptpgre_ackp *const a = &priv->ackp; int ticks; /* Be conservative: timeout can happen up to 1 tick early */ ticks = (((ackTimeout * hz) + PPTP_TIME_SCALE - 1) / PPTP_TIME_SCALE); - ng_callout(&a->sackTimer, node, NULL, ticks, - ng_pptpgre_send_ack_timeout, NULL, 0); + ng_callout(&hpriv->sackTimer, hpriv->node, hpriv->hook, + ticks, ng_pptpgre_send_ack_timeout, hpriv, 0); } /* * Stop send ack timer. */ static void -ng_pptpgre_stop_send_ack_timer(node_p node) +ng_pptpgre_stop_send_ack_timer(hpriv_p hpriv) { - const priv_p priv = NG_NODE_PRIVATE(node); - struct ng_pptpgre_ackp *const a = &priv->ackp; - - ng_uncallout(&a->sackTimer, node); + ng_uncallout(&hpriv->sackTimer, hpriv->node); } /* @@ -888,12 +929,12 @@ ng_pptpgre_stop_send_ack_timer(node_p node) static void ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) { - const priv_p priv = NG_NODE_PRIVATE(node); + const hpriv_p hpriv = arg1; - mtx_lock(&priv->mtx); + mtx_lock(&hpriv->mtx); /* Send a frame with an ack but no payload */ - ng_pptpgre_xmit(node, NULL); - mtx_assert(&priv->mtx, MA_NOTOWNED); + ng_pptpgre_xmit(hpriv, NULL); + mtx_assert(&hpriv->mtx, MA_NOTOWNED); } /************************************************************************* @@ -901,64 +942,62 @@ ng_pptpgre_send_ack_timeout(node_p node, hook_p hook, void *arg1, int arg2) *************************************************************************/ /* - * Reset state + * Find the hook with a given session ID. */ -static void -ng_pptpgre_reset(node_p node) +static hpriv_p +ng_pptpgre_find_session(priv_p privp, u_int16_t cid) { - const priv_p priv = NG_NODE_PRIVATE(node); - struct ng_pptpgre_ackp *const a = &priv->ackp; + uint16_t hash = SESSHASH(cid); + hpriv_p hpriv = NULL; - mtx_lock(&priv->mtx); + LIST_FOREACH(hpriv, &privp->sesshash[hash], sessions) { + if (hpriv->conf.cid == cid) + break; + } + + return (hpriv); +} +/* + * Reset state (must be called with lock held or from writer) + */ +static void +ng_pptpgre_reset(hpriv_p hpriv) +{ /* Reset adaptive timeout state */ - a->ato = PPTP_MAX_TIMEOUT; - a->rtt = priv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ - if (a->rtt < PPTP_MIN_RTT) - a->rtt = PPTP_MIN_RTT; - a->dev = 0; - a->xmitWin = (priv->conf.recvWin + 1) / 2; - if (a->xmitWin < 2) /* often the first packet is lost */ - a->xmitWin = 2; /* because the peer isn't ready */ - if (a->xmitWin > PPTP_XMIT_WIN) - a->xmitWin = PPTP_XMIT_WIN; - a->winAck = a->xmitWin; + hpriv->ato = PPTP_MAX_TIMEOUT; + hpriv->rtt = hpriv->conf.peerPpd * PPTP_TIME_SCALE / 10; /* ppd in 10ths */ + if (hpriv->rtt < PPTP_MIN_RTT) + hpriv->rtt = PPTP_MIN_RTT; + hpriv->dev = 0; + hpriv->xmitWin = (hpriv->conf.recvWin + 1) / 2; + if (hpriv->xmitWin < 2) /* often the first packet is lost */ + hpriv->xmitWin = 2; /* because the peer isn't ready */ + else if (hpriv->xmitWin > PPTP_XMIT_WIN) + hpriv->xmitWin = PPTP_XMIT_WIN; + hpriv->winAck = hpriv->xmitWin; /* Reset sequence numbers */ - priv->recvSeq = ~0; - priv->recvAck = ~0; - priv->xmitSeq = ~0; - priv->xmitAck = ~0; - - /* Reset start time */ - getmicrouptime(&priv->startTime); - - /* Reset stats */ - bzero(&priv->stats, sizeof(priv->stats)); + hpriv->recvSeq = ~0; + hpriv->recvAck = ~0; + hpriv->xmitSeq = ~0; + hpriv->xmitAck = ~0; /* Stop timers */ - ng_pptpgre_stop_send_ack_timer(node); - ng_pptpgre_stop_recv_ack_timer(node); - - mtx_unlock(&priv->mtx); + ng_pptpgre_stop_send_ack_timer(hpriv); + ng_pptpgre_stop_recv_ack_timer(hpriv); } /* * Return the current time scaled & translated to our internally used format. */ static pptptime_t -ng_pptpgre_time(node_p node) +ng_pptpgre_time(void) { - const priv_p priv = NG_NODE_PRIVATE(node); struct timeval tv; pptptime_t t; microuptime(&tv); - if (tv.tv_sec < priv->startTime.tv_sec - || (tv.tv_sec == priv->startTime.tv_sec - && tv.tv_usec < priv->startTime.tv_usec)) - return (0); - timevalsub(&tv, &priv->startTime); t = (pptptime_t)tv.tv_sec * PPTP_TIME_SCALE; t += (pptptime_t)tv.tv_usec / (1000000 / PPTP_TIME_SCALE); return(t); diff --git a/sys/netgraph/ng_pptpgre.h b/sys/netgraph/ng_pptpgre.h index 275f9e2..ecba3ce 100644 --- a/sys/netgraph/ng_pptpgre.h +++ b/sys/netgraph/ng_pptpgre.h @@ -52,6 +52,10 @@ #define NG_PPTPGRE_HOOK_UPPER "upper" /* to upper layers */ #define NG_PPTPGRE_HOOK_LOWER "lower" /* to lower layers */ +/* Session hooks: prefix plus hex session ID, e.g., "session_3e14" */ +#define NG_PPTPGRE_HOOK_SESSION_P "session_" +#define NG_PPTPGRE_HOOK_SESSION_F "session_%04x" + /* Configuration for a session */ struct ng_pptpgre_conf { u_char enabled; /* enables traffic flow */ |