--- old/usr/src/cmd/dladm/dladm.c	Thu Jun 25 14:59:32 2020
+++ new/usr/src/cmd/dladm/dladm.c	Thu Jun 25 14:59:32 2020
@@ -63,6 +63,7 @@
 #include <libdlether.h>
 #include <libdliptun.h>
 #include <libdlsim.h>
+#include <libdlfeth.h>
 #include <libdlbridge.h>
 #include <libdloverlay.h>
 #include <libinetutil.h>
@@ -226,6 +227,7 @@
 static cmdfunc_t do_create_etherstub, do_delete_etherstub, do_show_etherstub;
 static cmdfunc_t do_create_simnet, do_modify_simnet;
 static cmdfunc_t do_delete_simnet, do_show_simnet, do_up_simnet;
+static cmdfunc_t do_create_feth, do_delete_feth, do_show_feth, do_up_feth;
 static cmdfunc_t do_show_usage;
 static cmdfunc_t do_create_bridge, do_modify_bridge, do_delete_bridge;
 static cmdfunc_t do_add_bridge, do_remove_bridge, do_show_bridge;
@@ -391,6 +393,15 @@
 	{ "delete-simnet",	do_delete_simnet,	NULL		},
 	{ "show-simnet",	do_show_simnet,		NULL		},
 	{ "up-simnet",		do_up_simnet,		NULL		},
+	{ "create-feth",	do_create_feth,
+	    "    create-feth      [-t] [-s] [-R <root-dir>] [-m <mac address>] "
+		    "<feth-link>" },
+	{ "delete-feth",	do_delete_feth,		
+	    "    delete-feth      [-R <root-dir>] <feth-link>" },
+	{ "show-feth",		do_show_feth,
+	    "    show-feth        [-p] [-o <field>,...] [-P] [<feth-link>]" },
+	{ "up-feth",		do_up_feth,
+	    "    up-feth          <feth-link>\n" },
 	{ "create-bridge",	do_create_bridge,
 	    "    create-bridge    [-R <root-dir>] [-P <protect>] "
 	    "[-p <priority>]\n"
@@ -589,6 +600,12 @@
 	{ NULL, 0, NULL, 0 }
 };
 
+static const struct option feth_lopts[] = {
+	{"temporary",   no_argument,0, 't'  },
+	{"root-dir",    required_argument,      0, 'R'  },
+	{ NULL, 0, NULL, 0 }
+};
+
 static const struct option bridge_lopts[] = {
 	{ "protect",		required_argument,	0, 'P' },
 	{ "root-dir",		required_argument,	0, 'R'	},
@@ -1138,6 +1155,24 @@
 ;
 
 /*
+ * structures for 'dladm show-feth'
+ */
+
+typedef struct feth_fields_buf_s
+{
+	char feth_name[DLPI_LINKNAME_MAX];
+	char feth_macaddr[18];
+} feth_fields_buf_t;
+
+static const ofmt_field_t feth_fields[] = {
+	{ "LINK",12,
+		offsetof(feth_fields_buf_t, feth_name), print_default_cb},
+	{ "MACADDRESS",18,
+		offsetof(feth_fields_buf_t, feth_macaddr), print_default_cb},
+	{ NULL,0, 0, NULL}}
+;
+
+/*
  * structures for 'dladm show-usage'
  */
 
@@ -2896,6 +2931,11 @@
 				(void) strcpy(lbuf->link_over, "?");
 		}
 		break;
+	    }
+	
+	case DATALINK_CLASS_FETH: {
+		(void) strcpy(lbuf->link_over, "--");
+		break;
 	}
 	}
 }
@@ -5753,7 +5793,253 @@
 	ofmt_close(ofmt);
 }
 
+/* ARGSUSED */
 static void
+do_up_feth(int argc, char *argv[], const char *use)
+{
+	(void) dladm_feth_up(handle, DATALINK_ALL_LINKID, 0);
+}
+
+static void
+do_create_feth(int argc, char *argv[], const char *use)
+{
+	uint32_t flags;
+	char *altroot = NULL;
+	int option;
+	dladm_status_t status;
+	char name[MAXLINKNAMELEN];
+	boolean_t	mac_provided = B_FALSE;
+	boolean_t	mac_fixed;
+	uint8_t			mac_addr[ETHERADDRL];
+	
+	name[0] = '\0';
+	flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
+
+	opterr = 0;
+	while ((option = getopt_long(argc, argv, ":tsR:m:",
+					feth_lopts, NULL)) != -1) {
+		switch (option) {
+			case 't':
+				flags &= ~DLADM_OPT_PERSIST;
+				break;
+			case 's':
+				flags |= FETH_FLAG_SINGLESHOT;
+				break;
+			case 'R':
+				altroot = optarg;
+				break;
+                case 'm':
+                        if (mac_provided == B_TRUE)
+                                die("cannot specify -m option twice");
+
+                        if (!dladm_aggr_str2macaddr(optarg, &mac_fixed, mac_addr)) {
+				die("invalid MAC address");
+                        }
+			mac_provided = B_TRUE;
+			printf("have mac address\n");
+                        break;
+			default:
+				die_opterr(optopt, option, use);
+		}
+	}
+
+	/* the simnet id is the required operand */
+	if (optind != (argc - 1))
+		usage();
+
+	if (strlcpy(name, argv[optind], MAXLINKNAMELEN) >= MAXLINKNAMELEN)
+		die("link name too long '%s'", argv[optind]);
+
+	if (!dladm_valid_linkname(name))
+		die("invalid link name '%s'", name);
+
+	if (altroot != NULL)
+		altroot_cmd(altroot, argc, argv);
+
+	if(mac_provided)
+		status = dladm_feth_create_mac(handle, name, flags, mac_addr);
+	else
+		status = dladm_feth_create(handle, name, flags);
+	if (status != DLADM_STATUS_OK)
+		die_dlerr(status, "feth creation failed");
+}
+
+static void
+do_delete_feth(int argc, char *argv[], const char *use)
+{
+	int option;
+	uint32_t flags = DLADM_OPT_ACTIVE | DLADM_OPT_PERSIST;
+	datalink_id_t linkid;
+	char *altroot = NULL;
+	dladm_status_t status;
+	dladm_feth_attr_t flinfo;
+
+	opterr = 0;
+	while ((option = getopt_long(argc, argv, ":R:", feth_lopts,
+					NULL)) != -1) {
+		switch (option) {
+			case 'R':
+				altroot = optarg;
+				break;
+			default:
+				die_opterr(optopt, option, use);
+		}
+	}
+
+	/* get feth name (required last argument) */
+	if (optind != (argc - 1))
+		usage();
+
+	if (!dladm_valid_linkname(argv[optind]))
+		die("invalid link name '%s'", argv[optind]);
+
+	if (altroot != NULL)
+		altroot_cmd(altroot, argc, argv);
+
+	status = dladm_name2info(handle, argv[optind], &linkid, NULL, NULL,
+			NULL);
+	if (status != DLADM_STATUS_OK)
+		die("feth '%s' not found", argv[optind]);
+
+	if ((status = dladm_feth_info(handle, linkid, &flinfo,
+					flags)) != DLADM_STATUS_OK)
+		die_dlerr(status, "failed to retrieve feth information");
+	
+	status = dladm_feth_delete(handle, linkid, flags);
+	if (status != DLADM_STATUS_OK)
+		die_dlerr(status, "feth deletion failed");
+}
+
+static dladm_status_t
+print_feth(show_state_t *state, datalink_id_t linkid)
+{
+	dladm_feth_attr_t     flinfo;
+	uint32_t flags;
+	dladm_status_t status;
+	feth_fields_buf_t     flbuf;
+	char mstr[ETHERADDRL * 3];
+
+	bzero(&flbuf, sizeof (flbuf));
+	if ((status = dladm_datalink_id2info(handle, linkid, &flags, NULL, NULL,
+					flbuf.feth_name, sizeof (flbuf.feth_name)))
+			!= DLADM_STATUS_OK)
+		return (status);
+
+	if (!(state->ls_flags & flags))
+		return (DLADM_STATUS_NOTFOUND);
+
+	if ((status = dladm_feth_info(handle, linkid, &flinfo,
+					state->ls_flags)) != DLADM_STATUS_OK)
+		return (status);
+
+	if (flinfo.sna_mac_len > sizeof (flbuf.feth_macaddr))
+		return (DLADM_STATUS_BADVAL);
+
+	(void) strlcpy(flbuf.feth_macaddr,
+			dladm_aggr_macaddr2str(flinfo.sna_mac_addr, mstr),
+			sizeof (flbuf.feth_macaddr));
+
+	ofmt_print(state->ls_ofmt, &flbuf);
+	return (status);
+}
+
+/* ARGSUSED */
+static int
+show_feth(dladm_handle_t dh, datalink_id_t linkid, void *arg)
+{
+	show_state_t* state = arg;
+
+	state->ls_status = print_feth(state, linkid);
+	return (DLADM_WALK_CONTINUE);
+}
+
+static void
+do_show_feth(int argc, char *argv[], const char *use)
+{
+	int option;
+	uint32_t	flags = DLADM_OPT_ACTIVE;
+	boolean_t       p_arg = B_FALSE;
+	datalink_id_t   linkid = DATALINK_ALL_LINKID;
+	show_state_t    state;
+	dladm_status_t  status;
+	boolean_t       o_arg = B_FALSE;
+	ofmt_handle_t   ofmt;
+	ofmt_status_t   oferr;
+	char *all_fields = "link,macaddress";
+	char *fields_str = all_fields;
+	uint_t		ofmtflags = 0;
+
+	bzero(&state, sizeof (state));
+
+	opterr = 0;
+	while ((option = getopt_long(argc, argv, ":pPo:",
+					show_lopts, NULL)) != -1) {
+		switch (option) {
+			case 'p':
+				if (p_arg)
+					die_optdup(option);
+
+				p_arg = B_TRUE;
+				state.ls_parsable = p_arg;
+				break;
+			case 'P':
+				if (flags != DLADM_OPT_ACTIVE)
+					die_optdup(option);
+
+				flags = DLADM_OPT_PERSIST;
+				break;
+			case 'o':
+				o_arg = B_TRUE;
+				fields_str = optarg;
+				break;
+			default:
+				die_opterr(optopt, option, use);
+				break;
+		}
+	}
+
+	if (p_arg && !o_arg)
+		die("-p requires -o");
+
+	if (strcasecmp(fields_str, "all") == 0) {
+		if (p_arg)
+			die("\"-o all\" is invalid with -p");
+		fields_str = all_fields;
+	}
+
+	/* get link name (optional last argument) */
+	if (optind == (argc-1)) {
+		if ((status = dladm_name2info(handle, argv[optind], &linkid,
+		  NULL, NULL, NULL)) != DLADM_STATUS_OK) {
+			die_dlerr(status, "link %s is not valid", argv[optind]);
+		}
+	} else if (optind != argc) {
+		usage();
+	}
+
+	state.ls_flags = flags;
+	state.ls_donefirst = B_FALSE;
+	if (state.ls_parsable)
+		ofmtflags |= OFMT_PARSABLE;
+	oferr = ofmt_open(fields_str, feth_fields, ofmtflags, 0, &ofmt);
+	ofmt_check(oferr, state.ls_parsable, ofmt, die, warn);
+	state.ls_ofmt = ofmt;
+
+	if (linkid == DATALINK_ALL_LINKID) {
+		(void) dladm_walk_datalink_id(show_feth, handle, &state,
+				DATALINK_CLASS_FETH, DATALINK_ANY_MEDIATYPE, flags);
+	} else {
+		(void) show_feth(handle, linkid, &state);
+		if (state.ls_status != DLADM_STATUS_OK) {
+			ofmt_close(ofmt);
+			die_dlerr(state.ls_status, "failed to show feth %s",
+					argv[optind]);
+		}
+	}
+	ofmt_close(ofmt);
+}
+
+static void
 link_stats(datalink_id_t linkid, uint_t interval, char *fields_str,
     show_state_t *state)
 {
@@ -8032,7 +8318,8 @@
 		    &media) != DLADM_STATUS_OK)
 			die("invalid link name '%s'", links[n]);
 		if (class & ~(DATALINK_CLASS_PHYS | DATALINK_CLASS_AGGR |
-		    DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET))
+		    DATALINK_CLASS_ETHERSTUB | DATALINK_CLASS_SIMNET | 
+		    DATALINK_CLASS_FETH))
 			die("%s %s cannot be bridged",
 			    dladm_class2str(class, pointless), links[n]);
 		if (media != DL_ETHER && media != DL_100VG &&
