diff --git a/net/Kconfig b/net/Kconfig
index 4215889127..34c1e43c87 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -5,9 +5,12 @@
 menuconfig NET
 	bool "Networking support"
 	default y
+	imply LWIP
 
 if NET
 
+source net/lwip/Kconfig
+
 config ARP_TIMEOUT
 	int "Milliseconds before trying ARP again"
 	default 5000
diff --git a/net/Makefile b/net/Makefile
index 3e2d061338..61930c244e 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_CMD_WOL)  += wol.o
 obj-$(CONFIG_PROT_UDP) += udp.o
 obj-$(CONFIG_PROT_TCP) += tcp.o
 obj-$(CONFIG_CMD_WGET) += wget.o
+obj-$(CONFIG_LWIP) += lwip/
 
 # Disable this warning as it is triggered by:
 # sprintf(buf, index ? "foo%d" : "foo", index)
diff --git a/net/lwip/Kconfig b/net/lwip/Kconfig
new file mode 100644
index 0000000000..f07e26f7d9
--- /dev/null
+++ b/net/lwip/Kconfig
@@ -0,0 +1,55 @@
+menu "LWIP"
+config LWIP
+	bool "Support LWIP library"
+	help
+          Enable the lwIP library code with
+          all dependencies (commands are implemented with lwIP
+          library. This option is automatically enabled if CONFIG_NET=y.
+	  lwIP library (https://git.savannah.nongnu.org/git/lwip.git) provides
+          network stack and application code for U-Boot commands.
+	  Please see doc/develop/net_lwip.rst for more details.
+
+menu "LWIP options"
+
+config LWIP_LIB_DEBUG
+	bool "enable debug"
+	default n
+
+config LWIP_LIB_NOASSERT
+	bool "disable asserts"
+	default y
+	help
+	    Disabling asserts reduces binary size by 16k.
+
+config LWIP_LIB_TCP
+        bool "tcp"
+        default y
+	help
+	   Compile lwIP with TCP protocol support.
+
+config LWIP_LIB_UDP
+        bool "udp"
+        default y
+	help
+	   Compile lwIP with UDP protocol support (needed for TFTP).
+
+config LWIP_LIB_DNS
+        bool "dns"
+        default y
+	help
+	   Compile lwIP with DNS protocol support.
+
+config LWIP_LIB_DHCP
+        bool "dhcp"
+        default y
+	help
+	   Compile lwIP with DHCP protocol support.
+
+config LWIP_LIB_LOOPBACK
+        bool "loopback"
+        help
+	   Increases size by 1k.
+	   Compile lwIP with loopback interface support.
+endmenu
+
+endmenu
diff --git a/net/lwip/Makefile b/net/lwip/Makefile
new file mode 100644
index 0000000000..d1161bea78
--- /dev/null
+++ b/net/lwip/Makefile
@@ -0,0 +1,66 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2023 Linaro Ltd. <maxim.uvarov@linaro.org>
+
+LWIPDIR=lwip-external/src
+
+ccflags-y += -I$(srctree)/net/lwip/port/include
+ccflags-y += -I$(srctree)/net/lwip/lwip-external/src/include -I$(srctree)/net/lwip
+
+obj-$(CONFIG_NET) += $(LWIPDIR)/core/init.o \
+	$(LWIPDIR)/core/def.o \
+	$(LWIPDIR)/core/dns.o \
+	$(LWIPDIR)/core/inet_chksum.o \
+	$(LWIPDIR)/core/ip.o \
+	$(LWIPDIR)/core/mem.o \
+	$(LWIPDIR)/core/memp.o \
+	$(LWIPDIR)/core/netif.o \
+	$(LWIPDIR)/core/pbuf.o \
+	$(LWIPDIR)/core/raw.o \
+	$(LWIPDIR)/core/stats.o \
+	$(LWIPDIR)/core/sys.o \
+	$(LWIPDIR)/core/altcp.o \
+	$(LWIPDIR)/core/altcp_alloc.o \
+	$(LWIPDIR)/core/altcp_tcp.o \
+	$(LWIPDIR)/core/tcp.o \
+	$(LWIPDIR)/core/tcp_in.o \
+	$(LWIPDIR)/core/tcp_out.o \
+	$(LWIPDIR)/core/timeouts.o \
+	$(LWIPDIR)/core/udp.o
+
+# IPv4
+obj-$(CONFIG_NET) += $(LWIPDIR)/core/ipv4/acd.o \
+        $(LWIPDIR)/core/ipv4/autoip.o \
+        $(LWIPDIR)/core/ipv4/dhcp.o \
+        $(LWIPDIR)/core/ipv4/etharp.o \
+        $(LWIPDIR)/core/ipv4/icmp.o \
+        $(LWIPDIR)/core/ipv4/igmp.o \
+        $(LWIPDIR)/core/ipv4/ip4_frag.o \
+        $(LWIPDIR)/core/ipv4/ip4.o \
+        $(LWIPDIR)/core/ipv4/ip4_addr.o
+# IPv6
+obj-$(CONFIG_NET) += $(LWIPDIR)/core/ipv6/dhcp6.o \
+        $(LWIPDIR)/core/ipv6/ethip6.o \
+        $(LWIPDIR)/core/ipv6/icmp6.o \
+        $(LWIPDIR)/core/ipv6/inet6.o \
+        $(LWIPDIR)/core/ipv6/ip6.o \
+        $(LWIPDIR)/core/ipv6/ip6_addr.o \
+        $(LWIPDIR)/core/ipv6/ip6_frag.o \
+        $(LWIPDIR)/core/ipv6/mld6.o \
+        $(LWIPDIR)/core/ipv6/nd6.o
+# API
+obj-$(CONFIG_NET) += $(LWIPDIR)/api/api_lib.o \
+	$(LWIPDIR)/api/api_msg.o \
+	$(LWIPDIR)/api/err.o \
+	$(LWIPDIR)/api/if_api.o \
+	$(LWIPDIR)/api/netbuf.o \
+	$(LWIPDIR)/api/netdb.o \
+	$(LWIPDIR)/api/netifapi.o \
+	$(LWIPDIR)/api/sockets.o \
+	$(LWIPDIR)/api/tcpip.o
+
+# Netdevs
+obj-$(CONFIG_NET) += $(LWIPDIR)/netif/ethernet.o
+
+obj-$(CONFIG_NET) += port/if.o
+obj-$(CONFIG_NET) += port/sys-arch.o
diff --git a/net/net.c b/net/net.c
index 43abbac7c3..7f868ee516 100644
--- a/net/net.c
+++ b/net/net.c
@@ -125,6 +125,7 @@
 #endif
 #include "dhcpv6.h"
 #include "net_rand.h"
+#include <net/ulwip.h>
 
 /** BOOTP EXTENTIONS **/
 
@@ -452,7 +453,11 @@ int net_loop(enum proto_t protocol)
 #endif
 
 	bootstage_mark_name(BOOTSTAGE_ID_ETH_START, "eth_start");
+#if defined(CONFIG_LWIP)
+	if (!ulwip_enabled() || !ulwip_in_loop())
+#endif
 	net_init();
+
 	if (eth_is_on_demand_init()) {
 		eth_halt();
 		eth_set_current();
@@ -649,6 +654,16 @@ restart:
 		 */
 		eth_rx();
 
+		if (IS_ENABLED(CONFIG_LWIP) && ulwip_enabled()) {
+			net_set_state(NETLOOP_CONTINUE);
+			if (!ulwip_in_loop()) {
+				if (ulwip_app_get_err())
+					net_set_state(NETLOOP_FAIL);
+				else
+					net_set_state(NETLOOP_SUCCESS);
+				goto done;
+			}
+		}
 		/*
 		 *	Abort if ctrl-c was pressed.
 		 */
@@ -1213,6 +1228,11 @@ void net_process_received_packet(uchar *in_packet, int len)
 	if (len < ETHER_HDR_SIZE)
 		return;
 
+	if (IS_ENABLED(CONFIG_LWIP) && ulwip_enabled()) {
+		ulwip_poll();
+		return;
+	}
+
 #if defined(CONFIG_API) || defined(CONFIG_EFI_LOADER)
 	if (push_packet) {
 		(*push_packet)(in_packet, len);
