From feb878abbf78925a12e578353bde2b652e73b4c3 Mon Sep 17 00:00:00 2001
From: Mats Erik Andersson <gnu@gisladisker.se>
Date: Sat, 25 Jul 2015 23:01:10 +0200
Subject: [PATCH 05/60] ifconfig: Support changing of hardware address.

Implemented for GNU/Linux as first step.
---
 ChangeLog               | 25 ++++++++++++++++++
 ifconfig/changeif.c     | 57 +++++++++++++++++++++++++++++++++++++++++
 ifconfig/options.c      |  3 ++-
 ifconfig/options.h      |  3 +++
 ifconfig/system/linux.c | 14 ++++++++++
 5 files changed, 101 insertions(+), 1 deletion(-)

diff --git a/ChangeLog b/ChangeLog
index 47c70e3a..c194a545 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,28 @@
+2015-07-25  Mats Erik Andersson  <gnu@gisladisker.se>
+
+	ifconfig: Support changing of hardware address.
+	Implement the setting of link level addresses
+	for systems running GNU/Linux.
+
+	* ifconfig/changeif.c [HAVE_NETINET_ETHER_H]: Include
+	header <netinet/ether.h>.
+	(set_hwaddr): New function.
+	(configure_if): Call set_hwaddr() whenever `ifp->valid'
+	contains IF_VALID_HWADDR.
+	* ifconfig/options.c (ifconfig_initializer): Add a further
+	NULL at the end.
+	(parse_opt_set_hwaddr): New function built by the generic
+	macro PARSE_OPT_SET_ADDR.
+	* ifconfig/options.h (struct ifconfig): Define IF_VALID_HWADDR
+	and add a new member `hwaddr'.
+	(parse_opt_set_hwaddr): New prototype.
+
+	* ifconfig/system/linux.c (system_help): Add parsed option
+	`ether|hwaddr|lladdr MACADDR'.
+	(system_parse_opt_rest): New enum value EXPECT_HWADDR.  Insert
+	this into the parser, triggering on all the keywords `ether',
+	`hwaddr', and `lladdr' for best portability.
+
 2015-07-21  Mats Erik Andersson  <gnu@gisladisker.se>
 
 	ifconfig: Implement a hardware list for BSD systems.
diff --git a/ifconfig/changeif.c b/ifconfig/changeif.c
index 99fc03b4..dfcae9ff 100644
--- a/ifconfig/changeif.c
+++ b/ifconfig/changeif.c
@@ -30,6 +30,9 @@
 #include <sys/ioctl.h>
 #include <net/if.h>
 #include <netinet/in.h>
+#ifdef HAVE_NETINET_ETHER_H
+# include <netinet/ether.h>
+#endif
 #include <arpa/inet.h>
 #include <netdb.h>
 #include "ifconfig.h"
@@ -185,6 +188,58 @@ set_brdaddr (int sfd, struct ifreq *ifr, char *brdaddr)
 #endif
 }
 
+int
+set_hwaddr (int sfd, struct ifreq *ifr, char *hwaddr)
+{
+#ifndef SIOCSIFHWADDR
+  error (0, 0,
+         "don't know how to set a hardware address on this system");
+  return -1;
+#else
+# ifndef ifr_hwaddr
+#  ifdef ifr_enaddr
+#   define ifr_hwaddr ifr_enaddr
+#  endif /* ifr_en_addr */
+# endif /* ifr_hwaddr */
+  int err;
+  struct ether_addr *ether;
+  struct sockaddr *sa = (struct sockaddr *) &ifr->ifr_hwaddr;
+
+  ether = ether_aton (hwaddr);
+  if (!ether)
+    {
+      struct ether_addr addr;
+
+      err = ether_hostton (hwaddr, &addr);
+      if (err)
+	{
+	  error (0, 0, "`%s' is not a valid hardware address", hwaddr);
+	  return -1;
+	}
+
+	ether = &addr;
+    }
+
+  /* Reading the present hardware address is a simple
+   * way of initializing the structure!
+   */
+  (void) ioctl (sfd, SIOCGIFHWADDR, ifr);
+
+  memcpy (&sa->sa_data, ether, sizeof (*ether));
+  err = ioctl (sfd, SIOCSIFHWADDR, ifr);
+  if (err < 0)
+    {
+      error (0, errno, "%s failed", "SIOCSIFHWADDR");
+      return -1;
+    }
+
+  if (verbose)
+    printf ("Set interface hardware address of `%s' to %s.\n",
+	    ifr->ifr_name, ether_ntoa (ether));
+  return 0;
+#endif /* SIOCSIFHWADDR */
+}
+
 int
 set_mtu (int sfd, struct ifreq *ifr, int mtu)
 {
@@ -282,6 +337,8 @@ configure_if (int sfd, struct ifconfig *ifp)
     err = set_dstaddr (sfd, &ifr, ifp->dstaddr);
   if (!err && ifp->valid & IF_VALID_BRDADDR)
     err = set_brdaddr (sfd, &ifr, ifp->brdaddr);
+  if (!err && ifp->valid & IF_VALID_HWADDR)
+    err = set_hwaddr (sfd, &ifr, ifp->hwaddr);
   if (!err && ifp->valid & IF_VALID_MTU)
     err = set_mtu (sfd, &ifr, ifp->mtu);
   if (!err && ifp->valid & IF_VALID_METRIC)
diff --git a/ifconfig/options.c b/ifconfig/options.c
index 9478f310..30320622 100644
--- a/ifconfig/options.c
+++ b/ifconfig/options.c
@@ -61,7 +61,7 @@ int nifs;
 static struct ifconfig ifconfig_initializer = {
   NULL,				/* name */
   0,				/* valid */
-  NULL, NULL, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0
+  NULL, NULL, 0, NULL, NULL, NULL, NULL, 0, 0, 0, 0, NULL
 };
 
 struct format formats[] = {
@@ -338,6 +338,7 @@ PARSE_OPT_SET_ADDR (address, address, ADDR)
 PARSE_OPT_SET_ADDR (netmask, netmask, NETMASK)
 PARSE_OPT_SET_ADDR (dstaddr, destination / peer address, DSTADDR)
 PARSE_OPT_SET_ADDR (brdaddr, broadcast address, BRDADDR)
+PARSE_OPT_SET_ADDR (hwaddr, hardware address, HWADDR)
 
 #define PARSE_OPT_SET_INT(field, fname, fvalid) \
 void								\
diff --git a/ifconfig/options.h b/ifconfig/options.h
index a4b932d3..10528386 100644
--- a/ifconfig/options.h
+++ b/ifconfig/options.h
@@ -52,6 +52,8 @@ struct ifconfig
   int metric;
   int setflags;
   int clrflags;
+# define IF_VALID_HWADDR	0x200
+  char *hwaddr;
 };
 
 struct format
@@ -84,6 +86,7 @@ void parse_opt_set_address (struct ifconfig *ifp, char *addr);
 void parse_opt_set_brdaddr (struct ifconfig *ifp, char *addr);
 void parse_opt_set_dstaddr (struct ifconfig *ifp, char *addr);
 void parse_opt_set_netmask (struct ifconfig *ifp, char *addr);
+void parse_opt_set_hwaddr (struct ifconfig *ifp, char *addr);
 void parse_opt_set_mtu (struct ifconfig *ifp, char *addr);
 void parse_opt_set_metric (struct ifconfig *ifp, char *addr);
 void parse_opt_set_default_format (const char *format);
diff --git a/ifconfig/system/linux.c b/ifconfig/system/linux.c
index ef27d65a..cad35442 100644
--- a/ifconfig/system/linux.c
+++ b/ifconfig/system/linux.c
@@ -675,6 +675,7 @@ system_fh_txqlen (format_data_t form, int argc, char *argv[])
 const char *system_help = "\
  NAME [ADDR] [broadcast BRDADDR]\
  [pointopoint|dstaddr DSTADDR] [netmask MASK]\
+ [ether|hwaddr|lladdr MACADDR]\
  [metric N] [mtu N] [txqueuelen N] [up|down] [FLAGS]";
 
 void
@@ -768,6 +769,7 @@ system_parse_opt_rest (struct ifconfig **ifp, int argc, char *argv[])
     EXPECT_BROADCAST,
     EXPECT_DSTADDR,
     EXPECT_NETMASK,
+    EXPECT_HWADDR,
     EXPECT_MTU,
     EXPECT_METRIC,
     EXPECT_TXQLEN,
@@ -792,6 +794,10 @@ system_parse_opt_rest (struct ifconfig **ifp, int argc, char *argv[])
 	  parse_opt_set_netmask (*ifp, argv[i]);
 	  break;
 
+	case EXPECT_HWADDR:
+	  parse_opt_set_hwaddr (*ifp, argv[i]);
+	  break;
+
 	case EXPECT_MTU:
 	  parse_opt_set_mtu (*ifp, argv[i]);
 	  break;
@@ -828,6 +834,10 @@ system_parse_opt_rest (struct ifconfig **ifp, int argc, char *argv[])
 	expect = EXPECT_DSTADDR;
       else if (!strcmp (argv[i], "netmask"))
 	expect = EXPECT_NETMASK;
+      else if (!strcmp (argv[i], "ether")
+	       || !strcmp(argv[i], "hwaddr")
+	       || !strcmp(argv[i], "lladdr"))
+	expect = EXPECT_HWADDR;
       else if (!strcmp (argv[i], "metric"))
 	expect = EXPECT_METRIC;
       else if (!strcmp (argv[i], "mtu"))
@@ -859,6 +869,10 @@ system_parse_opt_rest (struct ifconfig **ifp, int argc, char *argv[])
       error (0, 0, "option `netmask' requires an argument");
       break;
 
+    case EXPECT_HWADDR:
+      error (0, 0, "option `hwaddr' requires an argument");
+      break;
+
     case EXPECT_METRIC:
       error (0, 0, "option `metric' requires an argument");
       break;
-- 
2.26.0.292.g33ef6b2f38

