From 05813e29f6d8c2edde12268559dcdd8214696879 Mon Sep 17 00:00:00 2001 From: Jaco Kroon Date: Fri, 8 Nov 2024 01:05:20 +0200 Subject: [PATCH] pppoe-server: -n to use vendor specific tag as remote*N*umber. This is based on TR101, the BBF (0xDE9) Vendor Specific tag allows for a way for FNO to inject circuit information into PPPoE discovery frames such that ISP can use this information in decision making processes. Just some examples of why this could be useful: 1. Authentication based on location (circuit) of use. 2. Logging where a PPPoE account gets used from. Signed-off-by: Jaco Kroon --- src/pppoe-server.c | 71 ++++++++++++++++++++++++++++++++++++++++++---- src/pppoe-server.h | 1 + 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/src/pppoe-server.c b/src/pppoe-server.c index 2b91254..c166e9c 100644 --- a/src/pppoe-server.c +++ b/src/pppoe-server.c @@ -99,6 +99,8 @@ int NumInterfaces = 0; int MaxInterfaces = 0; int draining = 0; +int vendorTagAsRemoteNumber = 0; /* 0 is disabled, 1 use "Agent Circuit ID", 2 use "Agent Remote ID". */ + /* The number of session slots */ size_t NumSessionSlots; @@ -176,6 +178,7 @@ static PPPoETag hostUniq; static PPPoETag relayId; static PPPoETag receivedCookie; static PPPoETag requestedService; +static PPPoETag vendorTag; #define HOSTNAMELEN 256 @@ -514,6 +517,14 @@ parsePADRTags(uint16_t type, uint16_t len, unsigned char *data, requestedService.length = htons(len); memcpy(requestedService.payload, data, len); break; + case TAG_VENDOR_SPECIFIC: + /* We only care about vendor 0x00000DE9 ("BBF" aka Broadband Forum, IANA) */ + if (len >= 4 && *(uint32_t*)data == htonl(0xDE9)) { + vendorTag.type = htons(type); + vendorTag.length = htons(len); + memcpy(vendorTag.payload, data, len); + } + break; } } @@ -881,6 +892,7 @@ processPADR(Interface *ethif, PPPoEPacket *packet, int len) hostUniq.type = 0; receivedCookie.type = 0; requestedService.type = 0; + vendorTag.type = 0; /* Ignore PADR's not directed at us */ if (memcmp(packet->ethHdr.h_dest, myAddr, ETH_ALEN)) return; @@ -979,6 +991,7 @@ processPADR(Interface *ethif, PPPoEPacket *packet, int len) cliSession->funcs = &DefaultSessionFunctionTable; cliSession->startTime = time(NULL); cliSession->serviceName = serviceName; + cliSession->remoteNumber = NULL; /* Create child process, send PADS packet back */ child = fork(); @@ -1071,6 +1084,32 @@ processPADR(Interface *ethif, PPPoEPacket *packet, int len) cursor += ntohs(hostUniq.length) + TAG_HDR_SIZE; plen += ntohs(hostUniq.length) + TAG_HDR_SIZE; } + if (vendorTag.type && vendorTag.length && vendorTagAsRemoteNumber) { + /* we know the Vendor-ID (32 bits) is 0xDE9, we can skip that */ + int len = ntohs(vendorTag.length) - 4; + char* parse = (char*)vendorTag.payload + 4; + + while (len >= 2) { + uint8_t tag = parse[0]; + uint8_t tlen = parse[1]; + + parse += 2; + len -= 2; + + /* corrupted packet, or malicious manipulation */ + if (tlen > len) + break; + + if (tag == vendorTagAsRemoteNumber) { + /* correct variant */ + cliSession->remoteNumber = strndup(parse, tlen); + break; + } + + len -= tlen; + parse += tlen; + } + } pads.length = htons(plen); sendPacket(NULL, sock, &pads, (int) (plen + HDR_SIZE)); @@ -1209,7 +1248,7 @@ main(int argc, char **argv) char const *s; int cookie_ok = 0; - char const *options = "X:ix:hI:C:L:R:T:m:FN:f:O:o:skp:lrudPS:q:Q:H:M:U:g:"; + char const *options = "X:ix:hI:C:L:R:T:m:FN:f:O:o:skp:lrudPS:q:Q:H:M:U:g:n:"; if (getuid() != geteuid() || getgid() != getegid()) { @@ -1457,6 +1496,18 @@ main(int argc, char **argv) SET_STRING(unix_control, optarg); break; + case 'n': + if (sscanf(optarg, "%d", &opt) != 1) { + usage(argv[0]); + exit(EXIT_FAILURE); + } + if (opt < 0 || opt > 2) { + fprintf(stderr, "-n: Value must be in the range 0 to 2\n"); + exit(EXIT_FAILURE); + } + vendorTagAsRemoteNumber = opt; + break; + case 'h': usage(argv[0]); exit(EXIT_SUCCESS); @@ -1989,11 +2040,17 @@ startPPPD(ClientSession *session) argv[c++] = "default-asyncmap"; argv[c++] = "remotenumber"; - snprintf(buffer, SMALLBUF, "%02x:%02x:%02x:%02x:%02x:%02x", - session->eth[0], session->eth[1], session->eth[2], - session->eth[3], session->eth[4], session->eth[5]); - if (!(argv[c++] = strdup(buffer))) { - exit(EXIT_FAILURE); + if (session->remoteNumber) { + if (!(argv[c++] = strdup(session->remoteNumber))) { + exit(EXIT_FAILURE); + } + } else { + snprintf(buffer, SMALLBUF, "%02x:%02x:%02x:%02x:%02x:%02x", + session->eth[0], session->eth[1], session->eth[2], + session->eth[3], session->eth[4], session->eth[5]); + if (!(argv[c++] = strdup(buffer))) { + exit(EXIT_FAILURE); + } } if (PassUnitOptionToPPPD) { @@ -2178,6 +2235,8 @@ pppoe_free_session(ClientSession *ses) ses->pid = 0; memset(ses->eth, 0, ETH_ALEN); ses->flags = 0; + free(ses->remoteNumber); + ses->remoteNumber = NULL; NumActiveSessions--; return 0; } diff --git a/src/pppoe-server.h b/src/pppoe-server.h index dcbaa3b..8d833e6 100644 --- a/src/pppoe-server.h +++ b/src/pppoe-server.h @@ -78,6 +78,7 @@ typedef struct ClientSessionStruct { time_t startTime; /* When session started */ char const *serviceName; /* Service name */ uint16_t requested_mtu; /* Requested PPP_MAX_PAYLOAD per RFC 4638 */ + char *remoteNumber; /* Vendor-Specific tag extracted remote number ... this is a bit of a hack */ } ClientSession; /* Hack for daemonizing */