Skip to content

Commit

Permalink
pppoe-server: -n to use vendor specific tag as remote*N*umber.
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
jkroonza committed Dec 24, 2024
1 parent 7390e76 commit 05813e2
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 6 deletions.
71 changes: 65 additions & 6 deletions src/pppoe-server.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -176,6 +178,7 @@ static PPPoETag hostUniq;
static PPPoETag relayId;
static PPPoETag receivedCookie;
static PPPoETag requestedService;
static PPPoETag vendorTag;

#define HOSTNAMELEN 256

Expand Down Expand Up @@ -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;
}
}

Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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));

Expand Down Expand Up @@ -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()) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions src/pppoe-server.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down

0 comments on commit 05813e2

Please sign in to comment.