diff --git a/sbin/ifconfig/ifconfig.8 b/sbin/ifconfig/ifconfig.8
index bf6dcaecd71..40f79391727 100644
--- a/sbin/ifconfig/ifconfig.8
+++ b/sbin/ifconfig/ifconfig.8
@@ -28,7 +28,7 @@
 .\"     From: @(#)ifconfig.8	8.3 (Berkeley) 1/5/94
 .\" $FreeBSD: src/sbin/ifconfig/ifconfig.8,v 1.124 2006/10/10 09:44:08 ru Exp $
 .\"
-.Dd June 26, 2020
+.Dd November 13, 2021
 .Dt IFCONFIG 8
 .Os
 .Sh NAME
@@ -341,6 +341,12 @@ Disable permanently promiscuous mode.
 Another name for the
 .Fl alias
 parameter.
+.It Cm description Ar value , Cm descr Ar value
+Specify a description of the interface.
+This can be used to label interfaces in situations where they may
+otherwise be difficult to distinguish.
+.It Cm -description , Cm -descr
+Clear the interface description.
 .It Cm down
 Mark an interface
 .Dq down .
diff --git a/sbin/ifconfig/ifconfig.c b/sbin/ifconfig/ifconfig.c
index a89d595ae44..cc68864f365 100644
--- a/sbin/ifconfig/ifconfig.c
+++ b/sbin/ifconfig/ifconfig.c
@@ -74,6 +74,8 @@
 struct	ifreq ifr;
 
 char	name[IFNAMSIZ];
+char	*descr = NULL;
+size_t	descrlen = 64;
 int	setaddr;
 int	setmask;
 int	doalias;
@@ -1166,6 +1168,37 @@ setifpollcpu(const char *val, int dummy __unused, int s,
 	setifflags("npolling", IFF_NPOLLING, s, afp);
 }
 
+static void
+setifdescr(const char *val, int dummy __unused, int s,
+    const struct afswtch *afp __unused)
+{
+	char *newdescr;
+
+	ifr.ifr_buffer.length = strlen(val) + 1;
+	if (ifr.ifr_buffer.length == 1) {
+		ifr.ifr_buffer.buffer = newdescr = NULL;
+		ifr.ifr_buffer.length = 0;
+	} else {
+		newdescr = strdup(val);
+		ifr.ifr_buffer.buffer = newdescr;
+		if (newdescr == NULL) {
+			warn("no memory to set ifdescr");
+			return;
+		}
+	}
+
+	if (ioctl(s, SIOCSIFDESCR, (caddr_t)&ifr) < 0)
+		warn("ioctl (set descr)");
+
+	free(newdescr);
+}
+
+static void
+unsetifdescr(const char *val, int dummy __unused, int s,
+    const struct afswtch *afp __unused)
+{
+	setifdescr("", 0, s, 0);
+}
 
 #define	IFFBITS \
 "\020\1UP\2BROADCAST\3DEBUG\4LOOPBACK\5POINTOPOINT\6SMART\7RUNNING" \
@@ -1210,6 +1243,28 @@ status(const struct afswtch *afp, const struct sockaddr_dl *sdl,
 		printf(" mtu %d", ifr.ifr_mtu);
 	putchar('\n');
 
+	for (;;) {
+		if ((descr = reallocf(descr, descrlen)) != NULL) {
+			ifr.ifr_buffer.buffer = descr;
+			ifr.ifr_buffer.length = descrlen;
+			if (ioctl(s, SIOCGIFDESCR, &ifr) == 0) {
+				if (ifr.ifr_buffer.length > 1)
+					printf("\tdescription: %s\n", descr);
+			} else if (errno == ENOMSG) {
+				break;
+			} else if (errno == ENAMETOOLONG) {
+				descrlen = ifr.ifr_buffer.length;
+				continue;
+			} else {
+				warn("ioctl (get descr)");
+			}
+		} else {
+			warn("unable to allocate memory for interface "
+			    "description");
+		}
+		break;
+	}
+
 	if (ioctl(s, SIOCGIFCAP, (caddr_t)&ifr) == 0) {
 		if (ifr.ifr_curcap != 0) {
 			printb("\toptions", ifr.ifr_curcap, IFCAPBITS);
@@ -1385,6 +1440,10 @@ static struct cmd basic_cmds[] = {
 	DEF_CMD("-arp",		IFF_NOARP,	setifflags),
 	DEF_CMD("debug",	IFF_DEBUG,	setifflags),
 	DEF_CMD("-debug",	-IFF_DEBUG,	setifflags),
+	DEF_CMD_ARG("description",		setifdescr),
+	DEF_CMD_ARG("descr",			setifdescr),
+	DEF_CMD("-description",	0,		unsetifdescr),
+	DEF_CMD("-descr",	0,		unsetifdescr),
 	DEF_CMD("promisc",	IFF_PPROMISC,	setifflags),
 	DEF_CMD("-promisc",	-IFF_PPROMISC,	setifflags),
 	DEF_CMD("add",		IFF_UP,		notealias),
diff --git a/share/man/man4/netintro.4 b/share/man/man4/netintro.4
index 46c839dac65..dcf0de4b4b3 100644
--- a/share/man/man4/netintro.4
+++ b/share/man/man4/netintro.4
@@ -28,7 +28,7 @@
 .\"     @(#)netintro.4	8.2 (Berkeley) 11/30/93
 .\" $FreeBSD: src/share/man/man4/netintro.4,v 1.10.2.6 2002/08/30 14:23:38 sobomax Exp $
 .\"
-.Dd March 3, 2020
+.Dd November 13, 2021
 .Dt NETINTRO 4
 .Os
 .Sh NAME
@@ -188,6 +188,7 @@ struct	ifreq {
         struct    sockaddr ifru_addr;
         struct    sockaddr ifru_dstaddr;
         struct    sockaddr ifru_broadaddr;
+        struct    ifreq_buffer ifru_buffer;
         short     ifru_flags[2];
         int       ifru_metric;
         int       ifru_mtu;
@@ -197,6 +198,7 @@ struct	ifreq {
 #define ifr_addr      ifr_ifru.ifru_addr      /* address */
 #define ifr_dstaddr   ifr_ifru.ifru_dstaddr   /* other end of p-to-p link */
 #define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_buffer    ifr_ifru.ifru_buffer    /* user supplied buffer with its length */
 #define ifr_flags     ifr_ifru.ifru_flags[0]  /* flags (low 16 bits) */
 #define ifr_flagshigh ifr_ifru.ifru_flags[1]  /* flags (high 16 bits) */
 #define ifr_metric    ifr_ifru.ifru_metric    /* metric */
@@ -231,6 +233,32 @@ Get interface address for protocol family.
 Get point to point address for protocol family and interface.
 .It Dv SIOCGIFBRDADDR
 Get broadcast address for protocol family and interface.
+.It Dv SIOCGIFDESCR
+Get the interface description, returned in the
+.Va buffer
+field of
+.Va ifru_buffer
+struct.
+The user supplied buffer length should be defined in the
+.Va length
+field of
+.Va ifru_buffer
+struct passed in as parameter, and the length would include
+the terminating nul character.
+If there is not enough space to hold the interface description,
+no copy would be done and an error would be returned.
+The kernel will store the required buffer length in the
+.Va length
+field upon return, regardless whether the buffer itself is
+sufficient to hold the data.
+.It Dv SIOCSIFDESCR
+Set the interface description to the value of the
+.Va buffer
+field of
+.Va ifru_buffer
+struct, with
+.Va length
+field specifying its length (counting the terminating nul).
 .It Dv SIOCSIFFLAGS
 Set interface flags field.  If the interface is marked down,
 any processes currently routing packets through the interface
@@ -357,6 +385,13 @@ struct if_clonereq {
         char    *ifcr_buffer;   /* buffer for cloner names */
 };
 .Ed
+.Bd -literal
+/* Structure used in SIOCGIFDESCR and SIOCSIFDESCR requests */
+struct ifreq_buffer {
+        size_t  length;         /* length of the buffer */
+        void   *buffer;         /* pointer to userland space buffer */
+};
+.Ed
 .Sh SEE ALSO
 .Xr ioctl 2 ,
 .Xr socket 2 ,
diff --git a/sys/net/if.c b/sys/net/if.c
index 23d2e999029..de132fb051b 100644
--- a/sys/net/if.c
+++ b/sys/net/if.c
@@ -161,6 +161,12 @@ static int if_ringmap_dumprdr = 0;
 SYSCTL_INT(_net_link_ringmap, OID_AUTO, dump_rdr, CTLFLAG_RW,
     &if_ringmap_dumprdr, 0, "dump redirect table");
 
+/* Interface description */
+static unsigned int ifdescr_maxlen = 1024;
+SYSCTL_UINT(_net, OID_AUTO, ifdescr_maxlen, CTLFLAG_RW,
+	&ifdescr_maxlen, 0,
+	"administrative maximum length for interface description");
+
 SYSINIT(interfaces, SI_SUB_PROTO_IF, SI_ORDER_FIRST, ifinit, NULL);
 SYSINIT(ifnet, SI_SUB_PRE_DRIVERS, SI_ORDER_ANY, ifnetinit, NULL);
 
@@ -170,6 +176,7 @@ static if_com_free_t *if_com_free[256];
 MALLOC_DEFINE(M_IFADDR, "ifaddr", "interface address");
 MALLOC_DEFINE(M_IFMADDR, "ether_multi", "link-level multicast address");
 MALLOC_DEFINE(M_IFNET, "ifnet", "interface structure");
+MALLOC_DEFINE(M_IFDESCR, "ifdescr", "ifnet descriptions");
 
 int			ifqmaxlen = IFQ_MAXLEN;
 struct ifnethead	ifnet = TAILQ_HEAD_INITIALIZER(ifnet);
@@ -1911,6 +1918,8 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct ucred *cred)
 	short oif_flags;
 	int new_flags;
 	size_t namelen, onamelen;
+	size_t descrlen;
+	char *descrbuf, *odescrbuf;
 	char new_name[IFNAMSIZ];
 	struct ifaddr *ifa;
 	struct sockaddr_dl *sdl;
@@ -1999,6 +2008,59 @@ ifioctl(struct socket *so, u_long cmd, caddr_t data, struct ucred *cred)
 	case SIOCSIFPOLLCPU:
 		break;
 
+	case SIOCGIFDESCR:
+		error = 0;
+		ifnet_lock();
+		if (ifp->if_description == NULL) {
+			ifr->ifr_buffer.length = 0;
+			error = ENOMSG;
+		} else {
+			/* space for terminating nul */
+			descrlen = strlen(ifp->if_description) + 1;
+			if (ifr->ifr_buffer.length < descrlen)
+				error = ENAMETOOLONG;
+			else
+				error = copyout(ifp->if_description,
+				    ifr->ifr_buffer.buffer, descrlen);
+			ifr->ifr_buffer.length = descrlen;
+		}
+		ifnet_unlock();
+		break;
+
+	case SIOCSIFDESCR:
+		error = priv_check_cred(cred, PRIV_ROOT, 0);
+		if (error)
+			break;
+
+		/*
+		 * Copy only (length-1) bytes to make sure that
+		 * if_description is always nul terminated.  The
+		 * length parameter is supposed to count the
+		 * terminating nul in.
+		 */
+		if (ifr->ifr_buffer.length > ifdescr_maxlen)
+			return (ENAMETOOLONG);
+		else if (ifr->ifr_buffer.length == 0)
+			descrbuf = NULL;
+		else {
+			descrbuf = kmalloc(ifr->ifr_buffer.length, M_IFDESCR,
+			    M_WAITOK | M_ZERO);
+			error = copyin(ifr->ifr_buffer.buffer, descrbuf,
+			    ifr->ifr_buffer.length - 1);
+			if (error) {
+				kfree(descrbuf, M_IFDESCR);
+				break;
+			}
+		}
+
+		ifnet_lock();
+		odescrbuf = ifp->if_description;
+		ifp->if_description = descrbuf;
+		ifnet_unlock();
+
+		if (odescrbuf)
+			kfree(odescrbuf, M_IFDESCR);
+
 	case SIOCSIFFLAGS:
 		error = priv_check_cred(cred, PRIV_ROOT, 0);
 		if (error)
@@ -2929,6 +2991,8 @@ if_alloc(uint8_t type)
 void
 if_free(struct ifnet *ifp)
 {
+	if (ifp->if_description != NULL)
+		kfree(ifp->if_description, M_IFDESCR);
 	kfree(ifp, M_IFNET);
 }
 
diff --git a/sys/net/if.h b/sys/net/if.h
index 1220414351b..4495448dead 100644
--- a/sys/net/if.h
+++ b/sys/net/if.h
@@ -220,6 +220,14 @@ struct if_announcemsghdr {
 #define	IFAN_ARRIVAL	0	/* interface arrival */
 #define	IFAN_DEPARTURE	1	/* interface departure */
 
+/*
+ * Buffer with length to be used in SIOCGIFDESCR/SIOCSIFDESCR requests
+ */
+struct ifreq_buffer {
+	size_t  length;
+	void    *buffer;
+};
+
 /*
  * Interface request structure used for socket
  * ioctl's.  All interface ioctl's must have parameter
@@ -232,6 +240,7 @@ struct	ifreq {
 		struct	sockaddr ifru_addr;
 		struct	sockaddr ifru_dstaddr;
 		struct	sockaddr ifru_broadaddr;
+		struct  ifreq_buffer ifru_buffer;
 		short	ifru_flags[2];
 		short	ifru_index;
 		int	ifru_metric;
@@ -246,6 +255,7 @@ struct	ifreq {
 #define	ifr_addr	ifr_ifru.ifru_addr	/* address */
 #define	ifr_dstaddr	ifr_ifru.ifru_dstaddr	/* other end of p-to-p link */
 #define	ifr_broadaddr	ifr_ifru.ifru_broadaddr	/* broadcast address */
+#define ifr_buffer	ifr_ifru.ifru_buffer	/* user supplied buffer with its length */
 #define	ifr_flags	ifr_ifru.ifru_flags[0]	/* flags (low 16 bits) */
 #define	ifr_flagshigh	ifr_ifru.ifru_flags[1]	/* flags (high 16 bits) */
 #define	ifr_metric	ifr_ifru.ifru_metric	/* metric */
diff --git a/sys/net/if_var.h b/sys/net/if_var.h
index 323a77ca747..d009b5c01bd 100644
--- a/sys/net/if_var.h
+++ b/sys/net/if_var.h
@@ -364,7 +364,7 @@ struct ifnet {
 		(void *);
 	int	(*if_resolvemulti)	/* validate/resolve multicast */
 		(struct ifnet *, struct sockaddr **, struct sockaddr *);
-	void	*if_unused5;
+	char	*if_description;	/* interface description */
 	int	(*if_mapsubq)		/* cpuid to if_snd subqueue map */
 		(struct ifaltq *, int);
 	int	if_unused2;
diff --git a/sys/sys/param.h b/sys/sys/param.h
index 006292e5f54..ee12357dc12 100644
--- a/sys/sys/param.h
+++ b/sys/sys/param.h
@@ -234,9 +234,10 @@
  * 600103 - remove the old vmm code
  * 600104 - add posix_fallocate()
  * 600105 - add fdatasync()
+ * 600106 - add SIOCGIFDESCR/SIOCSIFDESCR ioctl
  */
 #undef __DragonFly_version
-#define __DragonFly_version 600105	/* propagated to newvers */
+#define __DragonFly_version 600106	/* propagated to newvers */
 
 #include <sys/_null.h>
 
diff --git a/sys/sys/sockio.h b/sys/sys/sockio.h
index 79f40c0167d..5b11f498f02 100644
--- a/sys/sys/sockio.h
+++ b/sys/sys/sockio.h
@@ -77,6 +77,8 @@
 #define	SIOCGIFDATA	_IOWR('i', 38, struct ifreq)	/* get if_data */
 #define	SIOCSIFNAME	 _IOW('i', 40, struct ifreq)	/* set IF name */
 #define	SIOCGIFALIAS	_IOWR('i', 41, struct ifaliasreq)/* get IF alias */
+#define SIOCSIFDESCR	 _IOW('i', 42, struct ifreq)    /* set ifnet descr */
+#define SIOCGIFDESCR	_IOWR('i', 43, struct ifreq)    /* get ifnet descr */
 
 #define	SIOCADDMULTI	 _IOW('i', 49, struct ifreq)	/* add m'cast addr */
 #define	SIOCDELMULTI	 _IOW('i', 50, struct ifreq)	/* del m'cast addr */
