{"id":308,"date":"2012-05-30T09:00:51","date_gmt":"2012-05-30T08:00:51","guid":{"rendered":"http:\/\/www.phildev.net\/phil\/blog\/?p=308"},"modified":"2015-10-31T02:08:28","modified_gmt":"2015-10-31T01:08:28","slug":"native-ipv6-on-comcast","status":"publish","type":"post","link":"https:\/\/www.phildev.net\/phil\/blog\/?p=308","title":{"rendered":"Native IPv6 On Comcast"},"content":{"rendered":"<p><strong>Update 5\/31\/12<\/strong>: Scripts are now configurable and various bugs have been fixed.<br \/>\n<strong>Update 6\/07\/12<\/strong>: Broken link to if-up script fixed, typo in script fixed.<br \/>\n<strong>Update 10\/30\/15<\/strong>: Scripts moved to <a href=\"https:\/\/github.com\/jaymzh\/v6-gw-scripts\" target=\"_blank\">GitHub<\/a>.<\/p>\n<p>They&#8217;re not advertising it, but Comcast is turning on IPv6 for all their California customers (and presumably others) who have a <a href=\"http:\/\/mydeviceinfo.comcast.net\/\" target=\"_blank\">supported modem<\/a> this week. Most people&#8217;s equipment won&#8217;t request v6 addresses yet, but for those running one of the Linksys-style devices that are set to do it by default, they will get a \/64 and start advertising internally and voila, their network is IPv6-ified.<\/p>\n<p>That&#8217;s all well and good, but you&#8217;re running a Linux firewall like a good geek, so how do you get the IPv6 goodness? It&#8217;s actually not as straight-forward as you&#8217;d think, but I have all the bits worked out here so you can get up and running.<\/p>\n<p>Lets talk about what we&#8217;re going to be doing. We will&#8230;<\/p>\n<ol>\n<li>Use DHCPv6 to request a <em>prefix<\/em> (a \/64) on the <em>external<\/em> interface.<\/li>\n<li>Take that prefix, assign one address from it to the <em>internal<\/em> interface.<\/li>\n<li>Tell radvd to advertise that prefix to our internal network.<\/li>\n<\/ol>\n<p>I have automted this whole process and included the scripts below&#8230; but we will first start by walking through it manually so you understand what&#8217;s going on.<\/p>\n<h2>Get A Prefix<\/h2>\n<p>First, lets just look at how we get a prefix! Run the following command:<\/p>\n<p><code>dhclient -6 -P -d -v $YOUR_EXTERNAL_INTERFACE<\/code><\/p>\n<p>The <em>-6<\/em> option is to run in IPv6 mode, the <\/em>-P<\/em> option says to request a prefix, and <em>-d -v<\/em> are debug and verbose so you can see what&#8217;s going on.<\/p>\n<p>In the output look at the line with <em>IAPREFIX<\/em> in it&#8230; that&#8217;s your \/64! However, dhclient won&#8217;t do anything with that. (Depending on how new your dhclient is, if you did <strong>not<\/strong> use -P, then you&#8217;d get a single address which it would assign to eth1, but since we want our whole network to have IPv6, we requested a prefix. Older dhclient&#8217;s would write out the lease file but not do anything with any addresses, even when not in prefix mode.)<\/p>\n<h2>Assign An Address To Your Box<\/h2>\n<p>So we want to do something with that. First, we need to assign some address in our \/64 to our <strong>internal<\/strong> interface. Note that all routing is done via link-local addresses so your external interface doesn&#8217;t need a global address. In theory we could do this manually, with something like:<\/p>\n<p><code>ip addr add $PREFIX::1\/64 dev $YOUR_INTERNAL_INTERFACE<\/code><\/p>\n<p>So for example, if your prefix was 2001:a:b::\/64 then the address there would be 2001:a:b::1\/64. And don&#8217;t forget to replace your <strong>internal<\/strong> interface there.<\/p>\n<h2>Accept Router Advertisements<\/h2>\n<p>Then we need to make sure we accept Router Advertisements (RA) so we know how to send traffic to the internet.<\/p>\n<p>But first we need to understand something about IPv6 as well as something about linux&#8217;s IPv6 implementation. The RFC states that if you&#8217;re a router you shouldn&#8217;t accept router advertisements (RA) &#8211; the rationale being that you&#8217;re a router or your a host, but you are not both. Thus, even if you set net.ipv6.conf.all.accept_ra=1, if you also have ip_forward=1, linux will silently drop all RAs.<\/p>\n<p>In our case, however we want to accept router advertisements on our external interface and send our own router advertisements on our internal interface. Fortunately, Linux added a new value to accept_ra which says &#8220;accept RAs even if ip_forward is on&#8221;&#8230; and that value is 2.<\/p>\n<p>Sadly, in kernels before that was added (anything before 2.6.37), a value of 2 is accepted, but it has no benefit (it&#8217;s treated the same as 1). In these older kernels you can set ip_forward=0 on the external interface and 1 on the internal interface and packets will still be forwarded and you will accept RAs. See <a href=\"http:\/\/strugglers.net\/~andy\/blog\/2011\/09\/04\/linux-ipv6-router-advertisements-and-forwarding\/\" target=\"_blank\">this blog<\/a> for more details.<\/p>\n<p>Anyway, here&#8217;s what we do to ensure we get RAs:<\/p>\n<p><code># Ensure we'll get router advertisements<br \/>\nsysctl -w net.ipv6.conf.$YOUR_EXTERNAL_INTERFACE.accept_ra=2<br \/>\n# Needed for kernels before 2.6.37, don't worry,<br \/>\n# forwarding will still work as long as you have it set<br \/>\n# on your other interface.<br \/>\nsysctl -w net.ipv6.conf.$YOUR_EXTERNAL_INTERFACE.forwarding=0<br \/>\n<\/code><\/p>\n<p>And with that you should have connectivity:<\/p>\n<p><code>ping6 www.facebook.com<\/code><\/p>\n<h2>Advertising Your Space Internally<\/h2>\n<p>IPv6 is great at autoconfiguration, but in order for devices to configure themselves, we need to advertise our IPv6 space &#8211; and our selves as a router for that space &#8211; to the network. First, install <em>radvd<\/em> as appropriate for your distribution and create a config in <em>\/etc\/radvd.conf<\/em> that looks like this:<\/p>\n<blockquote>\n<pre>\r\ninterface YOUR_INTERNAL_INTERFACE {\r\n   AdvSendAdvert on;\r\n   RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};\r\n   prefix YOUR_PREFIX\r\n   {\r\n     AdvOnLink on;\r\n     AdvAutonomous on;\r\n   };\r\n};   \r\n<\/pre>\n<\/blockquote>\n<p>And replace &#8220;YOUR_INTERNAL_INTERFACE&#8221; with your internal interface and &#8220;YOUR_PREFIX&#8221; with your prefix. Start radvd and you should see devices on your network auto-configure ipv6 addresses and routes.<\/p>\n<p>This config hands out Google DNS servers as the DNS servers, but you can change to taste.<\/p>\n<h2>Automating Everything<\/h2>\n<p>Well, it&#8217;s neat that it works, but who wants to do that by hand on every boot? Ew.<\/p>\n<p>Fortunately, dhclient is pluggable, so I wrote a dhclient-script to do that for us. Drop this into <em>\/etc\/dhcp\/dhclient-exit-hooks.d\/<\/em> and call it something like <em>ipv6<\/em>.<\/p>\n<blockquote>\n<pre>\r\n#!\/bin\/bash\r\n\r\n# vim:tw=80:tabstop=2:shiftwidth=2\r\n\r\n# Copyright (c) 2012-present, Phil Dibowitz \r\n#\r\n# All rights reserved.\r\n#\r\n# Redistribution and use in source and binary forms, with or without\r\n# modification, are permitted provided that the following conditions are met:\r\n#\r\n#  * Redistributions of source code must retain the above copyright notice,\r\n#    this list of conditions and the following disclaimer.\r\n#  * Redistributions in\r\n#    binary form must reproduce the above copyright notice, this list of\r\n#    conditions and the following disclaimer in the documentation and\/or other\r\n#    materials provided with the distribution.\r\n#  * Neither the name of the author nor the names of its contributors may be\r\n#    used to endorse or promote products derived from this software without\r\n#    specific prior written permission.\r\n#\r\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\r\n# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r\n# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r\n# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r\n# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r\n# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r\n# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r\n# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r\n# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r\n# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r\n# POSSIBILITY OF SUCH DAMAGE.\r\n\r\n#\r\n# You can find the latest version of this at:\r\n#   https:\/\/github.com\/jaymzh\/v6-gw-scripts\r\n#\r\n# Will, given a 'dhclient -6 -P ...' on $EXT_IFACE and assign the prefix\r\n# given to the $INT_IFACE, and twiddle radvd.\r\n#\r\n# For radvd, it takes \/etc\/radvd.conf.tmpl, replaces \"__PREFIX__\" with your\r\n# prefix, and - if it's different from \/etc\/radvd.conf - replaces the config\r\n# file and restarts the daemon.\r\n#\r\n\r\n# Change INT_IFACE or EXT_IFACE by setting them in the config file\r\nCONF='\/etc\/ipv6_prefix_dhclient.conf'\r\nINT_IFACE='eth0'\r\nEXT_IFACE='eth1'\r\n\r\n[ -r $CONF ] &amp;&amp; . $CONF\r\n\r\nipv6_prefix_setup() {\r\n  current_ip=$(\/sbin\/ip -6 addr show dev $INT_IFACE scope global |\\\r\n               \/usr\/bin\/awk '\/inet6\/ {print $2}')\r\n  current_prefix=$(echo $current_ip | \/bin\/sed -e 's@::1\/64@::\/64@')\r\n  if [ \"$current_prefix\" == \"$new_ip6_prefix\" ] ; then\r\n    return\r\n  fi\r\n\r\n  # Setup the new IP\r\n  new_ip=$(echo $new_ip6_prefix | \/bin\/sed -e 's@::\/64@::1\/64@g')\r\n  if [ ! -z \"$current_ip\" ] ; then\r\n    ip -6 addr del $current_ip dev $INT_IFACE\r\n  fi\r\n  ip -6 addr add $new_ip dev $INT_IFACE\r\n\r\n  # Ensure we'll get router advertisements\r\n  sysctl -w \"net.ipv6.conf.$EXT_IFACE.accept_ra=2\"\r\n  # Needed for kernels before 2.6.37, don't worry,\r\n  # forwarding will still work as long as you have it set\r\n  # on your other interface.\r\n  sysctl -w \"net.ipv6.conf.$EXT_IFACE.forwarding=0\"\r\n\r\n  # Update radvd\r\n  tmpfile=\/tmp\/radvd.conf.$$\r\n  sed -e \"s@__PREFIX__@$new_ip6_prefix@g\" \/etc\/radvd.conf.tmpl &gt; $tmpfile\r\n  diff $tmpfile \/etc\/radvd.conf &gt;\/dev\/null\r\n  if [ $? == 1 ]; then\r\n    mv $tmpfile \/etc\/radvd.conf\r\n    \/etc\/init.d\/radvd restart\r\n  else\r\n    rm $tmpfile\r\n  fi\r\n}\r\n\r\nif [ \"$interface\" != \"$EXT_IFACE\" ] ; then\r\n  return\r\nfi\r\n\r\ncase \"$reason\" in\r\n  BOUND6|REBIND6)\r\n    # We will get called twice here - once for the temp address\r\n    # and once for the prefix. We only care about the prefix.\r\n    if [ ! -z \"$new_ip6_prefix\" ] ; then\r\n      ipv6_prefix_setup\r\n    fi\r\n    ;;\r\nesac\r\n<\/pre>\n<\/blockquote>\n<p>Be sure to set the two variables at the top as appropriate.<\/p>\n<p>This script does several things things:<\/p>\n<ol>\n<li>Assign an IP from our prefix to the internal interface<\/li>\n<li>Set the sysctls as appropriate<\/li>\n<li>Configure radvd<\/li>\n<\/ol>\n<p>It makes one assumption: that you have a templated radvd.conf file in <em>\/etc\/radvd.conf.tmpl<\/em> that has &#8220;__PREFIX__&#8221; instead of a prefix, ala:<\/p>\n<blockquote>\n<pre>\r\ninterface YOUR_INTERNAL_INTERFACE {\r\n   AdvSendAdvert on;\r\n   RDNSS 2001:4860:4860::8888 2001:4860:4860::8844 {};\r\n   prefix __PREFIX__\r\n   {\r\n     AdvOnLink on;\r\n     AdvAutonomous on;\r\n   };\r\n};   \r\n<\/pre>\n<\/blockquote>\n<p>This script is configurable&#8230; just drop a file in <em>\/etc\/ipv6_prefix_dhclient.conf<\/em> and define $INT_IFACE and $EXT_IFACE like so:<\/p>\n<blockquote>\n<pre>\r\nINT_IFACE='eth0'\r\nEXT_IFACE='eth1'\r\n<\/pre>\n<\/blockquote>\n<p>As before, this config hands out Google DNS servers as the DNS servers, but you can change to taste.<\/p>\n<p>So now we have dhclient doing all of the configuration work for us, there&#8217;s only one more piece: to launch dhclient correctly. For this, I dropped a simple script which fires off dhclient into <em>\/etc\/network\/if-up.d<\/em> which I called <em>99-ipv6<\/em>. This is Debian-like-distro specific, but you could do this with some ifup-local magic on Redhat-like-distros.<\/p>\n<blockquote>\n<pre>\r\n#!\/bin\/bash\r\n\r\n# vim:tw=80:tabstop=2:shiftwidth=2\r\n\r\n# Copyright (c) 2012-present, Phil Dibowitz \r\n#\r\n# All rights reserved.\r\n#\r\n# Redistribution and use in source and binary forms, with or without\r\n# modification, are permitted provided that the following conditions are met:\r\n#\r\n#  * Redistributions of source code must retain the above copyright notice,\r\n#    this list of conditions and the following disclaimer.\r\n#  * Redistributions in\r\n#    binary form must reproduce the above copyright notice, this list of\r\n#    conditions and the following disclaimer in the documentation and\/or other\r\n#    materials provided with the distribution.\r\n#  * Neither the name of the author nor the names of its contributors may be\r\n#    used to endorse or promote products derived from this software without\r\n#    specific prior written permission.\r\n#\r\n# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\r\n# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\r\n# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\r\n# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE\r\n# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR\r\n# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF\r\n# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS\r\n# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN\r\n# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r\n# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\r\n# POSSIBILITY OF SUCH DAMAGE.\r\n\r\n#\r\n# You can find the latest version of this at:\r\n#   https:\/\/github.com\/jaymzh\/v6-gw-scripts\r\n#\r\n# Debian-style if-up.d script for firing off dhclient for ipv6\r\n#\r\n\r\n# Change EXT_IFACE by updating the config file\r\nCONF='\/etc\/ipv6_prefix_dhclient.conf'\r\nEXT_IFACE=\"eth1\"\r\n\r\n[ -r $CONF ] &amp;&amp; . $CONF\r\n\r\n# We only care about the external interface.\r\nif [ \"$IFACE\" != \"$EXT_IFACE\" ]; then\r\n\texit 0\r\nfi\r\n\r\n# Only run from ifup.\r\nif [ \"$MODE\" != start ]; then\r\n\texit 0\r\nfi\r\n\r\n# If there's a stale dhclient, kill it\r\nkill `cat \/var\/run\/dhclient6.$IFACE.pid`\r\n\r\n# Start our new one\r\ndhclient -6 -P -pf \/var\/run\/dhclient6.$IFACE.pid \\\r\n  -lf \/var\/lib\/dhcp\/dhclient6.$IFACE.leases $IFACE &amp;\r\n\r\n# reload firewall rules\r\n\/etc\/network\/ip6tables reload\r\n\r\nexit 0\r\n<\/pre>\n<\/blockquote>\n<p>This script uses the same configuration file to determine the configuration of your network.<\/p>\n<p>In my particular case I have a script \/etc\/network\/ip6tables which setups my v6 firewall rules and is run here (and a similar one for v4 which I run as an if-up rule), but your configuration may differ.<\/p>\n<p>You can find these scripts on <a href=\"https:\/\/github.com\/jaymzh\/v6-gw-scripts\" target=\"_blank\">GitHub<\/a>.<\/p>\n<h2>Other Considerations<\/h2>\n<p>Some other thoughts:<\/p>\n<ul>\n<li>iptables&#8217; conntrack module, until recently, couldn&#8217;t statefully track dhcpv6. This was fixed with <a href=\"http:\/\/comments.gmane.org\/gmane.comp.security.firewalls.netfilter.devel\/41574\" target=\"_blank\">this patch<\/a> but if you don&#8217;t have the <em>nf_conntrack_dhcpv6<\/em> module available in your kernel, you will need to allow ports UDP traffic to\/from fe80::\/10 and to port 546\/from port 547.<\/li>\n<li>Debian was <a href=\"http:\/\/bugs.debian.org\/cgi-bin\/bugreport.cgi?bug=591589\" target=\"_blank\">using the old dhclient3 scripts<\/a> (even after the move to isc-dhcp-client) until very recently. You need version 4.1.1-P1-17 or later to have basic IPv6 support<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Update 5\/31\/12: Scripts are now configurable and various bugs have been fixed. Update 6\/07\/12: Broken link to if-up script fixed, typo in script fixed. Update 10\/30\/15: Scripts moved to GitHub. They&#8217;re not advertising it, but Comcast is turning on IPv6 for all their California customers (and presumably others) who have a supported modem this week. [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[5],"tags":[],"_links":{"self":[{"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=\/wp\/v2\/posts\/308"}],"collection":[{"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=308"}],"version-history":[{"count":56,"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=\/wp\/v2\/posts\/308\/revisions"}],"predecessor-version":[{"id":428,"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=\/wp\/v2\/posts\/308\/revisions\/428"}],"wp:attachment":[{"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=308"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=308"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.phildev.net\/phil\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=308"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}