Skip to content

Commit

Permalink
PPP enhanced ifname
Browse files Browse the repository at this point in the history
  • Loading branch information
EasyNetDev committed Jun 24, 2021
1 parent 11a9413 commit 86936b6
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 17 deletions.
67 changes: 66 additions & 1 deletion pppd/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <regex.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
Expand All @@ -87,6 +88,7 @@
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <net/if.h>

#include "pppd.h"
#include "magic.h"
Expand Down Expand Up @@ -728,10 +730,73 @@ set_ifunit(int iskey)
{
char ifkey[32];

char ifnameBase[IFNAMSIZ];

memset(ifnameBase, '\0', sizeof(ifnameBase));
memset(ifname, '\0', sizeof(ifname));

// Variable to create regex
regex_t *regex;

regex = malloc(sizeof(regex_t));

size_t nmatch = 2;
regmatch_t pmatch[2];

// We need at least one character in the front of the ifunit
char *pattern = ".+(%d)";

int rc = 0;

// If we are requesting a specific name via "ifname" parameter, we have to check if is static or we have to include the ifunit in the name.
if (req_ifname[0] != '\0')
slprintf(ifname, sizeof(ifname), "%s", req_ifname);
{

#ifdef __linux__
// Compile and create the regex
rc = regcomp(regex, pattern, REG_EXTENDED);

if (rc != 0) {
slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
warn("Warning: couldn't compile the regex \"%s\". Fallback to %s format.", pattern, ifname);
goto set_ifunit_regex_done;
}

// If the regex compliation is successful we will continue to try to extract the interface base name.
if ((rc = regexec(regex, req_ifname, nmatch, pmatch, 0)) != 0) {

This comment has been minimized.

Copy link
@enaess

enaess Oct 22, 2022

Contributor

I am not at all enthused by the use of regular expressions in this context. It feels like overkill in order to resolve this problem.

You should be able to add validation code to the option to ensure it is a proper format string, i.e. first character is [A-Za-z] and if a % is encountered, the following character is a 'd', and this combination can only happen once. Even if that could be done in the validation of the option code (even using regex), it would significantly reduce the complexity in this place., and it should be possible to support it on both Linux and SunOS

// If we can't find our regex, means that is using a fixed name.
slprintf(ifname, sizeof(ifname), "%s", req_ifname);
info("Using static interface naming %s", ifname);
goto set_ifunit_regex_done;
}

/* Matching the dynamic name. We need to have no more than IFNAMSIZ - 4 bytes in the name, to have enough room to build the name.
Linux have a maximum of 16 chars allocated for the interfece names, but only 15 are permited.
So we will have 12 chars for name and 3 chars for digits.
*/
if (pmatch[1].rm_so >= (IFNAMSIZ-4)) {
slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
warn("Warning: interface base name is too long! Please use up to 12 characters for the name to be able to use at least 3 digits! Fallback to default notation %s", ifname);
goto set_ifunit_regex_done;
}
else {
// We need to do a small correction in strlcpy when we are mentioning the length of the string: +1
strlcpy(ifnameBase, req_ifname, pmatch[1].rm_so+1);
slprintf(ifname, sizeof(ifname), "%s%d", ifnameBase, ifunit);
info("Found dynamic custom interface naming: %s", ifname);
}

set_ifunit_regex_done:
// Free regex
regfree(regex);
#else
slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
info("The option ifname requires Linux. Fallback to %s", ifname);
#endif
}
else
slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);

info("Using interface %s", ifname);
script_setenv("IFNAME", ifname, iskey);
slprintf(ifkey, sizeof(ifkey), "%d", ifunit);
Expand Down
3 changes: 1 addition & 2 deletions pppd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,9 +292,8 @@ option_t general_options[] = {
{ "unit", o_int, &req_unit,
"PPP interface unit number to use if possible",
OPT_PRIO | OPT_LLIMIT, 0, 0 },

{ "ifname", o_string, req_ifname,
"Set PPP interface name",
"Set PPP interface name. If format is like ppp%%d / pppoe%%d / l2tp%%d, where %%d will be replaced by unit number. Linux Only.",
OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXIFNAMELEN },

{ "dump", o_bool, &dump_options,
Expand Down
83 changes: 82 additions & 1 deletion pppd/plugins/radius/radius.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ static char const RCSID[] =
#include "ipcp.h"
#include <syslog.h>
#include <sys/types.h>
#include <regex.h>
#include <sys/time.h>
#include <string.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <net/if.h>

#define BUF_LEN 1024

Expand Down Expand Up @@ -1319,9 +1321,88 @@ static int
get_client_port(char *ifname)
{
int port;
if (sscanf(ifname, "ppp%d", &port) == 1) {
char ifnameBase[IFNAMSIZ]; // in case we have a custom interface naming, we extract the basename here and use in regex
char cport[IFNAMSIZ]; // here we have to copy from regex the id of the unit

regex_t *regex;
size_t nmatch = 2;
regmatch_t pmatch[2]; /* Array of matches */

char pattern[64];

int rc = 0;

memset(ifnameBase, '\0', sizeof(ifnameBase));
memset(cport, '\0', sizeof(cport));
memset(pattern, '\0', sizeof(pattern));

regex = malloc(sizeof(regex_t));

/*
Let extract the ifnameBase name from the req_ifname.
This code will use regex for extracting the ppp unit and it will not be affected by the naming of the interface.
We are intereseted to get the number in the end of the interface name.
Only one case is special when we are using fix name for the interface, without %d in the end.
*/
if (req_ifname[0] != '\0')
{

strcpy(pattern, ".+(%d)");

// Compile and create the regex
rc = regcomp(regex, pattern, REG_EXTENDED);

if (rc != 0) {
slprintf(ifnameBase, sizeof(ifnameBase), "%s", PPP_DRV_NAME);
warn("Warning: couldn't compile the regex \"%s\". Fallback to %s%%d format.", pattern, ifnameBase);
goto get_client_port_regcomp_done;
}

// If the regex compliation is successful we will try to extract the interface base name.
if ((rc = regexec(regex, req_ifname, nmatch, pmatch, 0)) != 0) {
// If we can't find our regex, means that is using a fixed name.
slprintf(ifnameBase, sizeof(ifnameBase), "%s", ifname);
info("Using static interface naming %s", ifnameBase);
goto get_client_port_regcomp_done;
}

// We need to do a small correction in strlcpy when we are setting the length of the string: +1
strlcpy(ifnameBase, req_ifname, pmatch[1].rm_so+1);
info("Found dynamic custom interface naming %s", ifnameBase);
}
else {
slprintf(ifnameBase, sizeof(ifnameBase), "%s", PPP_DRV_NAME);
info("Using standard format %s", ifnameBase);
}

get_client_port_regcomp_done:

// Build the new pattern using ifnameBase as part for search
strlcpy(pattern, ifnameBase, IFNAMSIZ);
strcat(pattern, "([0-9]+)");

/* Compile regular expression */
rc = regcomp(regex, pattern, REG_EXTENDED);
if (rc != 0) {
warn("Warning: couldn't compile the regex \"%s\". Fallback to rc_map2id", pattern);
goto get_client_port_regex_done;
}

/* Execute regular expression */
// If the regex compliation is successful we will continue to try to extract the interface base name.
if ((rc = regexec(regex, ifname, nmatch, pmatch, 0)) != 0) {
// If we can't find our regex, means that is using a fixed name.
info("ifname %s doesn't match the req_ifname %s. Fallback to rc_map2id", ifname, req_ifname);
goto get_client_port_regex_done;
}
else {
slprintf(cport, sizeof(cport), "%.*s", pmatch[1].rm_eo - pmatch[1].rm_so, ifname + pmatch[1].rm_so);
port = atoi(cport);

return port;
}

get_client_port_regex_done:
return rc_map2id(ifname);
}

Expand Down
10 changes: 6 additions & 4 deletions pppd/pppd.8
Original file line number Diff line number Diff line change
Expand Up @@ -1178,10 +1178,12 @@ Sets the ppp unit number (for a ppp0 or ppp1 etc interface name) for outbound
connections. If the unit is already in use a dynamically allocated number will
be used.
.TP
.B ifname \fIstring
Set the ppp interface name for outbound connections. If the interface name is
already in use, or if the name cannot be used for any other reason, pppd will
terminate.
.B ifname \fIstring%d
Set the ppp interface name for outbound or inbound connections. If you add
%d in the end of the string pppd will add ppp unit and will create a new
interface named like pppoe0, l2tp1, ptpp3. This is valid only for LINUX.
If is used without %d and the interface name is already in use, or if the
name cannot be used for any other reason, pppd will terminate.
.TP
.B unset \fIname
Remove a variable from the environment variable for scripts that are
Expand Down
77 changes: 68 additions & 9 deletions pppd/sys-linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
#include <stdlib.h>
#include <syslog.h>
#include <string.h>
#include <regex.h>
#include <time.h>
#include <memory.h>
#include <utmp.h>
Expand Down Expand Up @@ -667,6 +668,19 @@ static int make_ppp_unit(void)
{
int x, flags;

// Variable to create regex
regex_t *regex;

regex = malloc(sizeof(regex_t));

size_t nmatch = 2;
regmatch_t pmatch[2];

// We need at least one character in the front of the ifunit
char *pattern = ".+(%d)";

int rc = 0;

if (ppp_dev_fd >= 0) {
dbglog("in make_ppp_unit, already had /dev/ppp open?");
close(ppp_dev_fd);
Expand All @@ -690,17 +704,62 @@ static int make_ppp_unit(void)
error("Couldn't create new ppp unit: %m");

if (x == 0 && req_ifname[0] != '\0') {
/* We can't use ifname as global variable because is not defined yet by set_ifunit function.
This function will be called later in the ppp code.
We have to rewrite almost the same code as in set_ifunit in main.c
*/
struct ifreq ifr;
char t[MAXIFNAMELEN];
char old_ifname[MAXIFNAMELEN];
char new_ifname[MAXIFNAMELEN];
char ifnameBase[IFNAMSIZ];

memset(&ifr, 0, sizeof(struct ifreq));
slprintf(t, sizeof(t), "%s%d", PPP_DRV_NAME, ifunit);
strlcpy(ifr.ifr_name, t, IF_NAMESIZE);
strlcpy(ifr.ifr_newname, req_ifname, IF_NAMESIZE);
x = ioctl(sock_fd, SIOCSIFNAME, &ifr);
if (x < 0)
error("Couldn't rename interface %s to %s: %m", t, req_ifname);
else
info("Renamed interface %s to %s", t, req_ifname);
slprintf(old_ifname, sizeof(old_ifname), "%s%d", PPP_DRV_NAME, ifunit);

// Compile and create the regex
rc = regcomp(regex, pattern, REG_EXTENDED);

if (rc != 0) {
warn("Warning: couldn't compile the regex \"%s\". Fallback to %s%d format", pattern, PPP_DRV_NAME, ifunit);
slprintf(new_ifname, sizeof(new_ifname), "%s%d", PPP_DRV_NAME, ifunit);
goto set_ifunit_regex_done;
}

// If the regex compliation is successful we will continue to try to extract the interface base name.
if ((rc = regexec(regex, req_ifname, nmatch, pmatch, 0)) != 0) {
// If we can't find our regex, means that we are using a fixed name.
info("Using static interface naming %s format", new_ifname);
slprintf(new_ifname, sizeof(new_ifname), "%s", req_ifname);
goto set_ifunit_regex_done;
}

/* Matching the dynamic name. We need to have no more than IFNAMSIZ - 4 bytes for the base name to have enough room to build ifname + digits.
Linux have a maximum of 16 chars allocated for the interfece names, but only 15 are permited.
So we will have maximum 12 chars for name and 3 chars for digits.
*/
if (pmatch[1].rm_so >= (IFNAMSIZ-4)) {
warn("Warning: interface base name is too long! Please use up to 12 characters for the name to be able to use at least 3 digits! Fallback to %s%d format", PPP_DRV_NAME, ifunit);
slprintf(new_ifname, sizeof(new_ifname), "%s%d", PPP_DRV_NAME, ifunit);
goto set_ifunit_regex_done;
}

strlcpy(ifnameBase, req_ifname, pmatch[1].rm_so + 1);
slprintf(new_ifname, sizeof(new_ifname), "%s%d", ifnameBase, ifunit);

set_ifunit_regex_done:
// Free regex
regfree(regex);

if ( strncmp(new_ifname, old_ifname, (strlen(new_ifname) > strlen(new_ifname) ? strlen(new_ifname) : strlen(old_ifname))) ) {
info("Try to rename interface from %s to %s", old_ifname, new_ifname);
strlcpy(ifr.ifr_name, old_ifname, IF_NAMESIZE);
strlcpy(ifr.ifr_newname, new_ifname, IF_NAMESIZE);
x = ioctl(sock_fd, SIOCSIFNAME, &ifr);
if (x < 0)
error("Couldn't rename interface %s to %s: %m", old_ifname, new_ifname);
else
info("Renamed interface %s to %s", old_ifname, new_ifname);
}
}

return x;
Expand Down

0 comments on commit 86936b6

Please sign in to comment.