vcpe/srcs/pppoe/vcpe_pppoe.c

326 lines
12 KiB
C

//
// Created by xajhuang on 2022/5/10.
//
#include <zlog.h>
#include <uv/unix.h>
#include <uv.h>
#include <uthash/utlist.h>
#include <cjson/cJSON.h>
#include "pppoe_session.h"
#include "netif/rawif.h"
#include "netif/ppp/ppp.h"
#include "netif/ppp/pppoe.h"
#include "netif/pppoeif.h"
#include "misc.h"
#include "user_errno.h"
#include "netif/ppp/pppapi.h"
#include "config.h"
#include "msg_queue.h"
#include "vxlan_pkg.h"
#include "netif/pcapif.h"
typedef struct PPPOE_CACHE {
PPPPOE_SESSION_DATA pSessionData;
struct PPPOE_CACHE *next, *prev;
} PPPOE_CACHE, *PPPPOE_CACHE;
struct PPPOE_ERR_INFO_ {
int errid;
const char *errmsg;
} g_pppoeErr[] = {
{PPPERR_NONE, "No error"},
{PPPERR_PARAM, "Invalid parameter"},
{PPPERR_OPEN, "Unable to open PPP session"},
{PPPERR_DEVICE, "Invalid I/O device for PPP"},
{PPPERR_ALLOC, "Unable to allocate resources"},
{PPPERR_USER, "User interrupt"},
{PPPERR_CONNECT, "Connection lost"},
{PPPERR_AUTHFAIL, "Failed authentication challenge"},
{PPPERR_PROTOCOL, "Failed to meet protocol"},
{PPPERR_PEERDEAD, "Connection timeout"},
{PPPERR_IDLETIMEOUT, "Idle Timeout"},
{PPPERR_CONNECTTIME, "Max connect time reach max time"},
{PPPERR_LOOPBACK, "Loopback detect"},
};
static struct netif *g_rawSocketIf = NULL;
static PPPPOE_CACHE g_pPPPCache = NULL;
static uv_rwlock_t g_cacheLock;
static void pppLinkStatusCallback(ppp_pcb *pcb, int errCode, void *ctx) {
struct netif *pppif = ppp_netif(pcb);
struct pppoe_softc *sc = (struct pppoe_softc *)pcb->link_ctx_cb;
PUSER_INFO_CONTEXT pUser = (PUSER_INFO_CONTEXT)ctx;
switch (errCode) {
case PPPERR_NONE: { /* No error. */
PPPPOE_CACHE pCache = (PPPPOE_CACHE)malloc(sizeof(PPPOE_CACHE));
dzlog_info("<%p> PPPoE user(%05d:%s) connect server succeeded[%08X], Session: %04X\n",
pcb,
pUser->userid,
pUser->user_info.pppoe_user,
pcb->lcp_gotoptions.magicnumber,
sc->sc_session);
#if LWIP_IPV4
pUser->session.data.sessionId = sc->sc_session;
strncpy(pUser->session.data.clientIp, ip4addr_ntoa(netif_ip4_addr(pppif)), MAX_IP_V4_STR);
strncpy(pUser->session.data.clientGw, ip4addr_ntoa(netif_ip4_gw(pppif)), MAX_IP_V4_STR);
strncpy(pUser->session.data.clientMask, ip4addr_ntoa(netif_ip4_netmask(pppif)), MAX_IP_V4_STR);
sprintf(pUser->session.data.clientMac,
"%02X:%02X:%02X:%02X:%02X:%02X",
pUser->user_info.mac_addr[0],
pUser->user_info.mac_addr[1],
pUser->user_info.mac_addr[2],
pUser->user_info.mac_addr[3],
pUser->user_info.mac_addr[4],
pUser->user_info.mac_addr[5]);
dzlog_info(" our_ipaddr = %s\n", pUser->session.data.clientIp);
dzlog_info(" his_ipaddr = %s\n", pUser->session.data.clientGw);
dzlog_info(" netmask = %s\n", pUser->session.data.clientMask);
#endif /* LWIP_IPV4 */
#if LWIP_DNS
dzlog_info(" dns1 = %s\n", ipaddr_ntoa(dns_getserver(0)));
dzlog_info(" dns2 = %s\n", ipaddr_ntoa(dns_getserver(1)));
#endif /* LWIP_DNS */
#if PPP_IPV6_SUPPORT
dzlog_info(" our6_ipaddr = %s\n", ip6addr_ntoa(netif_ip6_addr(pppif, 0)));
#endif /* PPP_IPV6_SUPPORT */
if (pCache) {
pCache->pSessionData = &pUser->session.data;
uv_rwlock_wrlock(&g_cacheLock);
LL_APPEND(g_pPPPCache, pCache);
uv_rwlock_wrunlock(&g_cacheLock);
}
pUser->session.status = STATUS_TASK_CONNECTED;
break;
}
case PPPERR_PARAM:
case PPPERR_AUTHFAIL:
case PPPERR_PROTOCOL:
dzlog_error("<%p> pppLinkStatusCallback: %s(%d)",
pcb,
g_pppoeErr[errCode].errmsg,
g_pppoeErr[errCode].errid);
if (pUser->session.status != STATUS_TASK_DELETE) {
pUser->session.status = STATUS_TASK_ERROR;
}
break;
case PPPERR_USER:
dzlog_info("User(%05d:%s) disconnect\n", pUser->userid, pUser->user_info.pppoe_user);
break;
case PPPERR_OPEN:
case PPPERR_DEVICE:
case PPPERR_ALLOC:
case PPPERR_CONNECT:
case PPPERR_PEERDEAD:
case PPPERR_IDLETIMEOUT:
case PPPERR_CONNECTTIME:
case PPPERR_LOOPBACK:
dzlog_error("<%p> pppLinkStatusCallback: %s(%d)\n",
pcb,
g_pppoeErr[errCode].errmsg,
g_pppoeErr[errCode].errid);
if (pUser->session.status != STATUS_TASK_DELETE) {
pUser->session.status = STATUS_TASK_DISCONNECTED;
}
break;
default: {
printf("<%p> pppLinkStatusCallback: unknown errCode %d\n", pcb, errCode);
break;
}
}
}
_Noreturn void sessionCalcCb(void *UNUSED(pArg)) {
PUSER_INFO_CONTEXT pUserList = get_all_user_by_id();
do {
PUSER_INFO_CONTEXT pUser, pTmp;
uv_rwlock_rdlock(get_user_lock());
HASH_ITER(hh_id, pUserList, pUser, pTmp) {
PPPPOE_SESSION pSession = &pUser->session;
switch (pSession->status) {
case STATUS_TASK_INIT:
if (pppoe_session_create(pUser) == ERR_SUCCESS) {
dzlog_debug("User(%05d:%s) init pppoe session\n", pUser->userid, pUser->user_info.pppoe_user);
vxlan_peer_add(pUser->vxlan.vni, config_get_vxlan_peer_ip(), config_get_vxlan_peer_mac());
pSession->status = STATUS_TASK_DIAL;
}
break;
case STATUS_TASK_DIAL:
if (pUser->session.retry.timeout == 0) {
dzlog_debug("User(%05d:%s) connect PPPoE server\n", pUser->userid, pUser->user_info.pppoe_user);
pppapi_connect(pSession->ppp, 0);
pUser->session.retry.timeout = time(NULL) + PPPOE_MAX_TIMEOUT;
} else if (time(NULL) > pUser->session.retry.timeout) {
pUser->session.retry.timeout = 0;
}
break;
case STATUS_TASK_ERROR:
if (pUser->session.retry.timeout == 0) {
dzlog_error("User(%05d:%s) PPPoE dial error Invalid parameter\n",
pUser->userid,
pUser->user_info.pppoe_user);
// 10秒后尝试重新拨号
pUser->session.retry.timeout = time(NULL) + (PPPOE_MAX_TIMEOUT / 2);
pUser->session.retry.count = 0;
} else if (time(NULL) > pUser->session.retry.timeout) {
dzlog_warn("User(%05d:%s) retry dial %u times\n",
pUser->userid,
pUser->user_info.pppoe_user,
pUser->session.retry.count);
pUser->session.retry.timeout = 0;
pUser->session.retry.count++;
// 下次重新拨号
pSession->status = STATUS_TASK_DIAL;
}
break;
case STATUS_TASK_DISCONNECTED:
dzlog_error("User %s disconnect, auto reconnect\n", pUser->user_info.pppoe_user);
// 自动重新拨号
pSession->status = STATUS_TASK_DIAL;
break;
case STATUS_TASK_DELETE:
if (pUser->session.retry.timeout == 0) {
dzlog_debug("User(%05d:%s) PPPoE deleted\n", pUser->userid, pUser->user_info.pppoe_user);
pUser->session.retry.timeout = time(NULL);
pppapi_free(pUser->session.ppp);
}
break;
#if 0
case STATUS_TASK_CONNECTED:
if (pUser->session.retry.timeout == 0) {
pUser->session.retry.timeout = time(NULL) + 30;
} else if (time(NULL) > pUser->session.retry.timeout) {
dzlog_debug("User(%05d:%s) PPPoE disconnected\n", pUser->userid, pUser->user_info.pppoe_user);
pSession->status = STATUS_TASK_DELETE;
pppapi_close(pUser->session.ppp, 0);
pUser->session.retry.timeout = 0;
}
break;
#endif
default:
break;
}
}
uv_rwlock_rdunlock(get_user_lock());
uv_sleep(1000);
} while (TRUE);
}
_Noreturn void cacheCalcCb(void *UNUSED(pArg)) {
do {
PPPPOE_CACHE pCache, pTmp;
int count;
uv_rwlock_rdlock(&g_cacheLock);
LL_COUNT(g_pPPPCache, pCache, count);
uv_rwlock_rdunlock(&g_cacheLock);
if (count > 0) {
const char *pJsonString;
cJSON *pRoot = cJSON_CreateObject();
cJSON *pSession = cJSON_CreateArray();
cJSON_AddStringToObject(pRoot, "message", "add-ywg-pppoe");
uv_rwlock_wrlock(&g_cacheLock);
LL_FOREACH_SAFE(g_pPPPCache, pCache, pTmp) {
cJSON *pItem = cJSON_CreateObject();
cJSON_AddNumberToObject(pItem, "sessionId", pCache->pSessionData->sessionId);
cJSON_AddStringToObject(pItem, "clientIp", pCache->pSessionData->clientIp);
cJSON_AddStringToObject(pItem, "clientGw", pCache->pSessionData->clientGw);
cJSON_AddStringToObject(pItem, "clientMask", pCache->pSessionData->clientMask);
cJSON_AddStringToObject(pItem, "clientMac", pCache->pSessionData->clientMac);
cJSON_AddStringToObject(pItem, "localMac", pCache->pSessionData->svrBaseMac);
cJSON_AddItemToArray(pSession, pItem);
LL_DELETE(g_pPPPCache, pCache);
free(pCache);
}
uv_rwlock_wrunlock(&g_cacheLock);
cJSON_AddItemToObject(pRoot, "params", pSession);
pJsonString = cJSON_Print(pRoot);
mq_data_send_msg(pJsonString);
cJSON_Delete(pRoot);
free((void *)pJsonString);
}
uv_sleep(1000);
} while (TRUE);
}
int pppoe_session_init() {
static uv_thread_t uvThread, cacheThread;
uv_rwlock_init(&g_cacheLock);
g_rawSocketIf = bind_pcap_if(config_get_vxlan_nic_name(), config_get_vxlan_pkg_filter());
if (g_rawSocketIf) {
dzlog_info("Create Raw Socket netif: <%p>\n", (void *)g_rawSocketIf);
} else {
dzlog_info("Create Raw Socket error: <%p>\n", (void *)g_rawSocketIf);
}
// 启动Session状态机线程
uv_thread_create(&uvThread, sessionCalcCb, NULL);
uv_thread_create(&cacheThread, cacheCalcCb, NULL);
return ERR_SUCCESS;
}
int pppoe_session_create(PUSER_INFO_CONTEXT pUser) {
ppp_pcb *ppp;
struct netif *netif;
struct netif *ppp_netif = (struct netif *)malloc(sizeof(struct netif));
if (ppp_netif == NULL) {
dzlog_error("Malloc %lu bytes memory error\n", sizeof(struct netif));
return -ERR_MALLOC_MEMORY;
}
netif = create_pppoe_if(pUser);
if (netif == NULL) {
dzlog_error("Create PPPoE netif error: %u\n", pUser->userid);
free((void *)ppp_netif);
return -ERR_CREATE_PPPOE_NETIF;
}
pUser->session.nicif = netif;
pUser->session.pppif = ppp_netif;
ppp = pppapi_pppoe_create(pUser->session.pppif, pUser->session.nicif, NULL, NULL, pppLinkStatusCallback, pUser);
if (ppp == NULL) {
dzlog_error("Create PPPoE session error: %u\n", pUser->userid);
netif_remove(netif);
free((void *)ppp_netif);
return -ERR_CREATE_PPP_SESSION;
}
pUser->session.ppp = ppp;
ppp_set_auth(ppp, PPPAUTHTYPE_ANY, pUser->user_info.pppoe_user, pUser->user_info.pppoe_passwd);
dzlog_debug("Create PPPoE netif %p: %u(%c%c:%d, %c%c:%d)\n",
ppp,
pUser->userid,
ppp_netif->name[0],
ppp_netif->name[1],
ppp_netif->num,
netif->name[0],
netif->name[1],
netif->num);
return ERR_SUCCESS;
}
struct netif *get_rawsocket_if(void) {
return g_rawSocketIf;
}