diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index 23dbeaf..aea0bb5 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -169,7 +169,7 @@ protected function setExternal() {
     $this->path = $this->routeName;
 
     // Set empty route name and parameters.
-    $this->routeName = '';
+    $this->routeName = NULL;
     $this->routeParameters = array();
 
     return $this;
@@ -322,9 +322,9 @@ public function toString() {
    */
   public function toArray() {
     return array(
-      'route_name' => $this->getRouteName(),
-      'route_parameters' => $this->getRouteParameters(),
-      'options' => $this->getOptions(),
+      'route_name' => $this->routeName,
+      'route_parameters' => $this->routeParameters,
+      'options' => $this->options,
     );
   }
 
@@ -335,11 +335,19 @@ public function toArray() {
    *   An associative array suitable for a render array.
    */
   public function toRenderArray() {
-    return array(
-      '#route_name' => $this->getRouteName(),
-      '#route_parameters' => $this->getRouteParameters(),
-      '#options' => $this->getOptions(),
-    );
+    if ($this->external) {
+      return array(
+        '#href' => $this->path,
+        '#options' => $this->options,
+      );
+    }
+    else {
+      return array(
+        '#route_name' => $this->routeName,
+        '#route_parameters' => $this->routeParameters,
+        '#options' => $this->options,
+      );
+    }
   }
 
   /**
diff --git a/core/modules/link/lib/Drupal/link/LinkItemInterface.php b/core/modules/link/lib/Drupal/link/LinkItemInterface.php
new file mode 100644
index 0000000..bd71b41
--- /dev/null
+++ b/core/modules/link/lib/Drupal/link/LinkItemInterface.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * Contains \Drupal\link\LinkItemInterface.
+ */
+
+namespace Drupal\link;
+
+use Drupal\Core\Field\ConfigFieldItemInterface;
+
+/**
+ * Interface for the link field item.
+ */
+interface LinkItemInterface extends ConfigFieldItemInterface {
+
+  /**
+   * Determines if a link is external.
+   *
+   * @return bool
+   *   TRUE if the link is external, FALSE otherwise.
+   */
+  public function isExternal();
+
+}
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
index 111ce34..d29bfbd 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -7,10 +7,10 @@
 
 namespace Drupal\link\Plugin\Field\FieldFormatter;
 
-use Drupal\Component\Utility\Url;
+use Drupal\Core\Url;
 use Drupal\Core\Field\FieldItemListInterface;
-use Drupal\Core\Field\FieldItemInterface;
 use Drupal\Core\Field\FormatterBase;
+use Drupal\link\LinkItemInterface;
 
 /**
  * Plugin implementation of the 'link' formatter.
@@ -141,13 +141,10 @@ public function viewElements(FieldItemListInterface $items) {
         );
       }
       else {
-        $link = $this->buildLink($item);
         $element[$delta] = array(
           '#type' => 'link',
           '#title' => $link_title,
-          '#href' => $link['path'],
-          '#options' => $link['options'],
-        );
+        ) + $this->buildUrl($item)->toRenderArray();
       }
     }
 
@@ -155,41 +152,36 @@ public function viewElements(FieldItemListInterface $items) {
   }
 
   /**
-   * Builds the link information for a link field item.
+   * Builds the \Drupal\Core\Url object for a link field item.
    *
-   * @param \Drupal\Core\Field\FieldItemInterface $item
+   * @param \Drupal\link\LinkItemInterface $item
    *   The link field item being rendered.
    *
-   * @return array
-   *   An array with the following key/value pairs:
-   *   - 'path': a string suitable for the $path parameter in l().
-   *   - 'options': an array suitable for the $options parameter in l().
+   * @return \Drupal\Core\Url
+   *   An Url object.
    */
-  protected function buildLink(FieldItemInterface $item) {
+  protected function buildUrl(LinkItemInterface $item) {
     $settings = $this->getSettings();
-
-    // Split out the link into the parts required for url(): path and options.
-    $parsed_url = Url::parse($item->url);
-    $result = array(
-      'path' => $parsed_url['path'],
-      'options' => array(
-        'query' => $parsed_url['query'],
-        'fragment' => $parsed_url['fragment'],
-        'attributes' => $item->attributes,
-      ),
-    );
+    $options = $item->options;
 
     // Add optional 'rel' attribute to link options.
     if (!empty($settings['rel'])) {
-      $result['options']['attributes']['rel'] = $settings['rel'];
+      $options['attributes']['rel'] = $settings['rel'];
     }
     // Add optional 'target' attribute to link options.
     if (!empty($settings['target'])) {
-      $result['options']['attributes']['target'] = $settings['target'];
+      $options['attributes']['target'] = $settings['target'];
     }
 
-    return $result;
+    if ($item->isExternal()) {
+      $url = Url::createFromPath($item->url);
+      $url->setOptions($options);
+    }
+    else {
+      $url = new Url($item->route_name, $item->route_paramaters, $options);
+    }
+
+    return $url;
   }
 
 }
-
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php
index 4392123..f3417a8 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldFormatter/LinkSeparateFormatter.php
@@ -64,13 +64,12 @@ public function viewElements(FieldItemListInterface $items) {
         $url_title = truncate_utf8($item->url, $settings['trim_length'], FALSE, TRUE);
       }
 
-      $link = $this->buildLink($item);
+
       $element[$delta] = array(
         '#theme' => 'link_formatter_link_separate',
         '#title' => $link_title,
         '#url_title' => $url_title,
-        '#href' => $link['path'],
-        '#options' => $link['options'],
+        '#url' => $this->buildUrl($item),
       );
     }
     return $element;
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
index bc28594..b9e9aad 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldType/LinkItem.php
@@ -7,9 +7,11 @@
 
 namespace Drupal\link\Plugin\Field\FieldType;
 
+use Drupal\Component\Utility\Url as UrlHelper;
 use Drupal\Core\Field\ConfigFieldItemBase;
 use Drupal\Core\TypedData\DataDefinition;
 use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\link\LinkItemInterface;
 
 /**
  * Plugin implementation of the 'link' field type.
@@ -17,15 +19,31 @@
  * @FieldType(
  *   id = "link",
  *   label = @Translation("Link"),
- *   description = @Translation("Stores a URL string, optional varchar link text, and optional blob of attributes to assemble a link."),
+ *   description = @Translation("Stores a URL string, optional varchar link text, and optional blob of options to assemble a link."),
  *   instance_settings = {
+ *     "url_type" = "2",
  *     "title" = "1"
  *   },
  *   default_widget = "link_default",
  *   default_formatter = "link"
  * )
  */
-class LinkItem extends ConfigFieldItemBase {
+class LinkItem extends ConfigFieldItemBase implements LinkItemInterface {
+
+  /**
+   * Specifies whether the field supports only internal URLs.
+   */
+  const LINK_INTERNAL = 0;
+
+  /**
+   * Specifies whether the field supports only external URLs.
+   */
+  const LINK_EXTERNAL = 1;
+
+  /**
+   * Specifies whether the field supports both internal and external URLs.
+   */
+  const LINK_GENERIC = 2;
 
   /**
    * Definitions of the contained properties.
@@ -39,13 +57,19 @@ class LinkItem extends ConfigFieldItemBase {
    */
   public function getPropertyDefinitions() {
     if (!isset(static::$propertyDefinitions)) {
-      static::$propertyDefinitions['url'] = DataDefinition::create('uri')
+      static::$propertyDefinitions['url'] = DataDefinition::create('string')
         ->setLabel(t('URL'));
 
       static::$propertyDefinitions['title'] = DataDefinition::create('string')
         ->setLabel(t('Link text'));
 
-      static::$propertyDefinitions['attributes'] = DataDefinition::create('map')
+      static::$propertyDefinitions['route_name'] = DataDefinition::create('string')
+        ->setLabel(t('Route name'));
+
+      static::$propertyDefinitions['route_parameters'] = DataDefinition::create('map')
+        ->setLabel(t('Route parameters'));
+
+      static::$propertyDefinitions['options'] = DataDefinition::create('map')
         ->setLabel(t('Attributes'));
     }
     return static::$propertyDefinitions;
@@ -69,8 +93,21 @@ public static function schema(FieldDefinitionInterface $field_definition) {
           'length' => 255,
           'not null' => FALSE,
         ),
-        'attributes' => array(
-          'description' => 'Serialized array of attributes for the link.',
+        'route_name' => array(
+          'description' => 'The machine name of a defined Route this link represents.',
+          'type' => 'varchar',
+          'length' => 255,
+          'not null' => FALSE,
+        ),
+        'route_parameters' => array(
+          'description' => 'Serialized array of route parameters of the link.',
+          'type' => 'blob',
+          'size' => 'big',
+          'not null' => FALSE,
+          'serialize' => TRUE,
+        ),
+        'options' => array(
+          'description' => 'Serialized array of options for the link.',
           'type' => 'blob',
           'size' => 'big',
           'not null' => FALSE,
@@ -86,6 +123,17 @@ public static function schema(FieldDefinitionInterface $field_definition) {
   public function instanceSettingsForm(array $form, array &$form_state) {
     $element = array();
 
+    $element['url_type'] = array(
+      '#type' => 'radios',
+      '#title' => t('Allowed url type'),
+      '#default_value' => $this->getSetting('url_type'),
+      '#options' => array(
+        static::LINK_INTERNAL => t('Internal urls only'),
+        static::LINK_EXTERNAL => t('External urls only'),
+        static::LINK_GENERIC => t('Both internal and external urls'),
+      ),
+    );
+
     $element['title'] = array(
       '#type' => 'radios',
       '#title' => t('Allow link text'),
@@ -107,6 +155,13 @@ public function preSave() {
     // Trim any spaces around the URL and link text.
     $this->url = trim($this->url);
     $this->title = trim($this->title);
+
+    // Split out the link 'query' and 'fragment' parts.
+    $parsed_url = UrlHelper::parse($this->url);
+    $this->options = array(
+      'query' => $parsed_url['query'],
+      'fragment' => $parsed_url['fragment'],
+    ) + $this->options;
   }
 
   /**
@@ -117,4 +172,12 @@ public function isEmpty() {
     return $value === NULL || $value === '';
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isExternal() {
+    // External URLs don't have a route_name value.
+    return empty($this->route_name);
+  }
+
 }
diff --git a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php
index dc10574..bf80a66 100644
--- a/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/lib/Drupal/link/Plugin/Field/FieldWidget/LinkWidget.php
@@ -7,8 +7,11 @@
 
 namespace Drupal\link\Plugin\Field\FieldWidget;
 
+use Drupal\Component\Utility\Url as UrlHelper;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
+use Drupal\Core\Url;
+use Drupal\link\Plugin\Field\FieldType\LinkItem;
 
 /**
  * Plugin implementation of the 'link' widget.
@@ -31,6 +34,8 @@ class LinkWidget extends WidgetBase {
    * {@inheritdoc}
    */
   public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, array &$form_state) {
+    $url_type = $this->getFieldSetting('url_type');
+
     $element['url'] = array(
       '#type' => 'url',
       '#title' => t('URL'),
@@ -39,6 +44,26 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
       '#maxlength' => 2048,
       '#required' => $element['#required'],
     );
+
+    // If the field is configured to allow only internal or both internal and
+    // external paths, it cannot use the 'url' form element and we have to do
+    // the validation ourselves.
+    if ($url_type != LinkItem::LINK_EXTERNAL) {
+      $element['url']['#type'] = 'textfield';
+      $element['#element_validate'][] = array($this, 'validateUrl');
+    }
+
+    // If the field is configured to allow only internal paths, add a useful
+    // element prefix.
+    if ($url_type == LinkItem::LINK_INTERNAL) {
+      $element['url']['#field_prefix'] = \Drupal::url('<front>', array(), array('absolute' => TRUE));
+    }
+    // If the field is configured to allow both internal and external paths,
+    // show a useful description
+    elseif ($url_type == LinkItem::LINK_GENERIC) {
+      $element['url']['#description'] = t('This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org'));
+    }
+
     $element['title'] = array(
       '#type' => 'textfield',
       '#title' => t('Link text'),
@@ -52,7 +77,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     // settings cannot be saved otherwise.
     $is_field_edit_form = ($element['#entity'] === NULL);
     if (!$is_field_edit_form && $this->getFieldSetting('title') == DRUPAL_REQUIRED) {
-      $element['#element_validate'] = array(array($this, 'validateTitle'));
+      $element['#element_validate'][] = array($this, 'validateTitle');
     }
 
     // Exposing the attributes array in the widget is left for alternate and more
@@ -60,7 +85,7 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     $element['attributes'] = array(
       '#type' => 'value',
       '#tree' => TRUE,
-      '#value' => !empty($items[$delta]->attributes) ? $items[$delta]->attributes : array(),
+      '#value' => !empty($items[$delta]->options['attributes']) ? $items[$delta]->options['attributes'] : array(),
       '#attributes' => array('class' => array('link-field-widget-attributes')),
     );
 
@@ -125,17 +150,58 @@ public function settingsSummary() {
     return $summary;
   }
 
-
   /**
-   * Form element validation handler for link_field_widget_form().
+   * Form element validation handler; Validates the title property.
    *
    * Conditionally requires the link title if a URL value was filled in.
    */
-  function validateTitle(&$element, &$form_state, $form) {
+  public function validateTitle(&$element, &$form_state, $form) {
     if ($element['url']['#value'] !== '' && $element['title']['#value'] === '') {
       $element['title']['#required'] = TRUE;
       form_error($element['title'], $form_state, t('!name field is required.', array('!name' => $element['title']['#title'])));
     }
   }
-}
 
+  /**
+   * Form element validation handler; Validates the url property.
+   */
+  public function validateUrl(&$element, &$form_state, $form) {
+    $url_type = $this->getFieldSetting('url_type');
+
+    // Validate only if the field type supports external and/or internal URLs.
+    if ($url_type != LinkItem::LINK_EXTERNAL) {
+      try {
+        $url = Url::createFromPath($element['url']['#value']);
+
+        if ($url->isExternal() && !UrlHelper::isValid($element['url']['#value'])) {
+          form_error($element['url'], $form_state, t('%url is not a valid external url.', array('%url' => $element['url']['#value'])));
+        }
+      }
+      catch (\Exception $e) {
+        form_error($element['url'], $form_state, t('%url is not a valid internal url.', array('%url' => $element['url']['#value'])));
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, array &$form_state) {
+    foreach ($values as &$value) {
+      if (!empty($value['url'])) {
+        try {
+          $url = Url::createFromPath($value['url']);
+          $url->setOption('attributes', $value['attributes']);
+
+          $value += $url->toArray();
+        }
+        catch (\Exception $e) {
+          // Nothing to do here, validateUrl() already emitted a form validation
+          // error.
+        }
+      }
+    }
+    return $values;
+  }
+
+}
diff --git a/core/modules/link/link.module b/core/modules/link/link.module
index 24c6286..4dee62a 100644
--- a/core/modules/link/link.module
+++ b/core/modules/link/link.module
@@ -5,6 +5,8 @@
  * Defines simple link field types.
  */
 
+use Drupal\Component\Utility\String;
+
 /**
  * Implements hook_help().
  */
@@ -13,7 +15,7 @@ function link_help($path, $arg) {
     case 'admin/help#link':
       $output = '';
       $output .= '<h3>' . t('About') . '</h3>';
-      $output .= '<p>' . t('The Link module allows you to create fields that contain external URLs and optional link text. See the <a href="!field">Field module help</a> and the <a href="!field_ui">Field UI help</a> pages for general information on fields and how to create and manage them. For more information, see the <a href="!link_documentation">online documentation for the Link module</a>.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!link_documentation' => 'https://drupal.org/documentation/modules/link')) . '</p>';
+      $output .= '<p>' . t('The Link module allows you to create fields that contain internal or external URLs and optional link text. See the <a href="!field">Field module help</a> and the <a href="!field_ui">Field UI help</a> pages for general information on fields and how to create and manage them. For more information, see the <a href="!link_documentation">online documentation for the Link module</a>.', array('!field' => \Drupal::url('help.page', array('name' => 'field')), '!field_ui' => \Drupal::url('help.page', array('name' => 'field_ui')), '!link_documentation' => 'https://drupal.org/documentation/modules/link')) . '</p>';
       $output .= '<h3>' . t('Uses') . '</h3>';
       $output .= '<dl>';
       $output .= '<dt>' . t('Managing and displaying link fields') . '</dt>';
@@ -25,7 +27,7 @@ function link_help($path, $arg) {
       $output .= '<dt>' . t('Adding attributes to links') . '</dt>';
       $output .= '<dd>' . t('You can add attributes to links, by changing the <em>Format settings</em> in the <em>Manage display</em> page. Adding <em>rel="nofollow"</em> notifies search engines that links should not be followed.') . '</dd>';
       $output .= '<dt>' . t('Validating URLs') . '</dt>';
-      $output .= '<dd>' . t('All links are validated after a link field is filled in. They can include anchors or query strings. Links need to start with either the scheme name <em>http</em> or <em>https</em>; other scheme names (for example ftp or git) are not supported.') . '</dd>';
+      $output .= '<dd>' . t('All links are validated after a link field is filled in. They can include anchors or query strings.') . '</dd>';
       $output .= '</dl>';
     return $output;
   }
@@ -37,7 +39,7 @@ function link_help($path, $arg) {
 function link_theme() {
   return array(
     'link_formatter_link_separate' => array(
-      'variables' => array('title' => NULL, 'url_title' => NULL, 'href' => NULL, 'options' => array()),
+      'variables' => array('title' => NULL, 'url_title' => NULL, 'url' => NULL),
       'template' => 'link-formatter-link-separate',
     ),
   );
@@ -55,12 +57,17 @@ function link_theme() {
  *   - title: (optional) A descriptive or alternate title for the link, which
  *     may be different than the actual link text.
  *   - url_title: The anchor text for the link.
- *   - href: The link URL.
- *   - options: (optional) An array of options to pass to l().
+ *   - url: A \Drupal\Core\Url object.
  */
 function template_preprocess_link_formatter_link_separate(&$variables) {
   if (!empty($variables['title'])) {
-    $variables['title'] = check_plain($variables['title']);
+    $variables['title'] = String::checkPlain($variables['title']);
+  }
+
+  if (!$variables['url']->isExternal()) {
+    $variables['link'] = \Drupal::linkGenerator()->generateFromUrl($variables['url_title'], $variables['url']);
+  }
+  else {
+    $variables['link'] = l($variables['url_title'], $variables['url']->toString(), $variables['url']->getOptions());
   }
-  $variables['link'] = l($variables['url_title'], $variables['href'], $variables['options']);
 }
