/** * DPD in Initiator, out Responder * * @param st A state structure (the phase 1 state) * @param n A notification (isakmp_notification) * @param pbs A PB Stream * @return stf_status */ stf_status dpd_inI_outR(struct state *p1st , struct isakmp_notification *const n , pb_stream *pbs) { time_t tm = now(); u_int32_t seqno; if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) { loglog(RC_LOG_SERIOUS, "DPD Error: received R_U_THERE for unestablished ISKAMP SA"); return STF_IGNORE; } if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) { loglog(RC_LOG_SERIOUS, "DPD Error: R_U_THERE has invalid SPI length (%d)", n->isan_spisize); return STF_FAIL + PAYLOAD_MALFORMED; } if (memcmp(pbs->cur, p1st->st_icookie, COOKIE_SIZE) != 0) { /* RFC states we *SHOULD* check cookies, not MUST. So invalid cookies are technically valid, as per Geoffrey Huang */ loglog(RC_LOG_SERIOUS, "DPD Warning: R_U_THERE has invalid icookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (memcmp(pbs->cur, p1st->st_rcookie, COOKIE_SIZE) != 0) { loglog(RC_LOG_SERIOUS, "DPD Warning: R_U_THERE has invalid rcookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (pbs_left(pbs) != sizeof(seqno)) { loglog(RC_LOG_SERIOUS, "DPD Error: R_U_THERE has invalid data length (%d)", (int) pbs_left(pbs)); return STF_FAIL + PAYLOAD_MALFORMED; } seqno = ntohl(*(u_int32_t *)pbs->cur); if (p1st->st_dpd_peerseqno && seqno <= p1st->st_dpd_peerseqno) { loglog(RC_LOG_SERIOUS, "DPD Info: received old or duplicate R_U_THERE"); return STF_IGNORE; } DBG(DBG_DPD, DBG_log("received R_U_THERE seq:%u time:%lu (state=#%lu name=\"%s\")" , seqno , (unsigned long)tm , p1st->st_serialno, p1st->st_connection->name)); p1st->st_dpd_peerseqno = seqno; if (send_isakmp_notification(p1st, R_U_THERE_ACK , pbs->cur, pbs_left(pbs)) != STF_IGNORE) { loglog(RC_LOG_SERIOUS, "DPD Info: could not send R_U_THERE_ACK"); return STF_IGNORE; } /* update the time stamp */ p1st->st_last_dpd = tm; /* * since there was activity, kill any EVENT_DPD_TIMEOUT that might * be waiting. */ if(p1st->st_dpd_event != NULL && p1st->st_dpd_event->ev_type == EVENT_DPD_TIMEOUT) { delete_dpd_event(p1st); } return STF_IGNORE; }
/***************************************************************************************** 函数名称: dpd_outI 功能描述: dpd 处理事件,当dpd第一次触发时添加一个超时事件, 如果是重传的dpd处理,不添加超时事件。 输入参数: p1st 输出参数: 无 返 回 值: 无 ------------------------------------------------------------------------------------------- 最近一次修改记录 : 修改作者: 王之云 修改目的: dpd_outI处理 修改日期: 2012年3月20日 ********************************************************************************************/ static void dpd_outI(struct state *p1st) { time_t tm; u_int32_t seqno; time_t timeout = p1st->st_connection->dpd_timeout; EV_ADD ev_arg; DBG(DBG_DPD, DBG_log("processing dpd for state #%lu (\"%s\")" , p1st->st_serialno , p1st->st_connection->name)); /* If no DPD, then get out of here */ if (!p1st->hidden_variables.st_dpd) return; /* If there is no state, there can be no DPD */ if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) return; /* find out when now is */ tm = now(); if (!p1st->st_dpd_seqno) { p1st->st_dpd_seqno = dpd_seqno++; } seqno = htonl(p1st->st_dpd_seqno); /* 如果dpd事件为空,添加dpd事件 */ if(p1st->st_dpd_event == NULL) { ev_arg.u.st = p1st; event_schedule(EVENT_DPD, p1st->st_connection->dpd_delay, &ev_arg); } /* 如果dpd超时事件为空,添加dpd超时事件 */ if(p1st->st_dpd_timeout_event == NULL) { passert(timeout > 0); ev_arg.u.st = p1st; event_schedule(EVENT_DPD_TIMEOUT, timeout, &ev_arg); } DBG(DBG_DPD, DBG_log("sending R_U_THERE %u to %s:%d (state #%lu)" , seqno , ip_str(&p1st->st_remoteaddr) , p1st->st_remoteport , p1st->st_serialno)); if(p1st->hidden_variables.st_is_dp_dev && p1st->st_connection->modecfg_quick_dpd ) { if (send_quick_isakmp_notification(p1st, R_U_THERE, &seqno, sizeof(seqno)) != STF_IGNORE) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "QUICK DPD Error: connection(%s) send R_U_THERE error\n", p1st->st_connection->name); return; } } else { if (send_isakmp_notification(p1st, R_U_THERE, &seqno, sizeof(seqno)) != STF_IGNORE) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Error: connection(%s) send R_U_THERE error\n", p1st->st_connection->name); return; } } p1st->st_dpd_expectseqno = p1st->st_dpd_seqno++; }
/** * DPD Out Initiator * * @param p2st A state struct that is already in phase2 * @return void */ static void dpd_outI(struct state *p1st, struct state *st, bool eroute_care ,time_t delay, time_t timeout) { time_t tm; time_t last; u_int32_t seqno; bool eroute_idle; time_t nextdelay; DBG(DBG_DPD, DBG_log("processing dpd for state #%lu (\"%s\")" , st->st_serialno , st->st_connection->name)); /* If no DPD, then get out of here */ if (!st->hidden_variables.st_dpd) return; /* If there is no state, there can be no DPD */ if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) return; /* find out when now is */ tm = now(); /* * pick least recent activity value, since with multiple phase 2s, * it may well be that one phase 2 is very active, while the other * for some reason, gets stomped upon by some network screw up. * * (this would only happen if the network was sensitive to different * SPI#, since for NAT-T, all traffic should be on the same UDP port. * At worst, this means that we send a bit more traffic then we need * to when there are multiple SAs and one is much less active. * */ last = (p1st->st_last_dpd > st->st_last_dpd ? st->st_last_dpd : p1st->st_last_dpd ); nextdelay = p1st->st_last_dpd + delay - tm; /* has there been enough activity of late? */ if(nextdelay > 0) { /* Yes, just reschedule "phase 2" */ DBG(DBG_DPD, DBG_log("not yet time for dpd event: %lu < %lu" , (unsigned long)tm , (unsigned long)(p1st->st_last_dpd + delay))); event_schedule(EVENT_DPD, nextdelay, st); return; } /* now plan next check time */ if(nextdelay < 1) { nextdelay = delay; } /* * check the phase 2, if we are supposed to, * and return if it is active recently */ if(eroute_care && !st->hidden_variables.st_nat_traversal) { eroute_idle = was_eroute_idle(st, delay); if(!eroute_idle) { DBG(DBG_DPD, DBG_log("dpd out event not sent, phase 2 active")); /* update phase 2 time stamp only */ st->st_last_dpd = tm; event_schedule(EVENT_DPD, nextdelay, st); return; } } if(st != p1st) { /* * reschedule next event, since we can not do it from the activity * routine. */ event_schedule(EVENT_DPD, nextdelay, st); } if (!p1st->st_dpd_seqno) { /* Get a non-zero random value that has room to grow */ get_rnd_bytes((u_char *)&p1st->st_dpd_seqno , sizeof(p1st->st_dpd_seqno)); p1st->st_dpd_seqno &= 0x7fff; p1st->st_dpd_seqno++; } seqno = htonl(p1st->st_dpd_seqno); /* make sure that the timeout occurs. We do this before the send, * because the send may fail due to network issues, etc, and * the timeout has to occur anyway */ dpd_sched_timeout(p1st, tm, timeout); DBG(DBG_DPD, DBG_log("sending R_U_THERE %u to %s:%d (state #%lu)" , seqno , ip_str(&p1st->st_remoteaddr) , p1st->st_remoteport , p1st->st_serialno)); if (send_isakmp_notification(p1st, R_U_THERE , &seqno, sizeof(seqno)) != STF_IGNORE) { loglog(RC_LOG_SERIOUS, "DPD Error: could not send R_U_THERE"); return; } st->st_last_dpd = tm; p1st->st_last_dpd = tm; p1st->st_dpd_expectseqno = p1st->st_dpd_seqno++; }
/***************************************************************************************** 函数名称: dpd_inI_outR 功能描述: 处理R_U_THERE报文,判断R_U_THERE报文长度是否正确以及 通过序列号判断自己想要的报文 输入参数: p1st,n,pbs 输出参数: stf_status 返 回 值: 无 ------------------------------------------------------------------------------------------- 最近一次修改记录 : 修改作者: 王之云 修改目的: dpd_outI处理 修改日期: 2012年3月20日 ********************************************************************************************/ stf_status dpd_inI_outR(struct state *p1st, struct isakmp_notification *const n, pb_stream *pbs) { time_t tm = now(); u_int32_t seqno; char buf_remoteip[20] = {0}; EV_ADD ev_arg; addrtot(&p1st->st_remoteaddr, 0, buf_remoteip, sizeof(buf_remoteip)); if (!IS_ISAKMP_SA_ESTABLISHED(p1st->st_state)) { return STF_IGNORE; } if (n->isan_spisize != COOKIE_SIZE * 2 || pbs_left(pbs) < COOKIE_SIZE * 2) { IPSEC_log(IPSEC_LOGLEVEL_ERROR, "<%s> < %s > DPD Error: R_U_THERE has invalid SPI length (%d)", p1st->st_connection->name, buf_remoteip, n->isan_spisize); return STF_FAIL + PAYLOAD_MALFORMED; } if (memcmp(pbs->cur, p1st->st_icookie, COOKIE_SIZE) != 0) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Warning: R_U_THERE has invalid icookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (memcmp(pbs->cur, p1st->st_rcookie, COOKIE_SIZE) != 0) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Warning: R_U_THERE has invalid rcookie (broken Cisco?)"); } pbs->cur += COOKIE_SIZE; if (pbs_left(pbs) != sizeof(seqno)) { IPSEC_log(IPSEC_LOGLEVEL_ERROR, "<%s> < %s > DPD Error: R_U_THERE has invalid data length (%d)", p1st->st_connection->name, buf_remoteip, (int) pbs_left(pbs)); return STF_FAIL + PAYLOAD_MALFORMED; } seqno = ntohl(*(u_int32_t *)pbs->cur); if (p1st->st_dpd_peerseqno && seqno <= p1st->st_dpd_peerseqno) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Info: received old or duplicate R_U_THERE"); return STF_IGNORE; } DBG(DBG_DPD, DBG_log("received R_U_THERE seq:%u time:%lu (state=#%lu name=\"%s\")" , seqno , (unsigned long)tm , p1st->st_serialno, p1st->st_connection->name)); p1st->st_dpd_peerseqno = seqno; if (send_isakmp_notification(p1st, R_U_THERE_ACK , pbs->cur, pbs_left(pbs)) != STF_IGNORE) { IPSEC_log(IPSEC_LOGLEVEL_PRIVATE, "DPD Info: could not send R_U_THERE_ACK"); return STF_IGNORE; } p1st->st_last_dpd = tm; if(p1st->st_dpd_timeout_event != NULL) { ev_arg.u.st = p1st; disable_event(EVENT_DPD_TIMEOUT, &ev_arg); } return STF_IGNORE; }