diff -dur pfSense-radvd-orig/etc/inc/services.inc pfSense-radvd-patch/etc/inc/services.inc
--- pfSense-radvd-orig/etc/inc/services.inc	2019-11-14 17:18:35.495547197 +0100
+++ pfSense-radvd-patch/etc/inc/services.inc	2019-11-14 17:22:48.548598176 +0100
@@ -156,95 +156,122 @@
 				$radvdconf .= "\tAdvOtherConfigFlag on;\n";
 				break;
 		}
-		$radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
-		if ($racarpif == true) {
-			$radvdconf .= "\t\tDeprecatePrefix off;\n";
-		} else {
-			$radvdconf .= "\t\tDeprecatePrefix on;\n";
-		}
-		switch ($dhcpv6ifconf['ramode']) {
-			case "managed":
-				$radvdconf .= "\t\tAdvOnLink on;\n";
-				$radvdconf .= "\t\tAdvAutonomous off;\n";
-				$radvdconf .= "\t\tAdvRouterAddr on;\n";
-				break;
-			case "router":
-				$radvdconf .= "\t\tAdvOnLink off;\n";
-				$radvdconf .= "\t\tAdvAutonomous off;\n";
-				$radvdconf .= "\t\tAdvRouterAddr on;\n";
-				break;
-			case "stateless_dhcp":
-			case "assist":
-				$radvdconf .= "\t\tAdvOnLink on;\n";
-				$radvdconf .= "\t\tAdvAutonomous on;\n";
-				$radvdconf .= "\t\tAdvRouterAddr on;\n";
-				break;
-			case "unmanaged":
-				$radvdconf .= "\t\tAdvOnLink on;\n";
-				$radvdconf .= "\t\tAdvAutonomous on;\n";
-				$radvdconf .= "\t\tAdvRouterAddr on;\n";
-				break;
-		}
-
-		if (is_numericint($dhcpv6ifconf['ravalidlifetime'])) {
-		  $radvdconf .= "\t\tAdvValidLifetime {$dhcpv6ifconf['ravalidlifetime']};\n";
-		} else {
-		  $radvdconf .= "\t\tAdvValidLifetime 86400;\n";
-		}
-
-		if (is_numericint($dhcpv6ifconf['rapreferredlifetime'])) {
-		  $radvdconf .= "\t\tAdvPreferredLifetime {$dhcpv6ifconf['rapreferredlifetime']};\n";
-		} else {
-		  $radvdconf .= "\t\tAdvPreferredLifetime 14400;\n";
-		}
-
-		$radvdconf .= "\t};\n";
-
+		/* If there are subnets defined, do not generate prefix for interface subnet, but use the user-specified subnets instead */
 		if (is_array($dhcpv6ifconf['subnets']['item'])) {
-			foreach ($dhcpv6ifconf['subnets']['item'] as $subnet) {
-				if (is_subnetv6($subnet)) {
-					$radvdconf .= "\tprefix {$subnet} {\n";
+		foreach ($dhcpv6ifconf['subnets']['item'] as $dhcpv6subnet) {
+				$radvdconf .= "\tprefix {$dhcpv6subnet} {\n";
+				if ($racarpif == true) {
+					$radvdconf .= "\t\tDeprecatePrefix off;\n";
+				} else {
 					$radvdconf .= "\t\tDeprecatePrefix on;\n";
-					switch ($dhcpv6ifconf['ramode']) {
-						case "managed":
-							$radvdconf .= "\t\tAdvOnLink on;\n";
-							$radvdconf .= "\t\tAdvAutonomous off;\n";
-							$radvdconf .= "\t\tAdvRouterAddr on;\n";
-							break;
-						case "router":
-							$radvdconf .= "\t\tAdvOnLink off;\n";
-							$radvdconf .= "\t\tAdvAutonomous off;\n";
-							$radvdconf .= "\t\tAdvRouterAddr on;\n";
-							break;
-						case "assist":
-							$radvdconf .= "\t\tAdvOnLink on;\n";
-							$radvdconf .= "\t\tAdvAutonomous on;\n";
-							$radvdconf .= "\t\tAdvRouterAddr on;\n";
-							break;
-						case "unmanaged":
-							$radvdconf .= "\t\tAdvOnLink on;\n";
-							$radvdconf .= "\t\tAdvAutonomous on;\n";
-							$radvdconf .= "\t\tAdvRouterAddr on;\n";
-							break;
-					}
-					$radvdconf .= "\t};\n";
 				}
+				switch($dhcpv6ifconf['ramode']) {
+					case "managed":
+						$radvdconf .= "\t\tAdvOnLink on;\n";
+						$radvdconf .= "\t\tAdvAutonomous off;\n";
+						$radvdconf .= "\t\tAdvRouterAddr on;\n";
+						break;
+					case "router":
+						$radvdconf .= "\t\tAdvOnLink off;\n";
+						$radvdconf .= "\t\tAdvAutonomous off;\n";
+						$radvdconf .= "\t\tAdvRouterAddr on;\n";
+						break;
+					case "stateless_dhcp":
+					case "assist":
+						$radvdconf .= "\t\tAdvOnLink on;\n";
+						$radvdconf .= "\t\tAdvAutonomous on;\n";
+						$radvdconf .= "\t\tAdvRouterAddr on;\n";
+						break;
+					case "unmanaged":
+						$radvdconf .= "\t\tAdvOnLink on;\n";
+						$radvdconf .= "\t\tAdvAutonomous on;\n";
+						$radvdconf .= "\t\tAdvRouterAddr on;\n";
+						break;
+						
+				}
+				if (is_numericint($dhcpv6ifconf['ravalidlifetime'])) {
+				  $radvdconf .= "\t\tAdvValidLifetime {$dhcpv6ifconf['ravalidlifetime']};\n";
+				} else {
+				  $radvdconf .= "\t\tAdvValidLifetime 14400;\n";
+				}
+				if (is_numericint($dhcpv6ifconf['rapreferredlifetime'])) {
+				  $radvdconf .= "\t\tAdvPreferredLifetime {$dhcpv6ifconf['rapreferredlifetime']};\n";
+				} else {
+				  $radvdconf .= "\t\tAdvPreferredLifetime 14400;\n";
+				}
+				$radvdconf .= "\t};\n";
 			}
 		}
-		$radvdconf .= "\troute ::/0 {\n";
-		switch ($dhcpv6ifconf['rapriority']) {
-			case "low":
-				$radvdconf .= "\t\tAdvRoutePreference low;\n";
-				break;
-			case "high":
-				$radvdconf .= "\t\tAdvRoutePreference high;\n";
-				break;
-			default:
-				$radvdconf .= "\t\tAdvRoutePreference medium;\n";
-				break;
+
+		/* There are no user-specified subnets, so generate prefix for the interface subnet only */
+		else {
+			$radvdconf .= "\tprefix {$subnetv6}/{$ifcfgsnv6} {\n";
+			if ($racarpif == true) {
+				$radvdconf .= "\t\tDeprecatePrefix off;\n";
+			} else {
+				$radvdconf .= "\t\tDeprecatePrefix on;\n";
+			}
+			switch ($dhcpv6ifconf['ramode']) {
+				case "managed":
+					$radvdconf .= "\t\tAdvOnLink on;\n";
+					$radvdconf .= "\t\tAdvAutonomous off;\n";
+					$radvdconf .= "\t\tAdvRouterAddr on;\n";
+					break;
+				case "router":
+					$radvdconf .= "\t\tAdvOnLink off;\n";
+					$radvdconf .= "\t\tAdvAutonomous off;\n";
+					$radvdconf .= "\t\tAdvRouterAddr on;\n";
+					break;
+				case "stateless_dhcp":
+				case "assist":
+					$radvdconf .= "\t\tAdvOnLink on;\n";
+					$radvdconf .= "\t\tAdvAutonomous on;\n";
+					$radvdconf .= "\t\tAdvRouterAddr on;\n";
+					break;
+				case "unmanaged":
+					$radvdconf .= "\t\tAdvOnLink on;\n";
+					$radvdconf .= "\t\tAdvAutonomous on;\n";
+					$radvdconf .= "\t\tAdvRouterAddr on;\n";
+					break;
+			}
+			if (is_numericint($dhcpv6ifconf['ravalidlifetime'])) {
+			  $radvdconf .= "\t\tAdvValidLifetime {$dhcpv6ifconf['ravalidlifetime']};\n";
+			} else {
+			  $radvdconf .= "\t\tAdvValidLifetime 86400;\n";
+			}
+			if (is_numericint($dhcpv6ifconf['rapreferredlifetime'])) {
+			  $radvdconf .= "\t\tAdvPreferredLifetime {$dhcpv6ifconf['rapreferredlifetime']};\n";
+			} else {
+			  $radvdconf .= "\t\tAdvPreferredLifetime 14400;\n";
+			}
+			$radvdconf .= "\t};\n";
+		}
+		/* If there are routes defined, do not generate a default route, but use the user-specified routes instead */
+		if (is_array($dhcpv6ifconf['routes']['item'])) {
+			foreach ($dhcpv6ifconf['routes']['item'] as $dhcpv6route) {
+				$radvdconf .= "\troute " . $dhcpv6route['destination'] . " {\n";
+				$radvdconf .= "\t\tRemoveRoute on;\n";
+				$radvdconf .= "\t\tAdvRoutePreference " . $dhcpv6route['priority'] . ";\n";
+				$radvdconf .= "\t};\n";
+			}
+		}
+		/* There are no user-specified routes, so generate a default route entry */
+		else {
+			$radvdconf .= "\troute ::/0 {\n";
+			switch ($dhcpv6ifconf['rapriority']) {
+				case "low":
+					$radvdconf .= "\t\tAdvRoutePreference low;\n";
+					break;
+				case "high":
+					$radvdconf .= "\t\tAdvRoutePreference high;\n";
+					break;
+				default:
+					$radvdconf .= "\t\tAdvRoutePreference medium;\n";
+					break;
+			}
+			$radvdconf .= "\t\tRemoveRoute on;\n";
+			$radvdconf .= "\t};\n";
 		}
-		$radvdconf .= "\t\tRemoveRoute on;\n";
-		$radvdconf .= "\t};\n";
 
 		/* add DNS servers */
 		$dnslist = array();
diff -dur pfSense-radvd-orig/usr/local/www/js/pfSenseHelpers.js pfSense-radvd-patch/usr/local/www/js/pfSenseHelpers.js
--- pfSense-radvd-orig/usr/local/www/js/pfSenseHelpers.js	2019-11-14 17:18:35.495547197 +0100
+++ pfSense-radvd-patch/usr/local/www/js/pfSenseHelpers.js	2019-11-14 17:42:09.320492103 +0100
@@ -254,10 +254,11 @@
 
 // Called after a delete so that there are no gaps in the numbering. Most of the time the config system doesn't care about
 // gaps, but I do :)
-function renumber() {
+function renumber(element_id) {
 	var idx = 0;
+	var groupName = getRowGroupName(element_id);
 
-	$('.repeatable').each(function() {
+	$('.repeatable' + groupName).each(function() {
 
 		$(this).find('input').each(function() {
 			$(this).prop("id", this.id.replace(/\d+$/, "") + idx);
@@ -280,11 +281,23 @@
 	});
 }
 
+// Return the repeatable group number, if it is defined (=added as number to the end of the control's id, e.g. "addrow2")
+function getRowGroupName(element_id) {
+	var groupName = ''; // Always make sure we du return an empty string, so we can use it in a concatenation without doing additional if checks
+	var regexString = /^[a-zA-Z]+(_[a-zA-Z]+)[0-9]*$/; // match any string that begins with one or more characters, has a group name after a '_' and ends with nothing or numbers
+	var regexResult = regexString.exec(element_id);
+	if (regexResult != null) { // the capture inside the parentheses has succeeded, so we have successfully captured a group name
+		groupName = regexResult[1]; // only then, assign the second value to groupName
+	}
+	return groupName;
+}
+
 function delete_row(rowDelBtn) {
 	var rowLabel;
+	var groupName = getRowGroupName(rowDelBtn);
 
 	// If we are deleting row zero, we need to save/restore the label
-	if ( (rowDelBtn == "deleterow0") && ((typeof retainhelp) == "undefined")) {
+	if ((rowDelBtn == "deleterow" + groupName + "0") && ((typeof retainhelp) == "undefined")) {
 		rowLabel = $('#' + rowDelBtn).parent('div').parent('div').find('label:first').text();
 	}
 
@@ -292,17 +305,20 @@
 
 	renumber();
 	checkLastRow();
+	renumber(rowDelBtn);
+	checkLastRow(rowDelBtn);
 
-	if (rowDelBtn == "deleterow0") {
+	if (rowDelBtn == "deleterow" + groupName + "0") {
 		$('#' + rowDelBtn).parent('div').parent('div').find('label:first').text(rowLabel);
 	}
 }
 
-function checkLastRow() {
-	if (($('.repeatable').length <= 1) && (! $('#deleterow0').hasClass("nowarn"))) {
-		$('#deleterow0').hide();
+function checkLastRow(element_id) {
+	var groupName = getRowGroupName(element_id);
+	if($('.repeatable' + groupName).length <= 1 && (! $('#deleterow' + groupName + '0').hasClass("nowarn"))) {
+		$('#deleterow' + groupName + '0').hide();
 	} else {
-		$('[id^=deleterow]').show();
+		$('[id^=deleterow' + groupName + ']').show();
 	}
 }
 
@@ -337,9 +353,11 @@
 		}
 	});
 }
-function add_row() {
+function add_row(rowAddBtn) {
+	var groupName = getRowGroupName(rowAddBtn);
+	
 	// Find the last repeatable group
-	var lastRepeatableGroup = $('.repeatable:last');
+	var lastRepeatableGroup = $('.repeatable' + groupName + ':last');
 
 	// If the number of repeats exceeds the maximum, do not add another clone
 	if ($('.repeatable').length >= lastRepeatableGroup.attr('max_repeats')) {
@@ -356,6 +374,11 @@
 	// Increment the suffix number for each input element in the new group
 	bump_input_id(newGroup);
 
+	// Somehow delete button IDs did not increment, so increment the suffix number for delete buttons also
+	$(newGroup).find('button').each(function() {
+		$(this).prop("id", bumpStringInt(this.id));
+		$(this).prop("name", bumpStringInt(this.name));
+	});
 	// And for "for" tags
 //	$(newGroup).find('label').attr('for', bumpStringInt($(newGroup).find('label').attr('for')));
 
@@ -372,7 +395,7 @@
 
 	setMasks();
 
-	checkLastRow();
+	checkLastRow(rowAddBtn);
 
 	// Autocomplete
 	if ( typeof addressarray !== 'undefined') {
@@ -388,12 +411,13 @@
 	// Now that we are no longer cloning the event handlers, we need to remove and re-add after a new row
 	// has been added to the table
 	$('[id^=delete]').unbind();
-	$('[id^=delete]').click(function(event) {
-		if ($('.repeatable').length > 1) {
+	$('[id^=delete]').click(function() {
+		var groupName = getRowGroupName(this.id);
+		if($('.repeatable' + groupName).length > 1) {
 			if ((typeof retainhelp) == "undefined")
-				moveHelpText($(this).attr("id"));
+				moveHelpText(this.id);
 
-			delete_row($(this).attr("id"));
+			delete_row(this.id);
 		} else if ($(this).hasClass("nowarn")) {
 			clearRow0();
 		} else {
@@ -408,11 +432,12 @@
 
 // on click . .
 $('[id^=addrow]').click(function() {
-	add_row();
+	add_row(this.id);
 });
 
-$('[id^=delete]').click(function(event) {
-	if ($('.repeatable').length > 1) {
+$('[id^=delete]').click(function() {
+	var groupName = getRowGroupName(this.id);
+	if($('.repeatable' + groupName).length > 1) {
 		if ((typeof retainhelp) == "undefined")
 			moveHelpText($(this).attr("id"));
 
diff -dur pfSense-radvd-orig/usr/local/www/services_router_advertisements.php pfSense-radvd-patch/usr/local/www/services_router_advertisements.php
--- pfSense-radvd-orig/usr/local/www/services_router_advertisements.php	2019-11-14 17:18:35.495547197 +0100
+++ pfSense-radvd-patch/usr/local/www/services_router_advertisements.php	2019-11-14 17:18:53.719623488 +0100
@@ -85,10 +85,14 @@
 	$pconfig['rasamednsasdhcp6'] = isset($config['dhcpdv6'][$if]['rasamednsasdhcp6']);
 
 	$pconfig['subnets'] = $config['dhcpdv6'][$if]['subnets']['item'];
+	$pconfig['routes'] = $config['dhcpdv6'][$if]['routes']['item'];
 }
 if (!is_array($pconfig['subnets'])) {
 	$pconfig['subnets'] = array();
 }
+if (!is_array($pconfig['routes'])) {
+	$pconfig['routes'] = array();
+}
 
 $advertise_modes = array(
 	"disabled" => 	gettext("Disabled"),
@@ -108,6 +112,11 @@
 		"/128 specifies a single IPv6 host; /64 specifies a normal IPv6 network; etc.  " .
 		"If no subnets are specified here, the Router Advertisement (RA) Daemon will advertise to the subnet to which the router's interface is assigned.") .
 	'</span>';
+$routes_help = '<span class="help-block">' .
+	gettext("Routes are specified in CIDR format. " .
+			"Enter subnets to which routes should be advertised, and their priority. " .
+			"If you need a default route, specify it with ::/0 here.  " .
+			"If no routes are specified here, the Router Advertisement (RA) Daemon will advertise only the default route.");
 
 // THe use of <div class="infoblock"> here causes the text to be hidden until the user clicks the "info" icon
 $ramode_help = gettext('Select the Operating Mode for the Router Advertisement (RA) Daemon.') .
@@ -151,6 +160,29 @@
 			}
 		}
 	}
+	$pconfig['routes'] = array();
+	for ($x = 0; $x < 5000; $x += 1) {
+		$address = trim($_POST['route_address' . $x]);
+		if ($address === "") {
+			continue;
+		}
+		$bits = trim($_POST['route_bits' . $x]);
+		if ($bits === "") {
+			$bits = "128";
+		}
+		$priority = trim($_POST['route_priority' . $x]);
+		if ($priority === "0") {
+			$priority = "medium";
+		}
+		if (is_alias($address)) {
+			$pconfig['routes'][] = array('destination' => $address);
+		} else {
+			$pconfig['routes'][] = array('destination' => $address . "/" . $bits, 'priority' => $priority);
+			if (!is_ipaddrv6($address)) {
+				$input_errors[] = sprintf(gettext("An invalid route was specified. [%s/%s]"), $address, $bits);
+			}
+		}
+	}
 
 	if (($_POST['radns1'] && !is_ipaddrv6($_POST['radns1'])) || ($_POST['radns2'] && !is_ipaddrv6($_POST['radns2'])) || ($_POST['radns3'] && !is_ipaddrv6($_POST['radns3']))) {
 		$input_errors[] = gettext("A valid IPv6 address must be specified for each of the DNS servers.");
@@ -232,6 +264,11 @@
 		} else {
 			unset($config['dhcpdv6'][$if]['subnets']);
 		}
+		if (count($pconfig['routes'])) {
+			$config['dhcpdv6'][$if]['routes']['item'] = $pconfig['routes'];
+		} else {
+			unset($config['dhcpdv6'][$if]['routes']);
+		}
 
 		write_config();
 		$changes_applied = true;
@@ -430,6 +467,55 @@
 	null,
 	'fa-plus'
 ))->addClass('btn-success');
+/*-----------------------------------------------------------------------------*/
+$section->addInput(new Form_StaticText(
+	'Routes',
+	$routes_help
+));
+
+if (empty($pconfig['routes'])) {
+	$pconfig['routes'] = array('0' => '/128');
+}
+
+$route_counter = 0;
+$numrows = count($pconfig['routes']) - 1;
+$route_priority_modes = $priority_modes;
+array_unshift($route_priority_modes, '');
+foreach ($pconfig['routes'] as $route) {
+	$route_address_name = "route_address" . $route_counter;
+	$route_bits_name = "route_bits" . $route_counter;
+	$route_priority_name = "route_priority" . $route_counter;
+	list($address, $mask) = explode("/", $route['destination']);
+	$priority = $route['priority'];
+	$group = new Form_Group($route_counter == 0 ? 'Routes':'');
+	$group->add(new Form_IpAddress(
+		$route_address_name,
+		null,
+		$address
+	))->addMask($route_bits_name, $mask, 128, 0);
+	$group->add(new Form_Select(
+	$route_priority_name,
+	null,
+	$priority,
+	$route_priority_modes
+	));
+	$group->add(new Form_Button(
+		'deleterow_routes' . $route_counter,
+		'Delete',
+		null,
+		'fa-trash'
+	))->removeClass('btn-primary')->addClass('btn-warning');
+	$group->addClass('repeatable_routes');
+	$section->add($group);
+	$route_counter++;
+}
+$section->addInput(new Form_Button(
+	'addrow_routes',
+	'Add',
+	null,
+	'fa-plus'
+))->addClass('btn-success');
+/*-----------------------------------------------------------------------------*/
 
 $form->add($section);
 
@@ -474,7 +560,7 @@
 //<![CDATA[
 events.push(function() {
 	// Suppress "Delete row" button if there are fewer than two rows
-	checkLastRow();
+	checkLastRow(this.id);
 
 	// --------- Autocomplete -----------------------------------------------------------------------------------------
 	var addressarray = <?= json_encode(get_alias_list(array("host", "network", "openvpn", "urltable"))) ?>;
