#ifdef __KERNEL__ #include #include #endif #include #include static rtk_rg_isakmp_t isakmpDB[IPSEC_Max_ISAKMP]; unsigned char nat_t_md5[16] = {0x4a,0x13,0x1c,0x81,0x07,0x03,0x58,0x45, 0x5c,0x57,0x28,0xf2,0x0e,0x95,0x45,0x2f}; rtk_rg_fwdEngineAlgReturn_t rtk_rg_init_alg_ESP(void) { bzero(isakmpDB,sizeof(isakmpDB)); return RG_FWDENGINE_ALG_RET_SUCCESS; } int32 dump_isakmpDB(struct seq_file *s, void *v) { int len=0, i; PROC_PRINTF(">>Software isakmp table (jiffies: %lu):\n", jiffies); for(i=0; i>24)&0xff, (isakmpDB[i].local_ip>>16)&0xff, (isakmpDB[i].local_ip>>8)&0xff, (isakmpDB[i].local_ip)&0xff, (isakmpDB[i].alias_ip>>24)&0xff, (isakmpDB[i].alias_ip>>16)&0xff, (isakmpDB[i].alias_ip>>8)&0xff, (isakmpDB[i].alias_ip)&0xff, (isakmpDB[i].peer_ip>>24)&0xff, (isakmpDB[i].peer_ip>>16)&0xff, (isakmpDB[i].peer_ip>>8)&0xff, (isakmpDB[i].peer_ip)&0xff); PROC_PRINTF(" initiator cookie(0x%llx) responder cookie(0x%llx)\n", isakmpDB[i].icookie, isakmpDB[i].rcookie); PROC_PRINTF(" Outbound SPI(0x%x) Inbound SPI(0x%x)\n", isakmpDB[i].ospi, isakmpDB[i].ispi); } return len; } static rtk_rg_isakmp_t * _rtk_rg_esp_findEspOut(uint32 local_ip,uint32 peer_ip, uint32 ospi) { int i; rtk_rg_isakmp_t * tp = isakmpDB; for(i = 0 ; i < IPSEC_Max_ISAKMP ; i++){ if(tp[i].peer_ip == peer_ip && tp[i].local_ip == local_ip && tp[i].ospi == ospi && tp[i].valid == 1 ) { tp[i].expire = jiffies + IPSEC_AGING_TIME * HZ; DEBUG("Found session #%d outbound \n", i); return &tp[i]; } } return NULL; } static rtk_rg_isakmp_t * _rtk_rg_esp_addEsp(uint32 local_ip, uint32 peer_ip,uint32 ospi) { int i; rtk_rg_isakmp_t * tp = isakmpDB; for(i = 0 ; i < IPSEC_Max_ISAKMP ; i++){ if(tp[i].valid == 1 && tp[i].peer_ip == peer_ip && tp[i].local_ip == local_ip) { // Here comes new spi DEBUG("New ESP session #%d out, spi=%x -> %x\n", i, tp[i].ospi, ospi); tp[i].expire = jiffies + IPSEC_AGING_TIME * HZ; tp[i].ospi = ospi; tp[i].ispi = 0; return &tp[i]; } } return NULL; } static rtk_rg_isakmp_t * _rtk_rg_esp_findEspIn(uint32 peer_ip,uint32 alias_ip,uint32 ispi){ int i; rtk_rg_isakmp_t * tp = isakmpDB, *new_tp = NULL; for(i = 0 ; i < IPSEC_Max_ISAKMP ; i++) { if(tp[i].peer_ip == peer_ip && tp[i].alias_ip == alias_ip && tp[i].valid == 1 && tp[i].ispi == ispi ) { tp[i].expire = jiffies + IPSEC_AGING_TIME * HZ; DEBUG("Found session #%d inbound \n", i); return &tp[i]; } if(tp[i].peer_ip == peer_ip && tp[i].alias_ip == alias_ip && tp[i].ispi == 0 && tp[i].valid == 1) { DEBUG("Refresh ESP session #%d on reply (new spi)\n", i); new_tp = &tp[i]; } } if(new_tp != NULL) { // Here comes new spi new_tp->expire = jiffies + IPSEC_AGING_TIME * HZ; new_tp->ispi = ispi; return new_tp; } return NULL; } int _rtk_rg_esp_process(int direct, rtk_rg_alg_tuple_t * pTuple, uint32 spi, rtk_rg_isakmp_t ** pIsakmp) { *pIsakmp = NULL; if(direct == NAPT_DIRECTION_OUTBOUND){ DEBUG("findEspOut src %pI4, dst %pI4, ospi:%x\n", &pTuple->internalIp.ip, &pTuple->remoteIp.ip, spi); *pIsakmp = _rtk_rg_esp_findEspOut(pTuple->internalIp.ip, pTuple->remoteIp.ip, spi); if(*pIsakmp == NULL){ *pIsakmp = _rtk_rg_esp_addEsp(pTuple->internalIp.ip, pTuple->remoteIp.ip, spi); //for the esp packet whose ip pair doesn't exist in isakmp database, drop it if(*pIsakmp == NULL) return 0; } }else{ DEBUG("findEspIn src %pI4, dst %pI4, ispi:%x\n", &pTuple->remoteIp.ip, &pTuple->extIp.ip, spi); *pIsakmp = _rtk_rg_esp_findEspIn(pTuple->remoteIp.ip, pTuple->extIp.ip, spi); if(*pIsakmp != NULL){ DEBUG("reply to localip=%x\n", (*pIsakmp)->local_ip); }else{ DEBUG("can not bind to session on reply, drop it!\n"); return 0; } } return 1; } static rtk_rg_isakmp_t * _rtk_rg_ipsec_findIsakmpOut(uint32 local_ip,uint32 peer_ip,uint32 alias_ip,uint64 icookie,uint64 rcookie) { int i; rtk_rg_isakmp_t *tp = isakmpDB; for(i = 0 ; i < IPSEC_Max_ISAKMP; i++){ if(tp[i].peer_ip == peer_ip && tp[i].local_ip == local_ip && tp[i].icookie == icookie && tp[i].valid == 1){ DEBUG("find IKE out i=%d, local %pI4 peer %pI4 icookie=%x %x\n", i, &local_ip, &peer_ip, *(uint32 *)(&icookie), *((uint32 *)(&icookie)+1)); //update alias_ip if(tp[i].alias_ip !=alias_ip) { DEBUG("update ExtIp %pI4 -> %pI4",tp[i].alias_ip,alias_ip); tp[i].alias_ip = alias_ip; } return &tp[i]; } } return NULL; } static rtk_rg_isakmp_t* _rtk_rg_ipsec_findIsakmpIn(uint32 peer_ip,uint32 alias_ip,uint64 icookie,uint64 rcookie) { int i; rtk_rg_isakmp_t * tp = isakmpDB; for(i = 0 ; i < IPSEC_Max_ISAKMP ; i++){ if(tp[i].peer_ip == peer_ip && tp[i].alias_ip == alias_ip && tp[i].icookie == icookie && tp[i].valid == 1){ tp[i].expire = jiffies + IPSEC_AGING_TIME * HZ; tp[i].rcookie = rcookie; DEBUG("find IKE in i=%d, local_ip=%x, icookie=%x %x\n", i, tp[i].local_ip, *(uint32 *)(&icookie), *((uint32 *)(&icookie)+1)); return &tp[i]; } } return NULL; } static rtk_rg_isakmp_t* _rtk_rg_ipsec_addIsakmp(uint32 local_ip,uint32 peer_ip,uint32 alias_ip, uint64 icookie,uint64 rcookie){ int i, firstInvalidIdx=FAIL, oldestIndex=FAIL, isakmpIdx=FAIL; rtk_rg_isakmp_t * tp = isakmpDB; // find the existed one or the one exists the longset time for(i = 0 ; i < IPSEC_Max_ISAKMP ; i++) { if(tp[i].valid==0) { if(firstInvalidIdx==FAIL) firstInvalidIdx=i; continue; } if(tp[i].local_ip == local_ip && tp[i].peer_ip == peer_ip) { // session refresh tp[i].expire = jiffies + IPSEC_AGING_TIME * HZ; tp[i].icookie = icookie; tp[i].rcookie = rcookie; tp[i].alias_ip = alias_ip; DEBUG("Existed session #%d been found\n", i); return &tp[i]; } if(oldestIndex==FAIL || (oldestIndex!=FAIL && time_before(tp[i].expire, tp[oldestIndex].expire))) { oldestIndex = i; } } if(firstInvalidIdx!=FAIL) { isakmpIdx = firstInvalidIdx; DEBUG("Free session #%d been found\n", firstInvalidIdx); } else if(oldestIndex!=FAIL && time_after(jiffies, tp[oldestIndex].expire)) { isakmpIdx = oldestIndex; DEBUG("Replace the oldest session #%d by new one\n", oldestIndex); } if(isakmpIdx!=FAIL) { memset(&tp[isakmpIdx], 0, sizeof(rtk_rg_isakmp_t)); tp[isakmpIdx].expire = jiffies + IPSEC_AGING_TIME * HZ; tp[isakmpIdx].peer_ip = peer_ip; tp[isakmpIdx].local_ip = local_ip; tp[isakmpIdx].alias_ip = alias_ip; tp[isakmpIdx].icookie = icookie; tp[isakmpIdx].rcookie = rcookie; tp[isakmpIdx].valid = 1; return &tp[isakmpIdx]; } else { DEBUG("Can not found any available isakmp entry."); return NULL; } } static rtk_rg_fwdEngineAlgReturn_t _rtk_rg_ipsec_out_process(int direct, unsigned char * pData, unsigned int dataLen,rtk_rg_alg_connection_t * pConn) { rtk_rg_isakmp_t * pIsakmp; uint64 icookie, rcookie; int i; unsigned char is_natt = 0; //not nat-t if(dataLen<(IPSEC_HEADER_LEN)) return RG_FWDENGINE_ALG_RET_FAIL; //icookie= *(uint64*)pData; //rcookie= *(uint64*)(pData+8); memcpy((void *)&icookie,pData,8); memcpy((void *)&rcookie,pData+8,8); DEBUG("out-bound: s_addr=%pI4, d_addr=%pI4, icookie=%x %x,rcookie=%x %x\n", &pConn->tuple.internalIp.ip, &pConn->tuple.remoteIp.ip, *(uint32 *)(&icookie), *((uint32 *)(&icookie)+1), *(uint32 *)(&rcookie), *((uint32 *)(&rcookie)+1)); if(rcookie==0L) //1st pkt of initiator { if(dataLen > (IPSEC_HEADER_LEN+IPSEC_NATT_MD5_LEN+4)) { for(i=IPSEC_HEADER_LEN;i>>> natt=%d\n",is_natt); if((is_natt == 1) && (rg_db.systemGlobal.ipsec_passthru==PASS_DISABLE)) { DEBUG(">>>>> is nat-t=1 drop\n"); return RG_FWDENGINE_ALG_RET_DROP; } if((is_natt == 0) && (rg_db.systemGlobal.ipsec_passthru==PASS_ENABLE)) { DEBUG(">>>>> is nat-t=0 drop\n"); return RG_FWDENGINE_ALG_RET_DROP; } } //if(rcookie==0 || icookie==0) if(rcookie==0L) //1st pkt of initiator { pIsakmp = _rtk_rg_ipsec_findIsakmpOut(pConn->tuple.internalIp.ip, pConn->tuple.remoteIp.ip,pConn->tuple.extIp.ip, icookie, rcookie); if(pIsakmp == NULL) { rtk_rg_isakmp_t* pRet; DEBUG("This is ORIGINAL dir packet, finding in OUT way\n"); pRet = _rtk_rg_ipsec_addIsakmp(pConn->tuple.internalIp.ip, pConn->tuple.remoteIp.ip, pConn->tuple.extIp.ip, icookie, rcookie); if(pRet==NULL) //no available entry now { DEBUG("In OUT way, but TABLE FULL DROP\n"); return RG_FWDENGINE_ALG_RET_DROP; } else return RG_FWDENGINE_ALG_RET_SUCCESS; } else DEBUG("Found entry=0x%x in OUT way\n",(POINTER_CAST)pIsakmp); } return RG_FWDENGINE_ALG_RET_SUCCESS; } static rtk_rg_fwdEngineAlgReturn_t _rtk_rg_ipsec_in_process(int direct, unsigned char * pData, unsigned int dataLen,rtk_rg_alg_connection_t * pConn) { rtk_rg_isakmp_t * pIsakmp; uint64 icookie, rcookie; if(dataLen<(IPSEC_HEADER_LEN)) return RG_FWDENGINE_ALG_RET_FAIL; icookie= *(uint64*)pData; rcookie= *(uint64*)(pData+8); DEBUG("In-bound: s_addr=%pI4, d_addr=%pI4, icookie=%x %x,rcookie=%x %x\n", &pConn->tuple.remoteIp.ip, &pConn->tuple.extIp.ip, *(uint32 *)(&icookie), *((uint32 *)(&icookie)+1), *(uint32 *)(&rcookie), *((uint32 *)(&rcookie)+1)); //if(rcookie!=0 || icookie==0) if(rcookie!=0L) //reply pkt should have rcookie { pIsakmp = _rtk_rg_ipsec_findIsakmpIn(pConn->tuple.remoteIp.ip, pConn->tuple.extIp.ip, icookie, rcookie); if(pIsakmp == NULL) { DEBUG("In IN way, but NO EXISTED ENTRY DROP\n"); return RG_FWDENGINE_ALG_RET_DROP; /* it is not an expect reply packet, should drop it*/ } else DEBUG("Found entry=0x%x in IN way\n",(POINTER_CAST)pIsakmp); } return RG_FWDENGINE_ALG_RET_SUCCESS; } rtk_rg_fwdEngineAlgReturn_t _rtk_rg_isakmp_handler(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo, unsigned char * pConnInfo) { rtk_rg_fwdEngineAlgReturn_t ret = RG_FWDENGINE_ALG_RET_SUCCESS; #ifdef __KERNEL__ unsigned char * pData, *pAppData; unsigned int appLen=0,dataOff=0; rtk_rg_pktHdr_t *pPktHdr; struct sk_buff *skb; rtk_rg_alg_connection_t * pConn; pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo; pConn = (rtk_rg_alg_connection_t *)pConnInfo; skb= (struct sk_buff *)pSkb; //do nothing before napt modification if(after == 0) return RG_FWDENGINE_ALG_RET_SUCCESS; //only support udp packet if(pPktHdr->tagif&TCP_TAGIF) return RG_FWDENGINE_ALG_RET_FAIL; pData=skb->data; if(pPktHdr->tagif&TCP_TAGIF) dataOff = pPktHdr->l4Offset + pPktHdr->headerLen; else dataOff = pPktHdr->l4Offset + 8; /*udp header length is 8 bytes*/ #if 1 appLen = pPktHdr->l3Len + pPktHdr->l3Offset - dataOff; DEBUG("_rtk_rg_isakmp_handler appLen(%d-%d-%d) = %d\n",pPktHdr->l3Len,pPktHdr->l3Offset,dataOff,appLen); #else appLen = skb->len - dataOff; #endif pAppData = pData + dataOff; if(direct==NAPT_DIRECTION_OUTBOUND) { if(pConn->tuple.isIp6 == 0) { pConn->tuple.extIp.ip = ntohl(*pPktHdr->pIpv4Sip); pConn->tuple.extPort = ntohs(*pPktHdr->pSport); } } if(direct == NAPT_DIRECTION_OUTBOUND) ret = _rtk_rg_ipsec_out_process(direct, pAppData, appLen, pConn); else ret = _rtk_rg_ipsec_in_process(direct, pAppData, appLen, pConn); #endif return ret; } //protocol value in IP header is ESP and before napt modification rtk_rg_fwdEngineReturn_t rtk_rg_alg_ESP(int direct, unsigned char *pSkb,unsigned char *pPktInfo, rtk_rg_isakmp_t ** pIsakmp) { #ifdef __KERNEL__ rtk_rg_pktHdr_t *pPktHdr; struct sk_buff *skb; rtk_rg_alg_tuple_t tuple; unsigned char * pData; int len; uint32 spi; pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo; skb= (struct sk_buff *)pSkb; memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t)); /*Ipv6 doesn't support ip address nat modification*/ if(pPktHdr->tagif & IPV6_TAGIF) { TRACE("[To PS] alg ESP does not support ipv6 packet"); return RG_FWDENGINE_RET_TO_PS; } if(direct==NAPT_DIRECTION_OUTBOUND) { tuple.internalIp.ip = pPktHdr->ipv4Sip; tuple.remoteIp.ip = pPktHdr->ipv4Dip; } else { tuple.extIp.ip = pPktHdr->ipv4Dip; tuple.remoteIp.ip = pPktHdr->ipv4Sip; } pData = skb->data + pPktHdr->l4Offset; len = skb->len - pPktHdr->l4Offset; /*esp header * Security Parameters Index (SPI):4bytes * Sequence Number : 4bytes */ if(len < 8) { TRACE("[Drop] packet len < 8"); return RG_FWDENGINE_RET_DROP; } spi = *(uint32 *)pData; if(_rtk_rg_esp_process(direct, &tuple, spi, pIsakmp)) return RG_FWDENGINE_RET_CONTINUE; else { TRACE("[Drop] drop by esp process"); return RG_FWDENGINE_RET_DROP; } #endif TRACE("[To PS] does not support alg ESP"); return RG_FWDENGINE_RET_TO_PS; } //if udp port is 500, it is a isakmp packet //for IPsec transport mode, need to make sure the internal port and external port are the same. //for IPsec tunnel mode, can't support it since the tcp/udp headr is protected by ESP protocol and it is not visible rtk_rg_fwdEngineAlgReturn_t rtk_rg_algRegFunc_IPsec(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo) { //Attention: caller function needs to make sure it needs to do napt modification //ipv6 address and port doesn't need to do napt modification now #ifdef __KERNEL__ rtk_rg_fwdEngineAlgReturn_t ret=0; rtk_rg_pktHdr_t *pPktHdr; rtk_rg_alg_connection_t * pConn; rtk_rg_alg_tuple_t tuple; pPktHdr = (rtk_rg_pktHdr_t *)pPktInfo; memset(&tuple, 0, sizeof(rtk_rg_alg_tuple_t)); if(after==0) { //Pre function //DEBUG("***************** rtk_rg_algRegFunc_IPsec is initialized\n"); if(direct==NAPT_DIRECTION_OUTBOUND) { _rtk_rg_alg_init_tuple(direct, 1, pPktHdr, &tuple); pConn = _rtk_rg_alg_connection_find(&tuple); if(pConn == NULL) { pConn = _rtk_rg_alg_connection_add(&tuple); } } } else { //Post function if(direct==NAPT_DIRECTION_OUTBOUND) { _rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple); pConn = _rtk_rg_alg_connection_find(&tuple); if(pConn == NULL) return RG_FWDENGINE_ALG_RET_FAIL; ret = _rtk_rg_isakmp_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn); } else { //Attention, pPktHdr->ipv4Dip is not the original external ip after napt modification, it is the internal ip _rtk_rg_alg_init_tuple(direct, after, pPktHdr, &tuple); pConn = _rtk_rg_alg_connection_find(&tuple); if(pConn == NULL) return RG_FWDENGINE_ALG_RET_FAIL; ret = _rtk_rg_isakmp_handler(direct, after, pSkb, pPktInfo, (unsigned char *)pConn); } } #endif return ret; } /*udp port is 4500, it is NAT-Traversal (NAT-T). * 1. if value of the first 4 bytes after outer udp header are zero, it is a isakmp packet * * 2. if udp checksum is 0, it is a esp packet. * we can change the ip address and port in outer header by _rtk_rg_fwdEngine_shortCutNaptPacketModify * so don't need to handle it here * int rtk_rg_algRegFunc_UDP_4500_IPsec(int direct, int after, unsigned char *pSkb,unsigned char *pPktInfo) { return SUCCESS; } */