diff --git a/net/ip.go b/net/ip.go
index 616658ca14977195a4d3b81a3db73f64d61f3399..44b84d192402d5e309ea9e394ee5237664cecae2 100644
--- a/net/ip.go
+++ b/net/ip.go
@@ -62,6 +62,21 @@ func IPFromBytes(b []byte) (IP, error) {
 	return IP{}, fmt.Errorf("byte slice has an invalid legth. Expected either 4 (IPv4) or 16 (IPv6) bytes but got: %d", len(b))
 }
 
+// IPFromString returns an IP address for a given string
+func IPFromString(str string) (IP, error) {
+	ip := net.ParseIP(str)
+	if ip == nil {
+		return IP{}, fmt.Errorf("%s is not a valid IP address", str)
+	}
+
+	ip4 := ip.To4()
+	if ip4 != nil {
+		return IPFromBytes(ip4)
+	}
+
+	return IPFromBytes(ip.To16())
+}
+
 // Equal returns true if ip is equal to other
 func (ip IP) Equal(other IP) bool {
 	return ip == other
diff --git a/net/ip_test.go b/net/ip_test.go
index 4720985f825ba242b1c78cf75773499d55bdd9bf..961730c01844a3c13c569de97eb7caed0be0567e 100644
--- a/net/ip_test.go
+++ b/net/ip_test.go
@@ -370,3 +370,46 @@ func TestBitAtPosition(t *testing.T) {
 		}
 	}
 }
+
+func TestIPFromString(t *testing.T) {
+	tests := []struct {
+		name     string
+		input    string
+		expected IP
+		wantFail bool
+	}{
+		{
+			name:     "ipv4",
+			input:    "192.168.1.234",
+			expected: IPv4FromOctets(192, 168, 1, 234),
+		},
+		{
+			name:     "ipv6",
+			input:    "2001:678:1e0::cafe",
+			expected: IPv6FromBlocks(0x2001, 0x678, 0x1e0, 0, 0, 0, 0, 0xcafe),
+		},
+		{
+			name:     "invalid",
+			input:    "foo",
+			wantFail: true,
+		},
+	}
+
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			ip, err := IPFromString(test.input)
+			if err == nil && test.wantFail {
+				t.Fatal("expected error but got nil")
+			}
+			if err != nil {
+				if test.wantFail {
+					return
+				}
+
+				t.Fatal(err)
+			}
+
+			assert.Equal(t, test.expected, ip)
+		})
+	}
+}