--- /dev/null
+#
+# Copyright (C) 2021 David Yang <mmyangfl@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=ieee8021xclient
+PKG_RELEASE:=$(AUTORELEASE)
+PKG_LICENSE:=GPL-2.0-or-later
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/ieee8021xclient
+ SECTION:=net
+ CATEGORY:=Network
+ MAINTAINER:=David Yang <mmyangfl@gmail.com>
+ TITLE:=Wired 802.1x client config support
+ DEPENDS:=+wpad
+ PKGARCH:=all
+endef
+
+define Package/ieee8021xclient/description
+ Wired 802.1x client config support in /etc/config/network.
+endef
+
+define Build/Compile
+endef
+
+define Build/Configure
+endef
+
+define Package/ieee8021xclient/install
+ $(INSTALL_DIR) $(1)/lib/netifd/proto
+ $(INSTALL_BIN) ./files/ieee8021xclient.sh $(1)/lib/netifd/proto/ieee8021xclient.sh
+endef
+
+$(eval $(call BuildPackage,ieee8021xclient))
--- /dev/null
+#!/bin/sh
+
+[ -n "$INCLUDE_ONLY" ] || {
+ . /lib/functions.sh
+ . /lib/functions/network.sh
+ . ../netifd-proto.sh
+ init_proto "$@"
+}
+
+cfg_format() {
+ echo "$1" | sed -r 's/^[[:blank:]]+//;/^[[:space:]]*$/d'
+}
+
+ieee8021xclient_exitcode_tostring() {
+ local errorcode=$1
+ [ -n "$errorcode" ] || errorcode=5
+
+ case "$errorcode" in
+ 0) echo "OK" ;;
+ 1) echo "FATAL_ERROR" ;;
+ 5) echo "USER_REQUEST" ;;
+ *) echo "UNKNOWN_ERROR" ;;
+ esac
+}
+
+_wpa_supplicant_common() {
+ local ifname="$1"
+
+ _config="/var/run/wpa_supplicant-$ifname.conf"
+ _pid="/var/run/wpa_supplicant-$ifname.pid"
+}
+
+proto_ieee8021xclient_setup() {
+ local cfg="$1"
+ local ifname="$2"
+
+ local eapol_version
+ local identity anonymous_identity password
+ local ca_cert client_cert private_key private_key_passwd dh_file subject_match
+ local phase1 phase2 ca_cert2 client_cert2 private_key2 private_key_passwd2 dh_file2 subject_match2
+ local eap_workaround
+ json_get_vars eapol_version
+ json_get_vars identity anonymous_identity password
+ json_get_vars ca_cert client_cert private_key private_key_passwd dh_file subject_match
+ json_get_vars phase1 phase2 ca_cert2 client_cert2 private_key2 private_key_passwd2 dh_file2 subject_match2
+ json_get_vars eap_workaround
+
+ # launch
+ local _config _pid
+ _wpa_supplicant_common "$ifname"
+
+ cat > "${_config}" << EOF
+${eapol_version:+eapol_version=${eapol_version}}
+network={
+ ${identity:+identity=${identity}}
+ ${anonymous_identity:+anonymous_identity=${anonymous_identity}}
+ ${password:+password=${password}}
+ ${ca_cert:+ca_cert=${ca_cert}}
+ ${client_cert:+client_cert=${client_cert}}
+ ${private_key:+private_key=${private_key}}
+ ${private_key_passwd:+private_key_passwd=${private_key_passwd}}
+ ${dh_file:+dh_file=${dh_file}}
+ ${subject_match:+subject_match=${subject_match}}
+ ${phase1:+phase1=${phase1}}
+ ${phase2:+phase2=${phase2}}
+ ${ca_cert2:+ca_cert2=${ca_cert2}}
+ ${client_cert2:+client_cert2=${client_cert2}}
+ ${private_key2:+private_key2=${private_key2}}
+ ${private_key_passwd2:+private_key_passwd2=${private_key_passwd2}}
+ ${dh_file2:+dh_file2=${dh_file2}}
+ ${subject_match2:+subject_match2=${subject_match2}}
+ ${eap_workaround:+eap_workaround=1}
+}
+EOF
+ ubus wait_for wpa_supplicant
+ ubus call wpa_supplicant config_add "{ \"driver\":\"wired\", \"iface\": \"$ifname\", \"config\": \"$_config\" }"
+}
+
+proto_ieee8021xclient_teardown() {
+ local ifname="$1"
+ local errorstring=$(ieee8021xclient_exitcode_tostring $ERROR)
+
+ case "$ERROR" in
+ 0)
+ ;;
+ 2)
+ proto_notify_error "$ifname" "$errorstring"
+ proto_block_restart "$ifname"
+ ;;
+ *)
+ proto_notify_error "$ifname" "$errorstring"
+ ;;
+ esac
+
+ ubus call wpa_supplicant config_remove "{\"iface\":\"$ifname\"}"
+}
+
+proto_ieee8021xclient_init_config() {
+ proto_config_add_int eapol_version
+ proto_config_add_string identity
+ proto_config_add_string anonymous_identity
+ proto_config_add_string password
+ proto_config_add_string 'ca_cert:file'
+ proto_config_add_string 'client_cert:file'
+ proto_config_add_string 'private_key:file'
+ proto_config_add_string private_key_passwd
+ proto_config_add_string 'dh_file:file'
+ proto_config_add_string subject_match
+ proto_config_add_string phase1
+ proto_config_add_string phase2
+ proto_config_add_string 'ca_cert2:file'
+ proto_config_add_string 'client_cert2:file'
+ proto_config_add_string 'private_key2:file'
+ proto_config_add_string private_key_passwd2
+ proto_config_add_string 'dh_file2:file'
+ proto_config_add_string subject_match2
+ proto_config_add_boolean eap_workaround
+}
+
+[ -n "$INCLUDE_ONLY" ] || add_protocol ieee8021xclient