pppd: Support arbitrary interface names

This patch implements a new string option "ifname" which allows to specify
fully custom PPP interface names on Linux. It does so by renaming the
allocated pppX device immediately after it has been created to the requested
interface name.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>

--- a/pppd/main.c
+++ b/pppd/main.c
@@ -745,8 +745,11 @@ void
 set_ifunit(iskey)
     int iskey;
 {
-    info("Using interface %s%d", PPP_DRV_NAME, ifunit);
-    slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
+    if (use_ifname[0] == 0)
+	slprintf(ifname, sizeof(ifname), "%s%d", PPP_DRV_NAME, ifunit);
+    else
+	slprintf(ifname, sizeof(ifname), "%s", use_ifname);
+    info("Using interface %s", ifname);
     script_setenv("IFNAME", ifname, iskey);
     if (iskey) {
 	create_pidfile(getpid());	/* write pid to file */
--- a/pppd/options.c
+++ b/pppd/options.c
@@ -112,6 +112,7 @@ int	log_to_fd = 1;		/* send log messages
 bool	log_default = 1;	/* log_to_fd is default (stdout) */
 int	maxfail = 10;		/* max # of unsuccessful connection attempts */
 char	linkname[MAXPATHLEN];	/* logical name for link */
+char	use_ifname[IFNAMSIZ];	/* physical name for PPP link */
 bool	tune_kernel;		/* may alter kernel settings */
 int	connect_delay = 1000;	/* wait this many ms after connect script */
 int	req_unit = -1;		/* requested interface unit */
@@ -277,6 +278,9 @@ option_t general_options[] = {
     { "linkname", o_string, linkname,
       "Set logical name for link",
       OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, MAXPATHLEN },
+    { "ifname", o_string, use_ifname,
+      "Set physical name for PPP interface",
+      OPT_PRIO | OPT_PRIV | OPT_STATIC, NULL, IFNAMSIZ },
 
     { "maxfail", o_int, &maxfail,
       "Maximum number of unsuccessful connection attempts to allow",
--- a/pppd/pppd.h
+++ b/pppd/pppd.h
@@ -74,6 +74,10 @@
 #include "eui64.h"
 #endif
 
+#ifndef IFNAMSIZ
+#define IFNAMSIZ	16
+#endif
+
 /*
  * Limits.
  */
@@ -317,6 +321,7 @@ extern char	*record_file;	/* File to rec
 extern bool	sync_serial;	/* Device is synchronous serial device */
 extern int	maxfail;	/* Max # of unsuccessful connection attempts */
 extern char	linkname[MAXPATHLEN]; /* logical name for link */
+extern char	use_ifname[IFNAMSIZ]; /* physical name for PPP interface */
 extern bool	tune_kernel;	/* May alter kernel settings as necessary */
 extern int	connect_delay;	/* Time to delay after connect script */
 extern int	max_data_rate;	/* max bytes/sec through charshunt */
--- a/pppd/sys-linux.c
+++ b/pppd/sys-linux.c
@@ -161,6 +161,10 @@ struct in6_ifreq {
 /* We can get an EIO error on an ioctl if the modem has hung up */
 #define ok_error(num) ((num)==EIO)
 
+#if !defined(PPP_DRV_NAME)
+#define PPP_DRV_NAME	"ppp"
+#endif /* !defined(PPP_DRV_NAME) */
+
 static int tty_disc = N_TTY;	/* The TTY discipline */
 static int ppp_disc = N_PPP;	/* The PPP discpline */
 static int initfdflags = -1;	/* Initial file descriptor flags for fd */
@@ -620,7 +624,8 @@ void generic_disestablish_ppp(int dev_fd
  */
 static int make_ppp_unit()
 {
-	int x, flags;
+	struct ifreq ifr;
+	int x, flags, s;
 
 	if (ppp_dev_fd >= 0) {
 		dbglog("in make_ppp_unit, already had /dev/ppp open?");
@@ -643,6 +648,30 @@ static int make_ppp_unit()
 	}
 	if (x < 0)
 		error("Couldn't create new ppp unit: %m");
+
+	if (use_ifname[0] != 0) {
+		s = socket(PF_INET, SOCK_DGRAM, 0);
+		if (s < 0)
+			s = socket(PF_PACKET, SOCK_DGRAM, 0);
+		if (s < 0)
+			s = socket(PF_INET6, SOCK_DGRAM, 0);
+		if (s < 0)
+			s = socket(PF_UNIX, SOCK_DGRAM, 0);
+		if (s >= 0) {
+			slprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s%d", PPP_DRV_NAME, ifunit);
+			slprintf(ifr.ifr_newname, sizeof(ifr.ifr_newname), "%s", use_ifname);
+			x = ioctl(s, SIOCSIFNAME, &ifr);
+			close(s);
+		} else {
+			x = s;
+		}
+		if (x < 0) {
+			error("Couldn't rename %s to %s", ifr.ifr_name, ifr.ifr_newname);
+			close(ppp_dev_fd);
+			ppp_dev_fd = -1;
+		}
+	}
+
 	return x;
 }
 
--- a/pppstats/pppstats.c
+++ b/pppstats/pppstats.c
@@ -506,10 +506,12 @@ main(argc, argv)
     if (argc > 0)
 	interface = argv[0];
 
+#if 0
     if (sscanf(interface, PPP_DRV_NAME "%d", &unit) != 1) {
 	fprintf(stderr, "%s: invalid interface '%s' specified\n",
 		progname, interface);
     }
+#endif
 
 #ifndef STREAMS
     {
