]> Sergey Matveev's repositories - ndproxy.git/commitdiff
Latest ndproxy version
authorAlexandre Fenyo <nospam@fenyo.net>
Sat, 30 Jul 2016 21:48:51 +0000 (23:48 +0200)
committerSergey Matveev <stargrave@stargrave.org>
Wed, 22 Nov 2023 16:36:06 +0000 (19:36 +0300)
Taken from https://github.com/AlexandreFenyo/ndproxy.git,
but with removed .html, .ps, .pdf, .png and TAGS files,
because they are huge.

23 files changed:
.gitignore [new file with mode: 0644]
INFOS.TXT [new file with mode: 0644]
INSTALL.TXT [new file with mode: 0644]
LICENSE
MANUAL.TXT [new file with mode: 0644]
Makefile [new file with mode: 0644]
README.md
TESTING.TXT [new file with mode: 0644]
etags.sh [new file with mode: 0755]
ndconf.c [new file with mode: 0644]
ndconf.h [new file with mode: 0644]
ndpacket.c [new file with mode: 0644]
ndpacket.h [new file with mode: 0644]
ndparse.c [new file with mode: 0644]
ndparse.h [new file with mode: 0644]
ndproxy.4 [new file with mode: 0644]
ndproxy.c [new file with mode: 0644]
ndproxy.h [new file with mode: 0644]
usr/ports/net/ndproxy/Makefile [new file with mode: 0644]
usr/ports/net/ndproxy/distinfo [new file with mode: 0644]
usr/ports/net/ndproxy/files/ndproxy.in [new file with mode: 0644]
usr/ports/net/ndproxy/files/pkg-message.in [new file with mode: 0644]
usr/ports/net/ndproxy/pkg-descr [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..d96dce7
--- /dev/null
@@ -0,0 +1,10 @@
+.depend*
+[#]*[#]
+@
+*.o
+*.ko
+*.ko.*
+TAGS
+export_syms
+machine
+x86
diff --git a/INFOS.TXT b/INFOS.TXT
new file mode 100644 (file)
index 0000000..16eb5ea
--- /dev/null
+++ b/INFOS.TXT
@@ -0,0 +1,61 @@
+
+READ INSTALL.TXT to install ndproxy
+
+------------------------------------------------------------
+
+publishing ndproxy on GitHub:
+
+add DEVELOPER=yes to /etc/make.conf
+cd to usr/ports/net/ndproxy
+update PLIST_FILES in usr/ports/net/ndproxy/Makefile
+apply section "3.4. Testing the Port" at https://www.freebsd.org/doc/en/books/porters-handbook/book.html
+clean-up everything, as user:
+  make clean
+check consistency:
+  portlint -AC usr/ports/net/ndproxy
+clean-up everything in port distribution, as root:
+  ( cd usr/ports/net/ndproxy && make clean )
+create man pages, as user:
+  make all-man pdf manhtml catman
+  (sed '/````manpage/,$d' README.md ; (echo '````manpage' ; cat MANUAL.TXT)) > README.md.new ; mv README.md.new README.md
+look for tags:
+  git tag
+select a new version tag: v1.4 in this example
+add the new version tag, as user:
+  git tag v1.4
+  git push --tags
+sync with GitHub, as user:
+  git pull
+  git add -A
+  git commit -m new
+  git push
+  git push --tags
+update PORTVERSION in usr/ports/net/ndproxy/Makefile, as user:
+  PORTVERSION=    1.4
+create distinfo, as root:
+  ( cd usr/ports/net/ndproxy && make makesum )
+sync with GitHub, as user:
+  git pull
+  git add -A
+  git commit -m new
+  git push
+create shell archive:
+  ( cd usr/ports/net ; shar `find . -print` > /tmp/ndproxy.shar )
+
+------------------------------------------------------------
+
+Creating a release for the FreeBSD ports tree:
+
+git clone --branch v3.2 https://github.com/AlexandreFenyo/ndproxy.git
+cd ndproxy
+( cd usr/ports/net/ndproxy && make makesum )
+( cd usr/ports/net ; shar `find . -print` > /tmp/ndproxy.shar )
+mv /tmp/ndproxy.shar /tmp/ndproxy-v3.2.shar
+
+
+
+------------------------------------------------------------
+
+removing installed manpage:
+rm -f /usr/share/man/man4/ndproxy.4.gz
+rm -f /usr/local/man/man4/ndproxy.4.gz
diff --git a/INSTALL.TXT b/INSTALL.TXT
new file mode 100644 (file)
index 0000000..f2542e8
--- /dev/null
@@ -0,0 +1,61 @@
+
+Installation instructions for FreeBSD 10.x, 11.x, 12.x and 13.x
+
+------------------------------------------------------------
+
+Installing as a port:
+
+  1- using the port sources from the ports tree:
+    - cd /usr/ports/net/ndproxy
+    - make
+    - make install
+
+  2- using the port sources from the distribution tree:
+    - cd usr/ports/net/ndproxy
+    - make
+    - make install
+
+------------------------------------------------------------
+
+Installing as a package:
+
+  - pkg install ndproxy
+
+------------------------------------------------------------
+
+When not installing from a port or a package:
+
+  1- no debugging:
+    - run 'make clean && make' as user to compile the module and create ndproxy.ko
+    - run 'make install' as root to install the module
+    - run 'make maninstall' as root to install the man page
+
+  2- debug messages on console and debugging symbols:
+    - run 'make clean && make DEBUG_FLAGS=-DDEBUG_NDPROXY' as user to compile the module and create ndproxy.ko
+    - run 'make DEBUG_FLAGS=-DDEBUG_NDPROXY install' as root to install the module
+    - run 'make maninstall' as root to install the man page
+
+Usage and configuration instructions:
+- run 'man ndproxy'
+
+Here is the output of the installation steps:
+% make
+Warning: Object directory not changed from original /space/fenyo/public_html/newweb/ndproxy/ndproxy/tmp
+@ -> /usr/src/sys
+machine -> /usr/src/sys/amd64/include
+x86 -> /usr/src/sys/x86/include
+cc -O2 -pipe  -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -fno-common  -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer  -mno-aes -mno-avx -mcmodel=kernel -mno-red-zone -mno-mmx -mno-sse -msoft-float  -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -Qunused-arguments  -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions  -Wmissing-include-dirs -fdiagnostics-show-option  -Wno-error-tautological-compare -Wno-error-empty-body  -Wno-error-parentheses-equality -Wno-error-unused-function   -c ndproxy.c
+cc -O2 -pipe  -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -fno-common  -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer  -mno-aes -mno-avx -mcmodel=kernel -mno-red-zone -mno-mmx -mno-sse -msoft-float  -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -Qunused-arguments  -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions  -Wmissing-include-dirs -fdiagnostics-show-option  -Wno-error-tautological-compare -Wno-error-empty-body  -Wno-error-parentheses-equality -Wno-error-unused-function   -c ndparse.c
+cc -O2 -pipe  -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -fno-common  -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer  -mno-aes -mno-avx -mcmodel=kernel -mno-red-zone -mno-mmx -mno-sse -msoft-float  -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -Qunused-arguments  -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions  -Wmissing-include-dirs -fdiagnostics-show-option  -Wno-error-tautological-compare -Wno-error-empty-body  -Wno-error-parentheses-equality -Wno-error-unused-function   -c ndpacket.c
+cc -O2 -pipe  -fno-strict-aliasing -Werror -D_KERNEL -DKLD_MODULE -nostdinc   -I. -I@ -I@/contrib/altq -fno-common  -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer  -mno-aes -mno-avx -mcmodel=kernel -mno-red-zone -mno-mmx -mno-sse -msoft-float  -fno-asynchronous-unwind-tables -ffreestanding -fstack-protector -std=iso9899:1999 -Qunused-arguments  -fstack-protector -Wall -Wredundant-decls -Wnested-externs -Wstrict-prototypes  -Wmissing-prototypes -Wpointer-arith -Winline -Wcast-qual  -Wundef -Wno-pointer-sign -fformat-extensions  -Wmissing-include-dirs -fdiagnostics-show-option  -Wno-error-tautological-compare -Wno-error-empty-body  -Wno-error-parentheses-equality -Wno-error-unused-function   -c ndconf.c
+ld  -d -warn-common -r -d -o ndproxy.ko ndproxy.o ndparse.o ndpacket.o ndconf.o
+:> export_syms
+awk -f /sys/conf/kmod_syms.awk ndproxy.ko  export_syms | xargs -J% objcopy % ndproxy.ko
+objcopy --strip-debug ndproxy.ko
+% su   
+Password:
+# make install
+install -o root -g wheel -m 555   ndproxy.ko /boot/kernel
+kldxref /boot/kernel
+# make maninstall
+install -o root -g wheel -m 444 ndproxy.8.gz  /usr/share/man/man8
diff --git a/LICENSE b/LICENSE
index 3d52d0ac84c22c45e3aa33a04793c9d03e323fc8..c9365707c64647eb91fda6bc7462996ae700daaf 100644 (file)
--- a/LICENSE
+++ b/LICENSE
@@ -1,27 +1,23 @@
-Copyright (c) 2016, Alexandre Fenyo
+Copyright (c) 2015-2017 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
 
-* Redistributions of source code must retain the above copyright notice, this
-  list of conditions and the following disclaimer.
-
-* Redistributions in binary form must reproduce the above copyright notice,
-  this list of conditions and the following disclaimer in the documentation
-  and/or other materials provided with the distribution.
-
-* Neither the name of ndproxy nor the names of its
-  contributors may be used to endorse or promote products derived from
-  this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/MANUAL.TXT b/MANUAL.TXT
new file mode 100644 (file)
index 0000000..84a5516
--- /dev/null
@@ -0,0 +1,303 @@
+NDPROXY(4)             FreeBSD Kernel Interfaces Manual             NDPROXY(4)
+
+NAME
+     ndproxy -- Neighbor Discovery Proxy
+
+SYNOPSIS
+     ndproxy is a kernel module that implements IPv6 Neighbor Discovery proxy-
+     ing over Ethernet-like access networks, with many options to handle sev-
+     eral use-cases.
+
+     ndproxy replies to a neighbor solicitation with a specific neighbor ad-
+     vertisement, in order to let the PE uplink router send further packets to
+     a CPE downlink router, that may or may not be the same node that run nd-
+     proxy.
+
+     The hook-based pfil(9) framework is used to let ndproxy be invoked for
+     every IPv6 incoming packet, in order to specifically handle and filter
+     neighbor solicitations and reply with appropriate neighbor advertise-
+     ments.
+
+     ND (Neighbor Discovery) packets are mainly targeted at solicited-node
+     multicast addresses, but ndproxy has no information about the hosts to
+     proxy, then it can not join the corresponding groups. Thus, the interface
+     on which ndproxy listen to solicitations must be put into permanently
+     promiscuous mode: add "promisc" to the ifconfig_<interface> variable in
+     rc.conf(5).
+
+     For the same reason, MLD snooping must be disabled on the switches that
+     share the PE/CPE interconnect (the layer-2 link the listening interface
+     is attached to). Note that MLD snooping must not be disabled entirely on
+     each switch, but only on the corresponding vlan.
+
+     The interface on which ndproxy listen to solicitations only need to be
+     assigned a link-local address. No information about the delegated prefix
+     and no global address are needed on this interface. It is sufficient to
+     add "inet6 -ifdisabled -accept_rtadv auto_linklocal" to the ifconfig_<in-
+     terface>_ipv6 variable in rc.conf(5).
+
+DIFFERENCES WITH NDP
+     The target address to proxy must be given when using the ndp(8) command-
+     line tool with the proxy option. On the contrary, ndproxy does not rely
+     on a list of target addresses to proxy. Thus, RFC-4941 temporary ad-
+     dresses can be proxyfied. For security reasons, many operating systems
+     use a temporary address when establishing outbound connections.
+
+     When using ndp(8) command-line tool with the proxy option, the proxyfied
+     packets are redirected to the node that run ndp. With ndproxy, the host
+     that run ndp can be used only to redirect packets to another IPv6 inter-
+     nal router, for instance a dedicated router with hardware support of IPv6
+     routing process.
+
+PREFIX SUBNETTING
+     Connecting a flat IPv6 network to the Internet is easily done with the
+     RFC-4861 ND protocol. But connecting a subnetted IPv6 prefix is more com-
+     plicated, depending on the ISP network design choices.  ndproxy can help
+     subscribers to achieve this goal.
+
+     Here are some protocols or mechanisms the ISP need to support, when the
+     delegated prefix must be subnetted and assigned to multiple links within
+     the subscriber's network.  For instance, the ISP could learn routes from
+     the subscriber router using an IGP routing protocol, but the ISP and the
+     subscriber must agree with a common routing protocol.  The ISP could also
+     feed the PE with a static route to the CPE router, but the ISP must be
+     informed about the subscriber router address.  Finally, the ISP could use
+     the RFC-3633 IPv6 Prefix Options with DHCPv6 to delegate the prefix from
+     its PE router to a requesting subscriber's router: in such a case, the
+     ISP must support the DHCPv6 option.
+
+     ndproxy has been written for subscribers to ISP that do not support any
+     of those mechanisms or protocols, thus not being able to natively subnet
+     their IPv6 delegated prefix.
+
+NETWORK DESIGN
+     Here is a generic network design using ndproxy to solve such situations:
+        ______________________                 \
+       /                      \                 \
+       |   ISP core network   |                  \
+       \__________ ___________/                   |
+                  |                               | ISP network
+                  | dsl to subscriber premises    |
+                  |                              /
+              +---+---------------------+       /
+              |  PE or triple-play box  |      /
+              +-----------+-------------+
+                          |
+      PE/CPE interconnect | /48 prefix         \
+              --+---------+---------+--         \
+                |  no mld snooping  |            \
+                |                   |             |
+           +----+--------+     +----+-----+       |
+           |     CPE     |     | ndproxy  |       |
+           | IPv6 router |     | BSD host |       |
+           +----+---+----+     +----------+       |
+       subnet1  |   |  subnet2                    | Subscriber
+      ----+-----+- -+-----+------                 | network
+          | /60           | /60                   |
+      +---+------+   +----+-----+                 |
+      |Subscriber|   |Subscriber|                /
+      |  host    |   |  host    |               /
+      +----------+   +----------+              /
+
+     Note that many other use-cases can be handled with ndproxy: the BSD host
+     and the CPE router can be the same node, the delegated-prefix length can
+     be /64, the PE router can have several interfaces on the ISP/Subscriber
+     layer-2 boundary, there can be multiple PE routers, etc.
+
+PREFIX LENGTH
+     Even if the IESG and the IAB first recommended the allocations of /48
+     prefixes in the general case, for the boundary between the public and the
+     private topology (see RFC-3177), and that some Regional Internet Reg-
+     istries (APNIC, ARIN and RIPE) have subsequently revised the end site as-
+     signment policy to encourage the assignment of /56 blocks to end sites,
+     and that RFC-6177 finally recommended giving home sites significantly
+     more than a single /64, in order for home sites to be given multiple sub-
+     nets, some ISP currently only delegate /64 prefixes.
+
+     In such a case, the subscriber should subnet a RFC-4193 Unique Local IPv6
+     Unicast Addresses prefix to the internal subnetworks, for internal-to-in-
+     ternal communications. The /64 global prefix should be routed to the only
+     internal subnet in which RFC-4941 temporary addresses are used by hosts
+     when establishing outbound connections. Static routes on the CPE router
+     should be set to let hosts on other internal subnets be able to communi-
+     cate with the Internet. Using temporary addresses for outbound connec-
+     tions to the Internet must be disabled on hosts on those other internal
+     subnets.
+
+IPv6 EXTENSION HEADERS
+     For security reasons, ndproxy explicitely rejects neighbor solicitation
+     packets containing any extension header.  Such a packet is mainly unat-
+     tended:
+
+     Fragmentation:
+
+                   According to RFC-6980, IPv6 fragmentation header is forbid-
+                   den in all neighbor discovery messages.
+
+     Hop-by-hop header:
+
+                   commonly used for jumbograms or for MLD. Should not involve
+                   neighbor solicitation packets.
+
+     Destination mobility headers:
+
+                   commonly used for mobility, ndproxy does not support these
+                   headers.
+
+     Routing header:
+
+                   commonly used for mobility or source routing, ndproxy does
+                   not support these headers.
+
+     AH & ESP headers:
+
+                   securing the neighbor discovery process is not done with
+                   IPsec but with the SEcure Neighbor Discovery protocol
+                   (RFC-3971). ndproxy can not support RFC-3971, since proxi-
+                   fying ND packets is some kind of a spoofing process.
+
+EXCEPTION ADDRESSES
+     Some neigbhor solicitations sent on the PE/CPE interconnect must not be
+     proxyfied:
+
+     1.   solicitations sent by other nodes than the PE;
+
+     2.   solicitations sent by the PE to reach any on-link address (the ad-
+          dress filled in the target address option) owned by nodes attached
+          to the PE/CPE interconnect, for instance to reach the CPE, the nd-
+          proxy host or other hosts attached to this layer-2 interconnect.
+
+     The target addresses filled in those solicitations that ndproxy must ig-
+     nore have to be declared via sysctl (net.inet6.ndproxyconf_excep-
+     tion_ipv6_addresses). This list must contain the link-local and global-
+     scoped unicast and anycast addresses of the CPE, of the ndproxy host and
+     of any other host than the PE attached to the PE/CPE interconnect.
+
+     Failing to maintain this list correctly could lead to badly redirect some
+     packets to the CPE, but with a simple network design, this list can be
+     let empty.
+
+UPLINK ROUTER ADDRESSES
+     ndproxy only handles packets originating from one of the PE addresses.
+     During its address resolution process, different source addresses can be
+     choosen by the PE, depending on the packet that triggered the process or
+     depending on other external constraints.
+
+     Here are some cases when it can occur:
+
+     1.   The PE may have multiple interfaces;
+
+     2.   There may be multiple PE;
+
+     3.   Many routers choose to use a link-local address when sending neigh-
+          bor solicitations, but when an administrator of such a router, also
+          having a global address assigned on the same link, tries to send
+          packets (echo request, for instance) to an on-link destination
+          global address, the source address of the echo request packet
+          prompting the solicitation may be global-scoped according to the se-
+          lection algorithm described in RFC-6724. Therefore, the source ad-
+          dress of the Neighbor Solicitation packet should also be selected in
+          the same global scope, according to RFC-4861;
+
+     4.   When the uplink router does not yet know its own address, it must
+          use the unspecified address, according to RFC-4861.
+
+     So, it can not be assumed that an uplink router will always use the same
+     IPv6 address to send neighbor solicitations. Each assigned address that
+     can be used as a source address by the PE on its downlink interface must
+     then be declared to ndproxy via sysctl (net.inet6.ndproxyconf_up-
+     link_ipv6_addresses).
+
+     ndproxy will only handle packets that come from one of these addresses.
+
+     A special care must be taken about the unsolicited address. It may be
+     used by the PE, then it is part of the list of PE addresses and should
+     therefore be added to the list of PE addresses. Since this address can
+     also be used by other nodes during some initialization steps (for in-
+     stance when hot-swapping an Ethernet board), another node could use this
+     address to send neighbor solicitations that ndproxy should not handle,
+     because they are not sent by the PE. In fact, this is not a problem be-
+     cause the target address option contained in a solicitation from this
+     other node should be in the exception list. So, adding the unsolicited
+     address in the PE addresses list should be safe.
+
+     Failing to maintain this list correctly could lead the PE not to be able
+     to establish outbound connections to nodes on the PE/CPE interconnect,
+     but if this list contains at least the PE link-local address, IPv6 con-
+     nectivity should be correctly established between the Internet and the
+     internal subscriber's subnets.
+
+CONFIGURATION
+     An IPv6 address can be any valid textual representation according to
+     RFC-4291 and RFC-5952 (this means that transitional textual representa-
+     tion is fully supported).  Other representations will trigger an error
+     event. IPv6 address lists must be formated as series of IPv6 adresses
+     separated by semi-colons.
+
+     The sysctl utility or rc.conf(5) are used to set ndproxy configuration
+     parameters.
+
+     If you have installed ndproxy as a port or as a package, set the follow-
+     ing variables in rc.conf(5) and load the module at boot time by placing
+     the following line in rc.conf(5):
+
+           ndproxy_enable="YES"
+
+     On the contrary, if you have NOT installed ndproxy as a port or as a
+     package but as a standalone distribution, place the sysctl entries in
+     sysctl.conf(5) and load the module at boot time by placing the following
+     line in loader.conf(5):
+
+           ndproxy_load="YES"
+
+     net.inet6.ndproxyconf_uplink_interface sysctl entry or
+                   ndproxy_uplink_interface rc.conf variable:
+
+                   Name of the interface talking to the broadcast multi-access
+                   network connecting the PE and CPE routers.
+
+                   Example: "vlan2".
+
+     net.inet6.ndproxyconf_downlink_mac_address sysctl entry or
+                   ndproxy_downlink_mac_address rc.conf variable:
+
+                   MAC address of the CPE router. Neighbor advertisements sent
+                   by ndproxy will be filled with this address in the target
+                   link-layer address option. The format of this parameter is
+                   the hexadecimal representation made of 6 groups of 2 hexa-
+                   decimal numbers separated by colons.
+
+                   Example: "00:0C:29:B6:43:D5".
+
+     net.inet6.ndproxyconf_exception_ipv6_addresses sysctl entry or
+                   ndproxy_exception_ipv6_addresses rc.conf variable:
+
+                   Target addresses not to proxy. In a simple network design,
+                   this list can be let empty. See section "EXCEPTION AD-
+                   DRESSES".
+
+                   Example:
+                   "fe80::20d:edff:fe7b:68b7;fe80::222:15ff:fe3b:59a".
+
+     net.inet6.ndproxyconf_uplink_ipv6_addresses sysctl entry or
+                   ndproxy_uplink_ipv6_addresses rc.conf variable:
+
+                   Addresses of the PE. This list should at least contain the
+                   PE link-local address. See section "UPLINK ROUTER AD-
+                   DRESSES".
+
+                   Example:
+                   "fe80::207:cbff:fe4b:2d20;2a01:e35:8aae:bc60::1;::".
+
+     net.inet6.ndproxycount sysctl entry:
+
+                   Number of advertisements sent.
+
+SEE ALSO
+     inet6(4), rc.conf(5), loader.conf(5), sysctl.conf(5), sysctl(8),
+     loader(8), pfil(9)
+
+AUTHOR
+     Alexandre Fenyo <alex@fenyo.net> - www.fenyo.net
+
+FreeBSD 13.0                     May 27, 2017                     FreeBSD 13.0
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..54c6c85
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,74 @@
+
+# after having changed "#include" statements, call 'rm .depends; make depend' to create a new dependencies file
+
+# compile without debug informations on console and without debugging symbols:
+#   make clean && make
+# compile with debug informations on console:
+#   make clean && make DEBUG_FLAGS=-DDEBUG_NDPROXY
+
+# use load & unload predefined targets to load & unload the module:
+#   make load
+#   make unload
+
+# use install target to copy the module to /boot/kernel and generate hints for the kernel loader:
+#   make install
+# or, if compiled with debugging symbols:
+#  make DEBUG_FLAGS=-DDEBUG_NDPROXY install
+
+# use all-man target to compress man page
+# use maninstall target to install man page
+# use manlint target to check manpage
+
+# declare name of kernel module
+KMOD    =  ndproxy
+
+# build for a kernel with 'options VIMAGE'
+CFLAGS += -DVIMAGE
+
+# enumerate source files for kernel module
+SRCS    = ndproxy.c ndparse.c ndpacket.c ndconf.c
+MAN    += ndproxy.4
+
+CLEANFILES += ndproxy.ko.debug ndproxy.ko.full
+
+# Include kernel module makefile
+.include <bsd.kmod.mk>
+.include <bsd.man.mk>
+
+man: all-man maninstall
+       man ndproxy
+
+pdf: ndproxy.4
+       groff -man ndproxy.4 > ndproxy.ps
+       ps2pdf ndproxy.ps
+
+mandoc: ndproxy.4
+       mandoc -Ttree ndproxy.4
+
+manhtml: ndproxy.4
+       groff -Thtml -man ndproxy.4 > ndproxy.html
+
+catman: ndproxy.4
+       groff -Tascii -man ndproxy.4 | sed 's/.\[[012]*m//g' > MANUAL.TXT
+
+lines:
+       wc -l *.c *.h
+
+etags:
+       ./etags.sh
+
+propget:
+       svn propget svn:keywords *.c *.h *.TXT Makefile
+
+# call this target after adding a new text file to the repository
+propset:
+       svn propset svn:keywords Id *.c *.h *.TXT Makefile
+
+ci:
+       svn ci -m new
+
+distinfo:
+       cd usr/ports/net/ndproxy && make makesum
+
+update:
+       svn update
index 046f6530b41d52e33288cc0bf69f222806081dd6..4db348c116c5ee56f79a391cf36262fbc540b292 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,2 +1,320 @@
+# available on the official FreeBSD ports tree
+
+From 2017-08, ndproxy is made available from the official FreeBSD ports tree: [ndproxy port at FreeBSD](https://www.freshports.org/net/ndproxy/)
+
+This way, you can get precompiled packages for many hardware targets, or install it from the  sources, while maintaining dependencies. The port is based on this GitHub source repository content.
+
 # ndproxy
-kernel module for FreeBSD that implements IPv6 Neighbor Discovery proxying over Ethernet-like access networks
+
+Kernel module for FreeBSD that implements IPv6 Neighbor Discovery proxying over Ethernet-like access networks
+
+With this module, your uplink provider continues to see you as a flat network, but you can subnet your IPv6 prefix and route packets to your preferred gateway (a Cisco router for instance), that may split your network into several subnets. To achieve this goal, your FreeBSD router will redirect packets coming from your uplink provider router to your gateway. You simply need to connect one of your FreeBSD host interfaces to the layer 2 that interconnects your provider and your gateway, and disable MLD snooping on the interconnection switch.
+
+Tested with success on FreeBSD 10.x, FreeBSD 11.x, FreeBSD 12.x and CURRENT (FreeBSD 13)
+
+![network architecture](https://raw.githubusercontent.com/AlexandreFenyo/ndproxy/master/network-architecture.png)
+
+````manpage
+NDPROXY(4)             FreeBSD Kernel Interfaces Manual             NDPROXY(4)
+
+NAME
+     ndproxy -- Neighbor Discovery Proxy
+
+SYNOPSIS
+     ndproxy is a kernel module that implements IPv6 Neighbor Discovery proxy-
+     ing over Ethernet-like access networks, with many options to handle sev-
+     eral use-cases.
+
+     ndproxy replies to a neighbor solicitation with a specific neighbor ad-
+     vertisement, in order to let the PE uplink router send further packets to
+     a CPE downlink router, that may or may not be the same node that run nd-
+     proxy.
+
+     The hook-based pfil(9) framework is used to let ndproxy be invoked for
+     every IPv6 incoming packet, in order to specifically handle and filter
+     neighbor solicitations and reply with appropriate neighbor advertise-
+     ments.
+
+     ND (Neighbor Discovery) packets are mainly targeted at solicited-node
+     multicast addresses, but ndproxy has no information about the hosts to
+     proxy, then it can not join the corresponding groups. Thus, the interface
+     on which ndproxy listen to solicitations must be put into permanently
+     promiscuous mode: add "promisc" to the ifconfig_<interface> variable in
+     rc.conf(5).
+
+     For the same reason, MLD snooping must be disabled on the switches that
+     share the PE/CPE interconnect (the layer-2 link the listening interface
+     is attached to). Note that MLD snooping must not be disabled entirely on
+     each switch, but only on the corresponding vlan.
+
+     The interface on which ndproxy listen to solicitations only need to be
+     assigned a link-local address. No information about the delegated prefix
+     and no global address are needed on this interface. It is sufficient to
+     add "inet6 -ifdisabled -accept_rtadv auto_linklocal" to the ifconfig_<in-
+     terface>_ipv6 variable in rc.conf(5).
+
+DIFFERENCES WITH NDP
+     The target address to proxy must be given when using the ndp(8) command-
+     line tool with the proxy option. On the contrary, ndproxy does not rely
+     on a list of target addresses to proxy. Thus, RFC-4941 temporary ad-
+     dresses can be proxyfied. For security reasons, many operating systems
+     use a temporary address when establishing outbound connections.
+
+     When using ndp(8) command-line tool with the proxy option, the proxyfied
+     packets are redirected to the node that run ndp. With ndproxy, the host
+     that run ndp can be used only to redirect packets to another IPv6 inter-
+     nal router, for instance a dedicated router with hardware support of IPv6
+     routing process.
+
+PREFIX SUBNETTING
+     Connecting a flat IPv6 network to the Internet is easily done with the
+     RFC-4861 ND protocol. But connecting a subnetted IPv6 prefix is more com-
+     plicated, depending on the ISP network design choices.  ndproxy can help
+     subscribers to achieve this goal.
+
+     Here are some protocols or mechanisms the ISP need to support, when the
+     delegated prefix must be subnetted and assigned to multiple links within
+     the subscriber's network.  For instance, the ISP could learn routes from
+     the subscriber router using an IGP routing protocol, but the ISP and the
+     subscriber must agree with a common routing protocol.  The ISP could also
+     feed the PE with a static route to the CPE router, but the ISP must be
+     informed about the subscriber router address.  Finally, the ISP could use
+     the RFC-3633 IPv6 Prefix Options with DHCPv6 to delegate the prefix from
+     its PE router to a requesting subscriber's router: in such a case, the
+     ISP must support the DHCPv6 option.
+
+     ndproxy has been written for subscribers to ISP that do not support any
+     of those mechanisms or protocols, thus not being able to natively subnet
+     their IPv6 delegated prefix.
+
+NETWORK DESIGN
+     Here is a generic network design using ndproxy to solve such situations:
+        ______________________                 \
+       /                      \                 \
+       |   ISP core network   |                  \
+       \__________ ___________/                   |
+                  |                               | ISP network
+                  | dsl to subscriber premises    |
+                  |                              /
+              +---+---------------------+       /
+              |  PE or triple-play box  |      /
+              +-----------+-------------+
+                          |
+      PE/CPE interconnect | /48 prefix         \
+              --+---------+---------+--         \
+                |  no mld snooping  |            \
+                |                   |             |
+           +----+--------+     +----+-----+       |
+           |     CPE     |     | ndproxy  |       |
+           | IPv6 router |     | BSD host |       |
+           +----+---+----+     +----------+       |
+       subnet1  |   |  subnet2                    | Subscriber
+      ----+-----+- -+-----+------                 | network
+          | /60           | /60                   |
+      +---+------+   +----+-----+                 |
+      |Subscriber|   |Subscriber|                /
+      |  host    |   |  host    |               /
+      +----------+   +----------+              /
+
+     Note that many other use-cases can be handled with ndproxy: the BSD host
+     and the CPE router can be the same node, the delegated-prefix length can
+     be /64, the PE router can have several interfaces on the ISP/Subscriber
+     layer-2 boundary, there can be multiple PE routers, etc.
+
+PREFIX LENGTH
+     Even if the IESG and the IAB first recommended the allocations of /48
+     prefixes in the general case, for the boundary between the public and the
+     private topology (see RFC-3177), and that some Regional Internet Reg-
+     istries (APNIC, ARIN and RIPE) have subsequently revised the end site as-
+     signment policy to encourage the assignment of /56 blocks to end sites,
+     and that RFC-6177 finally recommended giving home sites significantly
+     more than a single /64, in order for home sites to be given multiple sub-
+     nets, some ISP currently only delegate /64 prefixes.
+
+     In such a case, the subscriber should subnet a RFC-4193 Unique Local IPv6
+     Unicast Addresses prefix to the internal subnetworks, for internal-to-in-
+     ternal communications. The /64 global prefix should be routed to the only
+     internal subnet in which RFC-4941 temporary addresses are used by hosts
+     when establishing outbound connections. Static routes on the CPE router
+     should be set to let hosts on other internal subnets be able to communi-
+     cate with the Internet. Using temporary addresses for outbound connec-
+     tions to the Internet must be disabled on hosts on those other internal
+     subnets.
+
+IPv6 EXTENSION HEADERS
+     For security reasons, ndproxy explicitely rejects neighbor solicitation
+     packets containing any extension header.  Such a packet is mainly unat-
+     tended:
+
+     Fragmentation:
+
+                   According to RFC-6980, IPv6 fragmentation header is forbid-
+                   den in all neighbor discovery messages.
+
+     Hop-by-hop header:
+
+                   commonly used for jumbograms or for MLD. Should not involve
+                   neighbor solicitation packets.
+
+     Destination mobility headers:
+
+                   commonly used for mobility, ndproxy does not support these
+                   headers.
+
+     Routing header:
+
+                   commonly used for mobility or source routing, ndproxy does
+                   not support these headers.
+
+     AH & ESP headers:
+
+                   securing the neighbor discovery process is not done with
+                   IPsec but with the SEcure Neighbor Discovery protocol
+                   (RFC-3971). ndproxy can not support RFC-3971, since proxi-
+                   fying ND packets is some kind of a spoofing process.
+
+EXCEPTION ADDRESSES
+     Some neigbhor solicitations sent on the PE/CPE interconnect must not be
+     proxyfied:
+
+     1.   solicitations sent by other nodes than the PE;
+
+     2.   solicitations sent by the PE to reach any on-link address (the ad-
+          dress filled in the target address option) owned by nodes attached
+          to the PE/CPE interconnect, for instance to reach the CPE, the nd-
+          proxy host or other hosts attached to this layer-2 interconnect.
+
+     The target addresses filled in those solicitations that ndproxy must ig-
+     nore have to be declared via sysctl (net.inet6.ndproxyconf_excep-
+     tion_ipv6_addresses). This list must contain the link-local and global-
+     scoped unicast and anycast addresses of the CPE, of the ndproxy host and
+     of any other host than the PE attached to the PE/CPE interconnect.
+
+     Failing to maintain this list correctly could lead to badly redirect some
+     packets to the CPE, but with a simple network design, this list can be
+     let empty.
+
+UPLINK ROUTER ADDRESSES
+     ndproxy only handles packets originating from one of the PE addresses.
+     During its address resolution process, different source addresses can be
+     choosen by the PE, depending on the packet that triggered the process or
+     depending on other external constraints.
+
+     Here are some cases when it can occur:
+
+     1.   The PE may have multiple interfaces;
+
+     2.   There may be multiple PE;
+
+     3.   Many routers choose to use a link-local address when sending neigh-
+          bor solicitations, but when an administrator of such a router, also
+          having a global address assigned on the same link, tries to send
+          packets (echo request, for instance) to an on-link destination
+          global address, the source address of the echo request packet
+          prompting the solicitation may be global-scoped according to the se-
+          lection algorithm described in RFC-6724. Therefore, the source ad-
+          dress of the Neighbor Solicitation packet should also be selected in
+          the same global scope, according to RFC-4861;
+
+     4.   When the uplink router does not yet know its own address, it must
+          use the unspecified address, according to RFC-4861.
+
+     So, it can not be assumed that an uplink router will always use the same
+     IPv6 address to send neighbor solicitations. Each assigned address that
+     can be used as a source address by the PE on its downlink interface must
+     then be declared to ndproxy via sysctl (net.inet6.ndproxyconf_up-
+     link_ipv6_addresses).
+
+     ndproxy will only handle packets that come from one of these addresses.
+
+     A special care must be taken about the unsolicited address. It may be
+     used by the PE, then it is part of the list of PE addresses and should
+     therefore be added to the list of PE addresses. Since this address can
+     also be used by other nodes during some initialization steps (for in-
+     stance when hot-swapping an Ethernet board), another node could use this
+     address to send neighbor solicitations that ndproxy should not handle,
+     because they are not sent by the PE. In fact, this is not a problem be-
+     cause the target address option contained in a solicitation from this
+     other node should be in the exception list. So, adding the unsolicited
+     address in the PE addresses list should be safe.
+
+     Failing to maintain this list correctly could lead the PE not to be able
+     to establish outbound connections to nodes on the PE/CPE interconnect,
+     but if this list contains at least the PE link-local address, IPv6 con-
+     nectivity should be correctly established between the Internet and the
+     internal subscriber's subnets.
+
+CONFIGURATION
+     An IPv6 address can be any valid textual representation according to
+     RFC-4291 and RFC-5952 (this means that transitional textual representa-
+     tion is fully supported).  Other representations will trigger an error
+     event. IPv6 address lists must be formated as series of IPv6 adresses
+     separated by semi-colons.
+
+     The sysctl utility or rc.conf(5) are used to set ndproxy configuration
+     parameters.
+
+     If you have installed ndproxy as a port or as a package, set the follow-
+     ing variables in rc.conf(5) and load the module at boot time by placing
+     the following line in rc.conf(5):
+
+           ndproxy_enable="YES"
+
+     On the contrary, if you have NOT installed ndproxy as a port or as a
+     package but as a standalone distribution, place the sysctl entries in
+     sysctl.conf(5) and load the module at boot time by placing the following
+     line in loader.conf(5):
+
+           ndproxy_load="YES"
+
+     net.inet6.ndproxyconf_uplink_interface sysctl entry or
+                   ndproxy_uplink_interface rc.conf variable:
+
+                   Name of the interface talking to the broadcast multi-access
+                   network connecting the PE and CPE routers.
+
+                   Example: "vlan2".
+
+     net.inet6.ndproxyconf_downlink_mac_address sysctl entry or
+                   ndproxy_downlink_mac_address rc.conf variable:
+
+                   MAC address of the CPE router. Neighbor advertisements sent
+                   by ndproxy will be filled with this address in the target
+                   link-layer address option. The format of this parameter is
+                   the hexadecimal representation made of 6 groups of 2 hexa-
+                   decimal numbers separated by colons.
+
+                   Example: "00:0C:29:B6:43:D5".
+
+     net.inet6.ndproxyconf_exception_ipv6_addresses sysctl entry or
+                   ndproxy_exception_ipv6_addresses rc.conf variable:
+
+                   Target addresses not to proxy. In a simple network design,
+                   this list can be let empty. See section "EXCEPTION AD-
+                   DRESSES".
+
+                   Example:
+                   "fe80::20d:edff:fe7b:68b7;fe80::222:15ff:fe3b:59a".
+
+     net.inet6.ndproxyconf_uplink_ipv6_addresses sysctl entry or
+                   ndproxy_uplink_ipv6_addresses rc.conf variable:
+
+                   Addresses of the PE. This list should at least contain the
+                   PE link-local address. See section "UPLINK ROUTER AD-
+                   DRESSES".
+
+                   Example:
+                   "fe80::207:cbff:fe4b:2d20;2a01:e35:8aae:bc60::1;::".
+
+     net.inet6.ndproxycount sysctl entry:
+
+                   Number of advertisements sent.
+
+SEE ALSO
+     inet6(4), rc.conf(5), loader.conf(5), sysctl.conf(5), sysctl(8),
+     loader(8), pfil(9)
+
+AUTHOR
+     Alexandre Fenyo <alex@fenyo.net> - www.fenyo.net
+
+FreeBSD 13.0                     May 27, 2017                     FreeBSD 13.0
diff --git a/TESTING.TXT b/TESTING.TXT
new file mode 100644 (file)
index 0000000..b0b2965
--- /dev/null
@@ -0,0 +1,95 @@
+
+Unit testing on a single vlan:
+
+on the same vlan than the BSD server, choose a host (W7):
+link local address of this host is:
+fe80::6d19:b3a3:b8cb:6f1b
+global address of this host is:
+2a01:e35:8aae:bc60:a159:8fcf:7043:3278
+
+on the BSD server, use the host addresses:
+
+  compile with debug messages
+  sysctl -w net.inet6.ndproxyconf_uplink_interface=em0
+  sysctl -w net.inet6.ndproxyconf_downlink_mac_address=00:01:23:45:67:89
+  sysctl -w net.inet6.ndproxyconf_uplink_ipv6_addresses='2a01:e35:8aae:bc60:a159:8fcf:7043:3278;fe80::6d19:b3a3:b8cb:6f1b;::'
+  ifconfig em0 promisc
+  tail -f /var/log/messages
+
+unit testing from the host (W7):
+
+  - 1st test:
+    ping 2a01:e35:8aae:bc60::29ff:fe9d:1234
+    netsh interface ipv6 show neig
+    and find 2a01:e35:8aae:bc60::29ff:fe9d:1234
+
+  - 2nd test:
+    netsh interface ipv6 show interface
+    ping -6 fe80::1:1:1:3%28
+    netsh interface ipv6 show neig
+    and find fe80::1:1:1:3%28
+
+------------------------------------------------------------
+
+Testing with poudriere on AWS
+create an EC2 instance of a FreeBSD version newer than the latest poudriere target to test
+add a ZFS volume dedicated to poudriere
+add DEVELOPER=yes to /etc/make.conf
+zpool create ztank /dev/xbd1
+update /usr/local/etc/poudriere.conf:
+root@ip-172-30-1-13:/usr/local/etc # diff poudriere.conf poudriere.conf.orig
+  12c12
+  < ZPOOL=ztank
+  ---
+  > #ZPOOL=tank
+  30c30
+  < FREEBSD_HOST=ftp://ftp.freebsd.org
+  ---
+  > FREEBSD_HOST=_PROTO_://_CHANGE_THIS_
+  48c48
+  < USE_PORTLINT=yes
+  ---
+  > USE_PORTLINT=no
+  82c82
+  < DISTFILES_CACHE=/root/distfiles
+  ---
+  > DISTFILES_CACHE=/usr/ports/distfiles
+mkdir /root/distfiles
+portsnap fetch
+portsnap extract
+poudriere ports -c -F -f none -M /root/ports -p local
+cp -rp /usr/ports /root/ports
+rm -rf /root/ports/net/ndproxy
+cd ~/git/ndproxy/usr/ports/net ; shar `find . -print` > /tmp/ndproxy.shar
+cd /root/ports/net/ndproxy
+sh /tmp/ndproxy.shar
+
+Creation of jails for poudriere:
+- by the network:
+  poudriere jail -c -j 12amd64 -v head@321619 -a amd64 -m svn+https (uname -a to get the svn release)
+- with a local repository:
+  - repository downloaded with svnlite:
+    mkdir /root/svn
+    cd /root/svn
+    svnlite co -r 344112 https://svn.freebsd.org/base/head src
+    cd src
+    poudriere jail -c -j 13i386 -b -a i386 -m src=/root/svn/src -J 112 >& /tmp/poud-c-13i386.log &
+  - repository downloaded with http:
+    mkdir /root/svn
+    fetch http://ftp.freebsd.org/pub/FreeBSD/releases/amd64/11.1-RELEASE/src.txz
+    unxz src.txz
+    tar xf src.tar
+    poudriere jail -c -j 111i386 -b -a i386 -m src=/root/svn/src -J 112 >& /tmp/poud-c-111i386.log &
+Many jails can be created at the same time: (note that -b means "make build-world")
+poudriere jail -c -j 13amd64 -b -a amd64 -m src=/root/bsd-current/src -J 112 > /tmp/poud-c-13amd64.log &
+poudriere jail -c -j 13i386 -b -a i386 -m src=/root/bsd-current/src -J 112 >& /tmp/poud-c-13i386.log &
+poudriere jail -c -j 12amd64 -b -a amd64 -m src=/root/bsd-12.0/src -J 112 >& /tmp/poud-c-12amd64.log &
+poudriere jail -c -j 12i386 -b -a i386 -m src=/root/bsd-12.0/src -J 112 >& /tmp/poud-c-12i386.log &
+
+Many tests can be done at the same time:
+poudriere testport -j 13amd64 -p local -o net/ndproxy -J 112 > ~/testport-13amd64.log &
+poudriere testport -j 13i386 -p local -o net/ndproxy -J 112 > ~/testport-13i386.log &
+poudriere testport -j 12amd64 -p local -o net/ndproxy -J 112 > ~/testport-12amd64.log &
+poudriere testport -j 12i386 -p local -o net/ndproxy -J 112 > ~/testport-12i386.log &
+
+For CURRENT, the jail version must be less recent than the host one (see uname -a to get the host svn revision version).
diff --git a/etags.sh b/etags.sh
new file mode 100755 (executable)
index 0000000..aa623eb
--- /dev/null
+++ b/etags.sh
@@ -0,0 +1,24 @@
+#!/usr/local/bin/zsh
+
+# index kernel sources with Emacs tags
+
+# http://www.emacswiki.org/emacs/EmacsTags#tags
+# M-. : search
+# M-, : next
+# M-TAB complete
+
+setopt EXTENDED_GLOB
+
+SYS=/usr/src/sys
+rm -f TAGS
+
+for D in $SYS/{netinet6,net,netinet} $SYS/*~$SYS/net~$SYS/netinet~$SYS/netinet6~$SYS/netinet6
+do
+    find $D -type f -name '*.s' | xargs etags --no-members -a
+    find $D -type f -name '*.h' | xargs etags --no-members -a
+done
+
+for D in $SYS/{netinet6,net,netinet} $SYS/*~$SYS/net~$SYS/netinet~$SYS/netinet6~$SYS/netinet6
+do
+    find $D -type f -name '*.c' | xargs etags --no-members -a
+done
diff --git a/ndconf.c b/ndconf.c
new file mode 100644 (file)
index 0000000..15dd877
--- /dev/null
+++ b/ndconf.c
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2015-2019 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/pfil.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include "ndconf.h"
+
+// packets handled counter
+int ndproxy_conf_count = 0;
+
+// uplink interface name
+char ndproxy_conf_str_uplink_interface[IFNAMSIZ] = "";
+
+// uplink router IPv6 link-local or global addresses
+struct in6_addr ndproxy_conf_uplink_ipv6_addresses[CONF_NUPLINK_MAX];
+int ndproxy_conf_uplink_ipv6_naddresses = 0;
+
+// IPv6 link-local or global exceptions address list
+struct in6_addr ndproxy_conf_exception_ipv6_addresses[CONF_NEXCEPTIONS_MAX];
+int ndproxy_conf_exception_ipv6_naddresses = 0;
+
+// downlink router MAC address
+struct ether_addr ndproxy_conf_downlink_mac_address;
+bool ndproxy_conf_downlink_mac_address_isset = false;
+
+// uplink router MAC address
+struct ether_addr ndproxy_conf_uplink_mac_address;
+bool ndproxy_conf_uplink_mac_address_isset = false;
diff --git a/ndconf.h b/ndconf.h
new file mode 100644 (file)
index 0000000..24c6dfe
--- /dev/null
+++ b/ndconf.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2015 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __NDCONF_H
+#define __NDCONF_H
+
+// define DEBUG_NDPROXY to send debugging informations to the console
+// #define DEBUG_NDPROXY
+
+// max size of a MAC address: XX:XX:XX:XX:XX:XX = 17 chars
+#define MACMAXSIZE 17
+
+// max size of an IPv6 address: XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:YYY.YYY.YYY.YYY = 45 chars
+#define IP6MAXSIZE 45
+
+// lower bound of exception addresses count to reserve space for
+// if more than 32 exception addresses are needed, you may have to adjust this parameter
+#define CONF_NEXCEPTIONS_MAX 32
+// exceptions list: IPv6 addresses separated by ';', whole string is null terminated
+#define CONF_NEXCEPTIONS_SIZE (CONF_NEXCEPTIONS_MAX * (IP6MAXSIZE + 1) + 1)
+
+// lower bound of uplink IPv6 addresses count to reserve space for
+// if more than 32 uplink IPv6 addresses are needed, you may have to adjust this parameter
+#define CONF_NUPLINK_MAX 32
+// exceptions list: IPv6 addresses separated by ';', whole string is null terminated
+#define CONF_NUPLINK_SIZE (CONF_NUPLINK_MAX * (IP6MAXSIZE + 1) + 1)
+
+// packets handled counter
+extern int ndproxy_conf_count;
+
+// uplink interface name
+extern char ndproxy_conf_str_uplink_interface[IFNAMSIZ];
+
+// uplink router IPv6 link-local or global address
+extern struct in6_addr ndproxy_conf_uplink_ipv6_addresses[CONF_NUPLINK_MAX];
+extern int ndproxy_conf_uplink_ipv6_naddresses;
+
+// IPv6 link-local or global exceptions address list
+extern struct in6_addr ndproxy_conf_exception_ipv6_addresses[CONF_NEXCEPTIONS_MAX];
+extern int ndproxy_conf_exception_ipv6_naddresses;
+
+// downlink router MAC address
+extern struct ether_addr ndproxy_conf_downlink_mac_address;
+extern bool ndproxy_conf_downlink_mac_address_isset;
+
+// uplink router MAC address
+extern struct ether_addr ndproxy_conf_uplink_mac_address;
+extern bool ndproxy_conf_uplink_mac_address_isset;
+
+#endif
diff --git a/ndpacket.c b/ndpacket.c
new file mode 100644 (file)
index 0000000..004c006
--- /dev/null
@@ -0,0 +1,420 @@
+/*-
+ * Copyright (c) 2015-2019 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/kdb.h>
+#include <net/if.h>
+#include <net/pfil.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/in_pcb.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/ip6_var.h>
+#include <netinet6/scope6_var.h>
+
+#include "ndpacket.h"
+#include "ndconf.h"
+#include "ndparse.h"
+
+// Reply to neighbor solicitations with a specific neighbor advertisement, in order
+// to let the uplink router send packets to a downlink router, that may or may not
+// be the current host that run ndproxy.
+// The current host, the uplink router and the downlink router must be attached
+// to a common layer-2 link with broadcast multi-access capability.
+
+// Neighbor solicitation messages are multicast to the solicited-node multicast
+// address of the target address. Since we do not know the target address,
+// we can not join the corresponding group. So, to capture the solicitation messages,
+// the uplink interface must be set in permanent promiscuous mode and MLD snooping
+// must be disabled on the switches that share the layer-2 link relative to
+// the uplink interface. Note that MLD snooping must not be disabled entirely on
+// each switch, but only on the corresponding vlan.
+
+// called by pfil_run_hooks() @ ip6_input.c:ip_input()
+
+#ifdef PFIL_VERSION
+pfil_return_t packet(struct mbuf **packet_mp, struct ifnet *packet_ifnet,
+                     const int packet_dir, void *packet_arg, struct inpcb *packet_inpcb) {
+#else
+int packet(void *packet_arg, struct mbuf **packet_mp, struct ifnet *packet_ifnet,
+           const int packet_dir, struct inpcb *packet_inpcb) {
+#endif
+  struct mbuf *m = NULL, *mreply = NULL;
+  struct ip6_hdr *ip6, *ip6reply;
+  struct icmp6_hdr *icmp6;
+  struct in6_addr srcaddr, dstaddr;
+  int output_flags = 0;
+  int maxlen, ret, i;
+
+#ifdef DEBUG_NDPROXY
+  // when debuging, increment counter of received packets from the uplink interface
+  ndproxy_conf_count = ++ndproxy_conf_count < 0 ? 1 : ndproxy_conf_count;
+#endif
+  
+  if (packet_mp == NULL) {
+    printf("NDPROXY ERROR: no mbuf\n");
+    return 0;
+  }
+  m = *packet_mp;
+
+  // locate start of IPv6 header data
+  ip6 = mtod(m, struct ip6_hdr *);
+  struct in6_addr ip6_src = ip6->ip6_src;
+
+  // handle only packets originating from the uplink interface
+  if (strcmp(if_name(packet_ifnet), ndproxy_conf_str_uplink_interface)) {
+#ifdef DEBUG_NDPROXY
+    printf("NDPROXY DEBUG: packets from uplink interface: %s - %d\n", if_name(packet_ifnet), ndproxy_conf_count);
+#endif
+    return 0;
+  }
+
+  // Handle only packets originating from one of the uplink router addresses.
+  // Note that different source addresses can be choosen from the same uplink router, depending on the packet
+  // that triggered the address resolution process or depending on other external factors.
+  // Here are some cases when it can happen:
+  // - the uplink router may have multiple interfaces;
+  // - there may be multiple uplink routers;
+  // - many routers choose to use a link-local address when sending neighbor solicitations,
+  //   but when an administrator of such a router, also having a global address assigned on the same link,
+  //   tries to send packets (echo request, for instance) to an on-link destination global address,
+  //   the source address of the echo request packet prompting the solicitation may be global-scoped according
+  //   to the selection algorithm described in RFC-6724. Therefore, the source address of the Neighbor Solicitation
+  //   packet should also be selected in the same global scope, according to RFC-4861 (§7.2.2);
+  // - when the uplink router does not yet know its own address, it must use the unspecified address,
+  //   according to RFC-4861.
+  // So, it can not be assumed that an uplink router will always use the same IPv6 address to send
+  // neighbor solicitations. Every assigned addresses to the downlink interface of the uplink router
+  // should then be declared to ndproxy via sysctl (net.inet6.ndproxyconf_uplink_ipv6_addresses).
+  // Since the unsolicited address can be used by many different nodes, another node than the uplink router could
+  // make use of such a source IP. This is why if such a node exists, the unsolicited address should not be
+  // declared in the net.inet6.ndproxyconf_uplink_ipv6_addresses sysctl configuration parameter.
+  for (i = 0; i < ndproxy_conf_uplink_ipv6_naddresses; i++) {
+#ifdef DEBUG_NDPROXY
+    printf("NDPROXY INFO: compare: ");
+    printf_ip6addr(ndproxy_conf_uplink_ipv6_addresses + i, false);
+    printf(" (uplink router address) with ");
+    printf_ip6addr(&ip6_src, false);
+    printf(" (source address)\n");
+#endif
+    if (IN6_ARE_ADDR_EQUAL(ndproxy_conf_uplink_ipv6_addresses + i, &ip6_src)) break;
+  }
+  if (i == ndproxy_conf_uplink_ipv6_naddresses) {
+#ifdef DEBUG_NDPROXY
+    printf("NDPROXY INFO: not from uplink router - from: ");
+    printf_ip6addr(&ip6_src, false);
+    printf(" - %d\n", ndproxy_conf_count);
+#endif
+    return 0;
+  }
+
+#ifdef DEBUG_NDPROXY
+  printf("NDPROXY DEBUG: got packet from uplink router - %d\n", ndproxy_conf_count);
+#endif
+  
+  // For security reasons, we explicitely reject neighbor solicitation packets containing any extension header:
+  // such a packet is mainly unattended:
+  //   Fragmentation:
+  //     According to RFC-6980, IPv6 fragmentation header is forbidden in all neighbor discovery messages.
+  //   Hop-by-hop header:
+  //     commonly used for jumbograms or for MLD. Should not involve neighbor solicitation packets.
+  //   Destination mobility headers:
+  //     commonly used for mobility, we do not support these headers.
+  //   Routing header:
+  //     commonly used for mobility or source routing, we do not support these headers.
+  //   AH & ESP headers:
+  //     securing the neighbor discovery process is not done with IPsec but with the SEcure Neighbor
+  //     Discovery protocol (RFC-3971). We can not support RFC-3971, since proxifying ND packets is
+  //     some kind of a spoofing process.
+  // look for an ICMPv6 payload and no IPv6 extension header
+  if (ip6->ip6_nxt != IPPROTO_ICMPV6) return 0;
+#ifdef DEBUG_NDPROXY
+  printf("NDPROXY DEBUG: got ICMPv6 from uplink router - %d\n", ndproxy_conf_count);
+#endif
+
+  // locate start of ICMPv6 header data
+  icmp6 = (struct icmp6_hdr *) ((caddr_t) ip6 + sizeof(struct ip6_hdr));
+
+  // check the checksum
+  const u_int16_t sum = icmp6->icmp6_cksum;
+  icmp6->icmp6_cksum = 0;
+  if (sum != in6_cksum(m, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
+                      m->m_len - sizeof(struct ip6_hdr))) {
+    icmp6->icmp6_cksum = sum;
+    printf("NDPROXY ERROR: bad checksum\n");
+    return 0;
+  }
+  icmp6->icmp6_cksum = sum;
+
+  if (icmp6->icmp6_type != ND_NEIGHBOR_SOLICIT || icmp6->icmp6_code) return 0;
+#ifdef DEBUG_NDPROXY
+  printf("NDPROXY DEBUG: got neighbor solicitation from ");
+  printf_ip6addr(&ip6_src, true);
+  printf("\n");
+#endif
+
+  // create a new mbuf to send a neighbor advertisement
+  // ICMPv6 options are rounded up to 8 bytes alignment
+  maxlen = (sizeof(struct ip6_hdr) + sizeof(struct nd_neighbor_advert) +
+           sizeof(struct nd_opt_hdr) + packet_ifnet->if_addrlen + 7) & ~7;
+  if (max_linkhdr + maxlen > MCLBYTES) {
+    printf("NDPROXY ERROR: reply length > MCLBYTES\n");
+    return 0;
+  }
+  if (max_linkhdr + maxlen > MHLEN)
+    mreply = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+  else
+    mreply = m_gethdr(M_NOWAIT, MT_DATA);
+  if (mreply == NULL) {
+    printf("NDPROXY ERROR: no more mbufs (ENOBUFS)\n");
+    return 0;
+  }
+
+  // this is a newly created packet
+  mreply->m_pkthdr.rcvif = NULL;
+
+  // packet content:
+  // IPv6 header + ICMPv6 Neighbor Advertisement including target address + target link-layer ICMPv6 address option
+  mreply->m_pkthdr.len = mreply->m_len = (sizeof(struct ip6_hdr) + sizeof(struct nd_neighbor_advert)
+                                         + sizeof(struct nd_opt_hdr) + packet_ifnet->if_addrlen + 7) & ~7;
+
+  // reserve space for the link-layer header
+  mreply->m_data += max_linkhdr;
+
+  // fill in the destination address we want to reply to
+  struct sockaddr_in6 dst_sa;
+  bzero(&dst_sa, sizeof(struct sockaddr_in6));
+  dst_sa.sin6_family = AF_INET6;
+  dst_sa.sin6_len = sizeof(struct sockaddr_in6);
+  dst_sa.sin6_addr = ip6->ip6_src;
+  if ((ret = in6_setscope(&dst_sa.sin6_addr, packet_ifnet, NULL))) {
+    printf("NDPROXY ERROR: can not set source scope id (err=%d)\n", ret);
+    m_freem(mreply);
+    return 0;
+  }
+
+  // According to RFC-4861 (§7.2.4), "The Target Address of the advertisement is copied from the Target Address
+  // of the solicitation. [...] If the source of the solicitation is the unspecified address, the
+  // node MUST [...] multicast the advertisement to the all-nodes address.".
+  if (!IN6_IS_ADDR_UNSPECIFIED(&ip6_src)) dstaddr = ip6->ip6_src;
+  else {
+    // Check compliance to RFC-4861: "If the IP source address is the unspecified address, the IP
+    // destination address is a solicited-node multicast address.".
+    if (ip6->ip6_dst.s6_addr16[0] == IPV6_ADDR_INT16_MLL &&
+       ip6->ip6_dst.s6_addr32[1] == 0 &&
+       ip6->ip6_dst.s6_addr32[2] == IPV6_ADDR_INT32_ONE &&
+       ip6->ip6_dst.s6_addr8[12] == 0xff) {
+#ifdef DEBUG_NDPROXY
+      printf("NDPROXY DEBUG: unspecified source address and solicited-node multicast destination address\n");
+#endif
+    } else {
+      printf("NDPROXY ERROR: destination address should be a solicited-node multicast address\n");
+      m_freem(mreply);
+      return 0;
+    }
+
+    output_flags |= M_MCAST;
+    dstaddr = in6addr_linklocal_allnodes;
+  }
+  if ((ret = in6_setscope(&dstaddr, packet_ifnet, NULL))) {
+    printf("NDPROXY ERROR: can not set destination scope id (err=%d)\n", ret);
+    m_freem(mreply);
+    return 0;
+  }
+    
+  // first, apply the RFC-3484 default address selection algorithm to get a source address for the advertisement packet.
+#if (__FreeBSD_version < 1100000)
+  ret = in6_selectsrc(&dst_sa, NULL, NULL, NULL, NULL, NULL, &srcaddr);
+#else
+  uint32_t _dst_sa_scopeid;
+  struct in6_addr _dst_sa;
+  in6_splitscope(&dst_sa.sin6_addr, &_dst_sa, &_dst_sa_scopeid);
+  ret = in6_selectsrc_addr(RT_DEFAULT_FIB, &_dst_sa,
+                          _dst_sa_scopeid, packet_ifnet, &srcaddr, NULL);
+#endif
+  if (ret && (ret != EHOSTUNREACH || in6_addrscope(&ip6_src) == IPV6_ADDR_SCOPE_LINKLOCAL)) {
+    printf("NDPROXY ERROR: can not select a source address to reply (err=%d), source scope is %x\n",
+          ret, in6_addrscope(&ip6_src));
+    m_freem(mreply);
+    return 0;
+  }
+  if (ret) {
+    // secondly, try to reply with a link-local address attached to the receiving interface
+    struct in6_ifaddr *llifaddr = in6ifa_ifpforlinklocal(packet_ifnet, 0);
+    if (llifaddr == NULL)
+      printf("NDPROXY WARNING: no link-local address attached to the receiving interface\n");
+    
+#ifdef DEBUG_NDPROXY
+    printf("NDPROXY INFO: no address in requested scope, using a link-local address to reply\n");
+#endif
+    if (llifaddr != NULL) {
+      // use the link-local address
+      srcaddr = (llifaddr->ia_addr).sin6_addr;
+      ifa_free((struct ifaddr *) llifaddr);
+    } else
+      // No link-local address, we may for instance currently be verifying that the link-local stateless
+      // autoconfiguration address is unused.
+      // Then, we temporary use the unspecified address (::).
+      bzero(&srcaddr, sizeof srcaddr);
+    
+    // Since we have no source address in the same scope of the destination address of the request packet,
+    // we can not simply reply to the source address of the request packet.
+    // Then we reply to the link-local all nodes multicast address (ff02::1).
+    output_flags |= M_MCAST;
+    dstaddr = in6addr_linklocal_allnodes;
+    if ((ret = in6_setscope(&dstaddr, packet_ifnet, NULL))) {
+      printf("NDPROXY ERROR: can not set destination scope id (err=%d)\n", ret);
+      m_freem(mreply);
+      return 0;
+    }
+  }
+
+#ifdef DEBUG_NDPROXY
+  printf("NDPROXY DEBUG: source address used to reply: "); printf_ip6addr(&srcaddr, true); printf("\n");  
+#endif
+
+  struct nd_neighbor_solicit *nd_ns = (struct nd_neighbor_solicit *) (ip6 + 1);
+  struct in6_addr nd_ns_target = nd_ns->nd_ns_target;
+
+  // fill in the IPv6 header
+  ip6reply = mtod(mreply, struct ip6_hdr *);
+  ip6reply->ip6_flow = 0;
+  ip6reply->ip6_vfc &= ~IPV6_VERSION_MASK;
+  ip6reply->ip6_vfc |= IPV6_VERSION;
+  ip6reply->ip6_plen = htons((u_short) (mreply->m_len - sizeof(struct ip6_hdr)));
+  ip6reply->ip6_nxt = IPPROTO_ICMPV6;
+  ip6reply->ip6_hlim = 255;
+  ip6reply->ip6_dst = dstaddr;
+  ip6reply->ip6_src = srcaddr;
+
+  // fill in the ICMPv6 neighbor advertisement header
+  struct nd_neighbor_advert *nd_na = (struct nd_neighbor_advert *) (ip6reply + 1);  
+  nd_na->nd_na_type = ND_NEIGHBOR_ADVERT;
+  nd_na->nd_na_code = 0;
+
+  nd_na->nd_na_flags_reserved = 0;
+  // According to RFC-4861 (§7.2.4), "If the source of the solicitation is the unspecified address, the
+  // node MUST set the Solicited flag to zero [...]"
+  if (!IN6_IS_ADDR_UNSPECIFIED(&ip6_src)) nd_na->nd_na_flags_reserved = ND_NA_FLAG_SOLICITED;
+
+  // According to RFC-4861 (§7.2.4), "If the Target Address is either an anycast address or a unicast
+  // address for which the node is providing proxy service, [...] the Override flag SHOULD
+  // be set to zero."
+  // Thus, we do not set the ND_NA_FLAG_OVERRIDE flag in nd_na->nd_na_flags_reserved.
+
+  nd_na->nd_na_flags_reserved |= ND_NA_FLAG_ROUTER;
+
+  // according to RFC-4861 (§7.2.3), the target address can not be a multicast address
+  if (IN6_IS_ADDR_MULTICAST(&nd_ns_target)) {
+    printf("NDPROXY WARNING: rejecting multicast target address\n");
+    m_freem(mreply);
+    return 0;
+  }
+
+  // we send a solicited neighbor advertisement relative to the target contained in the received neighbor solicitation
+  nd_na->nd_na_target = nd_ns->nd_ns_target;
+  struct in6_addr nd_na_target = nd_na->nd_na_target;
+
+  // do not manage packets relative to exception target addresses
+  for (i = 0; i < ndproxy_conf_exception_ipv6_naddresses; i++)
+    if (IN6_ARE_ADDR_EQUAL(ndproxy_conf_exception_ipv6_addresses + i, &nd_na_target)) {
+#ifdef DEBUG_NDPROXY
+      printf("NDPROXY INFO: rejecting target\n");
+#endif
+      m_freem(mreply);
+      return 0;
+    } else {
+#ifdef DEBUG_NDPROXY
+      printf("NDPROXY INFO: accepting target: ");
+      printf_ip6addr(ndproxy_conf_exception_ipv6_addresses + i, false);
+      printf(" - ");
+      printf_ip6addr(&nd_na_target, false);
+      printf("\n");
+#endif
+    }
+
+  // proxy to the downlink router: fill in the target link-layer address option with the MAC downlink router address
+  int optlen = sizeof(struct nd_opt_hdr) + ETHER_ADDR_LEN;
+  struct nd_opt_hdr *nd_opt = (struct nd_opt_hdr *) (nd_na + 1);
+  // roundup to 8 bytes alignment
+  optlen = (optlen + 7) & ~7;
+  bzero((caddr_t) nd_opt, optlen);
+  nd_opt->nd_opt_type = ND_OPT_TARGET_LINKADDR;
+  nd_opt->nd_opt_len = optlen >> 3;
+  bcopy(&ndproxy_conf_downlink_mac_address, (caddr_t) (nd_opt + 1), ETHER_ADDR_LEN);
+#ifdef DEBUG_NDPROXY
+  printf("NDPROXY INFO: mac option: ");
+  printf_macaddr_network_format(&ndproxy_conf_downlink_mac_address);
+  printf("\n");
+#endif
+  
+  // compute outgoing packet checksum
+  nd_na->nd_na_cksum = 0;
+  nd_na->nd_na_cksum = in6_cksum(mreply, IPPROTO_ICMPV6, sizeof(struct ip6_hdr),
+                                mreply->m_len - sizeof(struct ip6_hdr));
+
+#ifdef DEBUG_NDPROXY
+  struct in6_addr ip6reply_ip6_src = ip6reply->ip6_src;
+  struct in6_addr ip6reply_ip6_dst = ip6reply->ip6_dst;
+  printf("NDPROXY DEBUG: src="); printf_ip6addr(&ip6reply_ip6_src, false); printf(" / ");
+  printf("dst="); printf_ip6addr(&ip6reply_ip6_dst, false); printf("\n");
+#endif
+
+  struct ip6_moptions im6o;
+  if (output_flags & M_MCAST) {
+    bzero(&im6o, sizeof im6o);
+    im6o.im6o_multicast_hlim = 255;
+    im6o.im6o_multicast_loop = false;
+    im6o.im6o_multicast_ifp = NULL;
+  }
+  
+  // send router advertisement
+  if ((ret = ip6_output(mreply, NULL, NULL, output_flags, output_flags & M_MCAST ? &im6o : NULL, NULL, NULL))) {
+    printf("NDPROXY DEBUG: can not send packet (err=%d)\n", ret);
+#ifdef DEBUG_NDPROXY
+    kdb_backtrace();
+    return 0;
+#endif
+  } else {
+#ifdef DEBUG_NDPROXY
+    printf("NDPROXY DEBUG: reply sent\n");
+#endif
+  }
+
+#ifndef DEBUG_NDPROXY
+  // when NOT debuging, increment counter for each neighbor advertisement sent
+  ndproxy_conf_count = ++ndproxy_conf_count < 0 ? 1 : ndproxy_conf_count;
+#endif
+
+  // do not process this packet by upper layers to avoid sending another advertissement
+  m_freem(m);
+  *packet_mp = NULL;
+  return 1;
+}
diff --git a/ndpacket.h b/ndpacket.h
new file mode 100644 (file)
index 0000000..ceb8836
--- /dev/null
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2015 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __NDPACKET_H
+#define __NDPACKET_H
+
+#ifdef PFIL_VERSION
+extern pfil_return_t packet(struct mbuf **m, struct ifnet *, int, void *, struct inpcb *);
+#else
+extern int packet(void *, struct mbuf **, struct ifnet *, int, struct inpcb *);
+#endif
+
+#endif
diff --git a/ndparse.c b/ndparse.c
new file mode 100644 (file)
index 0000000..3956865
--- /dev/null
+++ b/ndparse.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 2015-2019 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/socket.h>
+#include <sys/ctype.h>
+#include <net/if.h>
+#include <net/pfil.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet6/scope6_var.h>
+
+#if (__FreeBSD_version < 1100000)
+#include <netinet6/in6_var.h>
+#endif
+
+#include "ndparse.h"
+#include "ndconf.h"
+
+static const char hexdigits[] = "0123456789abcdef";
+static int digit2int(const char digit) {
+  return strchr(hexdigits, digit) - hexdigits;
+}
+
+// This Ethernet address parser only handles the hexadecimal representation made of 6 groups of 2 hexadecimal
+// numbers separated by colons: "XX:XX:XX:XX:XX:XX".
+// Other representations will return -1.
+int parse_mac(char *str, struct ether_addr *retaddr) {
+  if (strlen(str) != MACMAXSIZE) return -1;
+  for (int i = 0; i < 6; i++) {
+    if ((i < 5 && str[3 * i + 2] != ':') || !isxdigit(str[3 * i]) || !isxdigit(str[3 * i + 1])) return -1;
+    retaddr->octet[i] = (digit2int(tolower(str[3 * i])) << 4) + digit2int(tolower(str[3 * i + 1]));
+  }
+  return 0;
+}
+
+// This IPv6 address parser handles any valid textual representation according to RFC-4291 and RFC-5952.
+// Other representations will return -1.
+//
+// note that str input parameter has been modified when the function call returns
+//
+// parse_ipv6(char *str, struct in6_addr *retaddr)
+// parse textual representation of IPv6 addresses
+// str:     input arg
+// retaddr: output arg
+int parse_ipv6(char *str, struct in6_addr *retaddr) {
+  bool compressed_field_found = false;
+  unsigned char *_retaddr = (unsigned char *) retaddr;
+  char *_str = str;
+  char *delim;
+
+  bzero((void *) retaddr, sizeof(struct in6_addr));
+  if (!strlen(str) || strchr(str, ':') == NULL || (str[0] == ':' && str[1] != ':') ||
+      (strlen(str) >= 2 && str[strlen(str) - 1] == ':' && str[strlen(str) - 2] != ':')) return -1;
+
+  // convert transitional to standard textual representation
+  if (strchr(str, '.')) {
+    int ipv4bytes[4];
+    char *curp = strrchr(str, ':');
+    if (curp == NULL) return -1;
+    char *_curp = ++curp;
+    for (int i = 0; i < 4; i++) {
+      char *nextsep = strchr(_curp, '.');
+      if (_curp[0] == '0' || (i < 3 && nextsep == NULL) || (i == 3 && nextsep != NULL)) return -1;
+      if (nextsep != NULL) *nextsep = 0;
+      for (int j = 0; j < strlen(_curp); j++) if (_curp[j] < '0' || _curp[j] > '9') return -1;
+      if (strlen(_curp) > 3) return -1;
+      const long val = strtol(_curp, NULL, 10);
+      if (val < 0 || val > 255) return -1;
+      ipv4bytes[i] = val;
+      _curp = nextsep + 1;
+    }
+    sprintf(curp, "%x%02x:%x%02x", ipv4bytes[0], ipv4bytes[1], ipv4bytes[2], ipv4bytes[3]);
+  }    
+
+  // parse standard textual representation
+  do {
+    if ((delim = strchr(_str, ':')) == _str || (delim == NULL && !strlen(_str))) {
+      if (delim == str) _str++;
+      else if (delim == NULL) return 0;
+      else {
+       if (compressed_field_found == true) return -1;
+       if (delim == str + strlen(str) - 1 && _retaddr != (unsigned char *) (retaddr + 1)) return 0;
+       compressed_field_found = true;
+       _str++;
+       int cnt = 0;
+       for (char *__str = _str; *__str; ) if (*(__str++) == ':') cnt++;
+       unsigned char *__retaddr = - 2 * ++cnt + (unsigned char *) (retaddr + 1);
+       if (__retaddr <= _retaddr) return -1;
+       _retaddr = __retaddr;
+      }
+    } else {
+      char hexnum[4] = "0000";
+      if (delim == NULL) delim = str + strlen(str);
+      if (delim - _str > 4) return -1;
+      for (int i = 0; i < delim - _str; i++)
+       if (!isxdigit(_str[i])) return -1;
+       else hexnum[4 - (delim - _str) + i] = tolower(_str[i]);
+      _str = delim + 1;
+      *(_retaddr++) = (digit2int(hexnum[0]) << 4) + digit2int(hexnum[1]);
+      *(_retaddr++) = (digit2int(hexnum[2]) << 4) + digit2int(hexnum[3]);
+    }
+  } while (_str < str + strlen(str));
+  return 0;
+}
+
+void printf_ip6addr(const struct in6_addr *addrp, const bool clear_scope) {
+  struct in6_addr tmpaddr = *addrp;
+  char addrstr[INET6_ADDRSTRLEN + 1];
+
+  if (clear_scope) in6_clearscope(&tmpaddr);
+  ip6_sprintf(addrstr, &tmpaddr);
+  addrstr[INET6_ADDRSTRLEN] = 0;
+  printf("%s", addrstr);
+}
+
+void printf_ip6addr_network_format(const struct in6_addr *addrp) {
+  struct in6_addr tmpaddr = *addrp;
+
+  in6_clearscope(&tmpaddr);
+
+  //  for (int i = 0; i < 4; i++) printf("addr[%d] = %x\n", i, tmpaddr.__u6_addr.__u6_addr32[i]);
+  for (int i = 0; i < 16; i++) {
+    printf("%02X", ((const unsigned char *) addrp)[i]);
+    if (i%2 && i != 15) printf(":");
+  }
+}
+
+void printf_macaddr_network_format(const struct ether_addr *addrp) {
+  char addrstr[MACMAXSIZE + 1];
+  sprintf(addrstr, "%02X:%02X:%02X:%02X:%02X:%02X", addrp->octet[0], addrp->octet[1], addrp->octet[2], addrp->octet[3], addrp->octet[4], addrp->octet[5]);
+  printf("%s", addrstr);
+}
diff --git a/ndparse.h b/ndparse.h
new file mode 100644 (file)
index 0000000..a75f4c3
--- /dev/null
+++ b/ndparse.h
@@ -0,0 +1,41 @@
+/*-
+ * Copyright (c) 2015 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __NDPARSE_H
+#define __NDPARSE_H
+
+// simple Ethernet address parser
+extern int parse_mac(char *, struct ether_addr *);
+
+// IPv6 address parser compliant to RFC-4291 and RFC-5952
+extern int parse_ipv6(char *, struct in6_addr *);
+
+// some useful functions needed to debug
+extern void printf_ip6addr(const struct in6_addr *, const bool);
+extern void printf_ip6addr_network_format(const struct in6_addr *);
+extern void printf_macaddr_network_format(const struct ether_addr *);
+
+#endif
diff --git a/ndproxy.4 b/ndproxy.4
new file mode 100644 (file)
index 0000000..9729522
--- /dev/null
+++ b/ndproxy.4
@@ -0,0 +1,236 @@
+'\" te
+.\" Copyright (c) 2015 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ndproxy.c 173 2015-03-30 00:10:36Z fenyo $
+.\"
+.Dd May 27, 2017
+.Dt NDPROXY 4
+.Os
+.Sh NAME
+.Nm ndproxy
+.Nd Neighbor Discovery Proxy
+.Sh SYNOPSIS
+ndproxy is a kernel module that implements IPv6 Neighbor Discovery proxying over Ethernet-like access networks, with many options to handle several use-cases.
+.Pp
+ndproxy replies to a neighbor solicitation with a specific neighbor advertisement, in order
+to let the PE uplink router send further packets to a CPE downlink router, that may or may not
+be the same node that run ndproxy.
+.Pp
+The hook-based
+.Xr pfil 9
+framework is used to let ndproxy be invoked for every IPv6 incoming packet, in order to specifically handle and filter neighbor solicitations and reply with appropriate neighbor advertisements.
+.Pp
+ND (Neighbor Discovery) packets are mainly targeted at solicited-node multicast addresses, but ndproxy has no information about the hosts to proxy, then it can not join the corresponding groups. Thus, the interface on which ndproxy listen to solicitations must be put into permanently promiscuous mode: add "promisc" to the
+ifconfig_<interface> variable in
+.Xr rc.conf 5 .
+.Pp
+For the same reason, MLD snooping must be disabled on the switches that share the PE/CPE interconnect (the layer-2 link the listening interface is attached to). Note that MLD snooping must not be disabled entirely on each switch, but only on the corresponding vlan.
+.Pp
+The interface on which ndproxy listen to solicitations only need to be assigned a link-local address. No information about the delegated prefix and no global address are needed on this interface. It is sufficient to add 
+"inet6 -ifdisabled -accept_rtadv auto_linklocal" to the
+ifconfig_<interface>_ipv6 variable in
+.Xr rc.conf 5 .
+.Sh DIFFERENCES WITH NDP
+The target address to proxy must be given when using the
+.Xr ndp 8
+command-line tool with the proxy option. On the contrary, ndproxy does not rely on a list of target addresses to proxy. Thus, RFC-4941 temporary addresses can be proxyfied. For security reasons, many operating systems use a temporary address when establishing outbound connections.
+.Pp
+When using
+.Xr ndp 8
+command-line tool with the proxy option, the proxyfied packets are redirected to the node that run ndp. With ndproxy, the host that run ndp can be used only to redirect packets to another IPv6 internal router, for instance a dedicated router with hardware support of IPv6 routing process.
+.Sh PREFIX SUBNETTING
+Connecting a flat IPv6 network to the Internet is easily done with the RFC-4861 ND protocol. But connecting a subnetted IPv6 prefix is more complicated, depending on the ISP network design choices.
+ndproxy can help subscribers to achieve this goal.
+.Pp
+Here are some protocols or mechanisms the ISP need to support, when the delegated prefix must be subnetted and assigned to multiple links within the subscriber's network.
+For instance, the ISP could learn routes from the subscriber router using an IGP routing protocol, but the ISP and the subscriber must agree with a common routing protocol.
+The ISP could also feed the PE with a static route to the CPE router, but the ISP must be informed about the subscriber router address.
+Finally, the ISP could use the RFC-3633 IPv6 Prefix Options with DHCPv6 to delegate the prefix from its PE router to a requesting subscriber's router: in such a case, the ISP must support the DHCPv6 option.
+.Pp
+ndproxy has been written for subscribers to ISP that do not support any of those mechanisms or protocols, thus not being able to natively subnet their IPv6 delegated prefix.
+.Sh NETWORK DESIGN
+Here is a generic network design using ndproxy to solve such situations:
+.ft CR
+   ______________________                 \\
+  /                      \\                 \\
+  |   ISP core network   |                  \\
+  \\__________ ___________/                   |
+             |                               | ISP network
+             | dsl to subscriber premises    |
+             |                              /
+         +---+---------------------+       /
+         |  PE or triple-play box  |      /
+         +-----------+-------------+
+                     |
+ PE/CPE interconnect | /48 prefix         \\
+         --+---------+---------+--         \\
+           |  no mld snooping  |            \\
+           |                   |             |
+      +----+--------+     +----+-----+       |
+      |     CPE     |     | ndproxy  |       |
+      | IPv6 router |     | BSD host |       |
+      +----+---+----+     +----------+       |
+  subnet1  |   |  subnet2                    | Subscriber
+ ----+-----+- -+-----+------                 | network
+     | /60           | /60                   |
+ +---+------+   +----+-----+                 |
+ |Subscriber|   |Subscriber|                /
+ |  host    |   |  host    |               /
+ +----------+   +----------+              /
+.ft
+.Pp
+Note that many other use-cases can be handled with ndproxy: the BSD host and the CPE router can be the same node, the delegated-prefix length can be /64, the PE router can have several interfaces on the ISP/Subscriber layer-2 boundary, there can be multiple PE routers, etc.
+.Sh PREFIX LENGTH
+Even if the IESG and the IAB first recommended the allocations of /48 prefixes in the general case, for the boundary between the public and the private topology (see RFC-3177), and that some Regional Internet Registries (APNIC, ARIN and RIPE) have subsequently revised the end site assignment policy to encourage the assignment of /56 blocks to end sites, and that RFC-6177 finally recommended giving home sites significantly more than a single /64, in order for home sites to be given multiple subnets, some ISP currently only delegate /64 prefixes.
+.Pp
+In such a case, the subscriber should subnet a RFC-4193 Unique Local IPv6 Unicast Addresses prefix to the internal subnetworks, for internal-to-internal communications. The /64 global prefix should be routed to the only internal subnet in which RFC-4941 temporary addresses are used by hosts when establishing outbound connections. Static routes on the CPE router should be set to let hosts on other internal subnets be able to communicate with the Internet. Using temporary addresses for outbound connections to the Internet must be disabled on hosts on those other internal subnets.
+.Sh IPv6 EXTENSION HEADERS
+For security reasons, ndproxy explicitely rejects neighbor solicitation packets containing any extension header.
+Such a packet is mainly unattended:
+.Bl -hang -width 12n
+.It Sy Fragmentation:
+.Pp
+According to RFC-6980, IPv6 fragmentation header is forbidden in all neighbor discovery messages.
+.It Sy Hop-by-hop header:
+.Pp
+commonly used for jumbograms or for MLD. Should not involve neighbor solicitation packets.
+.It Sy Destination mobility headers:
+.Pp
+commonly used for mobility, ndproxy does not support these headers.
+.It Sy Routing header:
+.Pp
+commonly used for mobility or source routing, ndproxy does not support these headers.
+.It Sy AH & ESP headers:
+.Pp
+securing the neighbor discovery process is not done with IPsec but with the SEcure Neighbor
+Discovery protocol (RFC-3971). ndproxy can not support RFC-3971, since proxifying ND packets is
+some kind of a spoofing process.
+.El
+.Sh EXCEPTION ADDRESSES
+Some neigbhor solicitations sent on the PE/CPE interconnect must not be proxyfied:
+.Bl -enum
+.It
+solicitations sent by other nodes than the PE;
+.It
+solicitations sent by the PE to reach any on-link address (the address filled in the target address option) owned by nodes attached to the PE/CPE interconnect, for instance to reach the CPE, the ndproxy host or other hosts attached to this layer-2 interconnect.
+.El
+.Pp
+The target addresses filled in those solicitations that ndproxy must ignore have to be declared via sysctl (net.inet6.ndproxyconf_exception_ipv6_addresses). This list must contain the link-local and global-scoped unicast and anycast addresses of the CPE, of the ndproxy host and of any other host than the PE attached to the PE/CPE interconnect.
+.Pp
+Failing to maintain this list correctly could lead to badly redirect some packets to the CPE, but with a simple network design, this list can be let empty.
+.Sh UPLINK ROUTER ADDRESSES
+ndproxy only handles packets originating from one of the PE addresses.
+During its address resolution process, different source addresses can be choosen by the PE, depending on the packet
+that triggered the process or depending on other external constraints.
+.Pp
+Here are some cases when it can occur:
+.Pp
+.Bl -enum
+.It
+The PE may have multiple interfaces;
+.It
+There may be multiple PE;
+.It
+Many routers choose to use a link-local address when sending neighbor solicitations,
+but when an administrator of such a router, also having a global address assigned on the same link,
+tries to send packets (echo request, for instance) to an on-link destination global address,
+the source address of the echo request packet prompting the solicitation may be global-scoped according
+to the selection algorithm described in RFC-6724. Therefore, the source address of the Neighbor Solicitation
+packet should also be selected in the same global scope, according to RFC-4861;
+.It
+When the uplink router does not yet know its own address, it must use the unspecified address,
+according to RFC-4861.
+.El
+.Pp
+So, it can not be assumed that an uplink router will always use the same IPv6 address to send
+neighbor solicitations. Each assigned address that can be used as a source address by the PE on its downlink interface
+must then be declared to ndproxy via sysctl (net.inet6.ndproxyconf_uplink_ipv6_addresses).
+.Pp
+ndproxy will only handle packets that come from one of these addresses.
+.Pp
+A special care must be taken about the unsolicited address. It may be used by the PE, then it is part of the list of PE addresses and should therefore be added to the list of PE addresses. Since this address can also be used by other nodes during some initialization steps (for instance when hot-swapping an Ethernet board), another node could use this address to send neighbor solicitations that ndproxy should not handle, because they are not sent by the PE. In fact, this is not a problem because the target address option contained in a solicitation from this other node should be in the exception list. So, adding the unsolicited address in the PE addresses list should be safe.
+.Pp
+Failing to maintain this list correctly could lead the PE not to be able to establish outbound connections to nodes on the PE/CPE interconnect, but if this list contains at least the PE link-local address, IPv6 connectivity should be correctly established between the Internet and the internal subscriber's subnets.
+.Sh CONFIGURATION
+.Pp
+An IPv6 address can be any valid textual representation according to RFC-4291 and RFC-5952 (this means that transitional textual representation is fully supported).
+Other representations will trigger an error event. IPv6 address lists must be formated as series of IPv6 adresses separated by semi-colons.
+.Pp
+The sysctl utility or
+.Xr rc.conf 5
+are used to set ndproxy configuration parameters.
+.Pp
+If you have installed ndproxy as a port or as a package, set the following variables in
+.Xr rc.conf 5
+and load the module at boot time by placing the following line in
+.Xr rc.conf 5 :
+.Bd -literal -offset indent
+ndproxy_enable="YES"
+.Ed
+.Pp
+On the contrary, if you have NOT installed ndproxy as a port or as a package but as a standalone distribution, place the sysctl entries in
+.Xr sysctl.conf 5 and load the module at boot time by placing the following line in
+.Xr loader.conf 5 :
+.Bd -literal -offset indent
+ndproxy_load="YES"
+.Ed
+.Pp
+.Bl -hang -width 12n
+.It Sy net.inet6.ndproxyconf_uplink_interface sysctl entry or ndproxy_uplink_interface rc.conf variable:
+.Pp
+Name of the interface talking to the broadcast multi-access network connecting the PE and CPE routers.
+.Pp
+Example: "vlan2".
+.It Sy net.inet6.ndproxyconf_downlink_mac_address sysctl entry or ndproxy_downlink_mac_address rc.conf variable:
+.Pp
+MAC address of the CPE router. Neighbor advertisements sent by ndproxy will be filled with this address in the target link-layer address option. The format of this parameter is the hexadecimal representation made of 6 groups of 2 hexadecimal
+numbers separated by colons.
+.Pp
+Example: "00:0C:29:B6:43:D5".
+.It Sy net.inet6.ndproxyconf_exception_ipv6_addresses sysctl entry or ndproxy_exception_ipv6_addresses rc.conf variable:
+.Pp
+Target addresses not to proxy. In a simple network design, this list can be let empty. See section "EXCEPTION ADDRESSES".
+.Pp
+Example: "fe80::20d:edff:fe7b:68b7;fe80::222:15ff:fe3b:59a".
+.It Sy net.inet6.ndproxyconf_uplink_ipv6_addresses sysctl entry or ndproxy_uplink_ipv6_addresses rc.conf variable:
+.Pp
+Addresses of the PE. This list should at least contain the PE link-local address. See section "UPLINK ROUTER ADDRESSES".
+.Pp
+Example: "fe80::207:cbff:fe4b:2d20;2a01:e35:8aae:bc60::1;::".
+.Pp
+.It Sy net.inet6.ndproxycount sysctl entry:
+.Pp
+Number of advertisements sent.
+.El
+.Sh SEE ALSO
+.Xr inet6 4 ,
+.Xr rc.conf 5 ,
+.Xr loader.conf 5 ,
+.Xr sysctl.conf 5 ,
+.Xr sysctl 8 ,
+.Xr loader 8 ,
+.Xr pfil 9
+.Sh AUTHOR
+Alexandre Fenyo <alex@fenyo.net> - www.fenyo.net
diff --git a/ndproxy.c b/ndproxy.c
new file mode 100644 (file)
index 0000000..d2bac05
--- /dev/null
+++ b/ndproxy.c
@@ -0,0 +1,356 @@
+/*-
+ * Copyright (c) 2015-2019 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+#include <net/pfil.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#ifdef PFIL_VERSION
+#include <net/vnet.h>
+#include <netinet6/ip6_var.h>
+#endif
+
+#include "ndproxy.h"
+#include "ndconf.h"
+#include "ndparse.h"
+#include "ndpacket.h"
+
+static int hook_added = false;
+
+#ifdef PFIL_VERSION
+
+static pfil_hook_t pfh_hook;
+
+static void register_hook() {
+  struct pfil_hook_args pha;
+  struct pfil_link_args pla;
+
+  if (hook_added) return;
+
+  pha.pa_version = PFIL_VERSION;
+  pha.pa_type = PFIL_TYPE_IP6;
+  pha.pa_flags = PFIL_IN;
+  pha.pa_modname = "ndproxy";
+  pha.pa_ruleset = NULL;
+  pha.pa_rulname = "default-in6";
+  pha.pa_func = packet;
+  pfh_hook = pfil_add_hook(&pha);
+
+  pla.pa_version = PFIL_VERSION;
+  pla.pa_flags = PFIL_IN | PFIL_HEADPTR | PFIL_HOOKPTR;
+  pla.pa_hook = pfh_hook;
+  pla.pa_head = V_inet6_pfil_head;
+  pfil_link(&pla);
+
+  hook_added = true;
+}
+
+static void unregister_hook() {
+  if (!hook_added) return;
+  pfil_remove_hook(pfh_hook);
+}
+
+#else
+
+static struct pfil_head *pfh_inet6 = NULL;
+
+// when module is loaded from /boot/loader.conf, pfh_inet6 is not already initialized,
+// so postpone registration
+static void register_hook() {
+  if (hook_added) return;
+  
+  if (pfh_inet6 == NULL) {
+    if ((pfh_inet6 = pfil_head_get(PFIL_TYPE_AF, AF_INET6)) == NULL) {
+#ifdef DEBUG_NDPROXY
+      uprintf("NDPROXY WARNING: pfil_head_get returned null\n");
+      printf("NDPROXY WARNING: pfil_head_get returned null\n");
+#endif
+      return;
+    }
+  }
+
+  const int ret = pfil_add_hook(packet, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6);
+  if (ret) {
+#ifdef DEBUG_NDPROXY
+    uprintf("NDPROXY WARNING: can not add hook (err=%d)\n", ret);
+    printf("NDPROXY WARNING: can not add hook (err=%d)\n", ret);
+#endif
+    return;
+  }
+  hook_added = true;
+}
+
+static void unregister_hook() {
+  int ret;
+
+  if (hook_added && (ret = pfil_remove_hook(packet, NULL, PFIL_IN | PFIL_WAITOK, pfh_inet6))) {
+#ifdef DEBUG_NDPROXY
+    uprintf("NDPROXY WARNING: can not remove hook (err=%d)\n", ret);
+    printf("NDPROXY WARNING: can not remove hook (err=%d)\n", ret);
+#endif
+  }
+}
+
+#endif
+
+// called when the module is loaded or unloaded
+static int event_handler(struct module *module, const int event, void *arg) {
+  switch (event) {
+  case MOD_LOAD:
+    register_hook();
+#ifdef DEBUG_NDPROXY
+    uprintf("NDPROXY loaded\n");
+    printf("NDPROXY loaded\n");
+#endif
+    return 0;
+    // NOTREACHED
+    break;
+
+  case MOD_UNLOAD:
+    unregister_hook();
+#ifdef DEBUG_NDPROXY
+    uprintf("NDPROXY unloaded\n");
+    printf("NDPROXY unloaded\n");
+#endif
+    return 0;
+    // NOTREACHED
+    break;
+
+  default:
+    return EOPNOTSUPP;
+    // NOTREACHED
+    break;
+  }
+}
+
+// declare module data
+
+static moduledata_t ndproxy_conf = {
+  "ndproxy",     // module name
+  event_handler, // event handler
+  NULL           // extra data
+};
+DECLARE_MODULE(ndproxy, ndproxy_conf, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
+
+// declare sysctl interface used to configure the behaviour of the module
+
+SYSCTL_DECL(_net_inet6);
+
+#define GENERIC_CB_STRING                                            \
+  if (arg1 == NULL) {                                                \
+    printf("NDPROXY ERROR: conf arg is null\n");                     \
+    return EFAULT;                                                   \
+  }                                                                  \
+                                                                     \
+  if (strlen((char *) arg1) > sizeof conf_str - 1) return E2BIG;     \
+                                                                     \
+  strncpy(conf_str, (char *) arg1, sizeof conf_str);                 \
+  conf_str[(sizeof conf_str) - 1] = '\0';                            \
+                                                                     \
+  ret = SYSCTL_OUT(req, conf_str, sizeof conf_str);                  \
+  if (ret || !req->newptr) return ret;                               \
+                                                                     \
+  /* the caller asks to set a new value */                          \
+                                                                     \
+  if ((req->newlen - req->newidx) >= arg2) return EINVAL;            \
+  arg2 = (req->newlen - req->newidx);                                \
+  ret = SYSCTL_IN(req, arg1, arg2);                                  \
+  ((char *)arg1)[arg2] = '\0';                                       \
+  if (ret) return ret;
+
+////////////////////////////////////////////////////////////////////////////////
+// net.inet6.ndproxyconf_uplink_interface
+
+// declare the sysctl node named net.inet6.ndproxyconf_uplink_interface
+SYSCTL_STRING(_net_inet6, OID_AUTO, ndproxyconf_uplink_interface, CTLFLAG_RW, ndproxy_conf_str_uplink_interface, sizeof ndproxy_conf_str_uplink_interface, "uplink interface name");
+
+////////////////////////////////////////////////////////////////////////////////
+// net.inet6.ndproxyconf_{up,down}link_mac_address
+
+// storage string for the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
+#if 0
+// reserved for a future use
+static char ndproxy_conf_str_uplink_mac_address[MACMAXSIZE + 1] = "";
+#endif
+static char ndproxy_conf_str_downlink_mac_address[MACMAXSIZE + 1] = "";
+
+// get or update the value of the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
+static int cb_string_mac_addr(SYSCTL_HANDLER_ARGS, char xconf_str[MACMAXSIZE + 1], struct ether_addr *xconf_val, bool *xconf_isset) {
+  char conf_str[MACMAXSIZE + 1];
+  struct ether_addr _ndproxy_conf_link_mac_address;
+  int ret;
+
+  register_hook();
+
+  GENERIC_CB_STRING;
+
+  if (!strlen(xconf_str)) {
+    *xconf_isset = false;
+    return 0;
+  }
+
+  char *curp = xconf_str;
+  char tmpstr[18];
+  strcpy(tmpstr, curp);
+  ret = parse_mac(tmpstr, &_ndproxy_conf_link_mac_address);
+  if (!ret) {
+    *xconf_isset = true;
+#ifdef DEBUG_NDPROXY
+    printf("NDPROXY INFO: parsed address: [");
+    printf_macaddr_network_format(&_ndproxy_conf_link_mac_address);
+    printf("]\n");
+#endif
+  } else {
+    strncpy(xconf_str, conf_str, sizeof conf_str);
+    xconf_str[sizeof conf_str - 1] = 0;
+    return EINVAL;
+  }
+
+  *xconf_val = _ndproxy_conf_link_mac_address;
+  *xconf_isset = true;
+  return 0;
+}
+
+// get or update the value of the sysctl node named net.inet6.ndproxyconf_downlink_mac_address
+static int cb_string_downlink_mac_addr(SYSCTL_HANDLER_ARGS) {
+  return cb_string_mac_addr(oidp, arg1, arg2, req, ndproxy_conf_str_downlink_mac_address, &ndproxy_conf_downlink_mac_address, &ndproxy_conf_downlink_mac_address_isset);
+}
+
+#if 0
+// reserved for a future use
+// get or update the value of the sysctl node named net.inet6.ndproxyconf_uplink_mac_address
+static int cb_string_uplink_mac_addr(SYSCTL_HANDLER_ARGS) {
+  return cb_string_mac_addr(oidp, arg1, arg2, req, ndproxy_conf_str_uplink_mac_address, &ndproxy_conf_uplink_mac_address, &ndproxy_conf_uplink_mac_address_isset);
+}
+#endif
+
+// declare the sysctl node named net.inet6.ndproxyconf_{up,down}link_mac_address
+// format: NN:NN:NN:NN:NN:NN
+SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_downlink_mac_address, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_downlink_mac_address, sizeof ndproxy_conf_str_downlink_mac_address, cb_string_downlink_mac_addr, "S", "downlink mac adress");
+// the uplink mac address is reserved for a future use when it could be used to filter uplink packets instead of using the uplink ipv6 addresses
+// SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_uplink_mac_address, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_uplink_mac_address, sizeof ndproxy_conf_str_uplink_mac_address, cb_string_uplink_mac_addr, "S", "uplink mac adress");
+
+////////////////////////////////////////////////////////////////////////////////
+// net.inet6.ndproxyconf_exception_ipv6_addresses && net.inet6.ndproxyconf_uplink_ipv6_addresses
+
+// storage string for the sysctl node named net.inet6.ndproxyconf_exception_ipv6_addresses
+static char ndproxy_conf_str_exception_ipv6_addresses[CONF_NEXCEPTIONS_SIZE] = "";
+
+// storage string for the sysctl node named net.inet6.ndproxyconf_uplink_ipv6_addresses
+static char ndproxy_conf_str_uplink_ipv6_addresses[CONF_NUPLINK_SIZE] = "";
+
+// get or update the value of the sysctl node named net.inet6.ndproxyconf_{uplink,exception}_ipv6_addresses
+static int cb_string_list(SYSCTL_HANDLER_ARGS, int nentries_size, int nentries_max, char *ndproxy_conf_str_ipv6_addresses, struct in6_addr *ndproxy_conf_ipv6_addresses, int *ndproxy_conf_ipv6_naddresses) {
+  char conf_str[nentries_size];
+  struct in6_addr _ndproxy_conf_ipv6_addresses[nentries_max];
+  int _ndproxy_conf_ipv6_naddresses = 0;
+  int ret;
+
+  register_hook();
+  
+  GENERIC_CB_STRING;
+
+  if (!strlen(ndproxy_conf_str_ipv6_addresses)) {
+    *ndproxy_conf_ipv6_naddresses = 0;
+    return 0;
+  }
+
+  char *curp = ndproxy_conf_str_ipv6_addresses;
+  char *delim;
+  do {
+    char tmpstr[nentries_size];
+    delim = strchr(curp, ';');
+    if (delim != NULL) {
+      strncpy(tmpstr, curp, delim - curp);
+      tmpstr[delim - curp] = 0;
+    } else strcpy(tmpstr, curp);
+    ret = parse_ipv6(tmpstr, _ndproxy_conf_ipv6_addresses + _ndproxy_conf_ipv6_naddresses);
+    if (!ret) {
+#ifdef DEBUG_NDPROXY
+      printf("NDPROXY INFO: parsed address: [");
+      printf_ip6addr_network_format(_ndproxy_conf_ipv6_addresses + _ndproxy_conf_ipv6_naddresses);
+      printf("]\n");
+#endif
+    } else {
+      strncpy(ndproxy_conf_str_ipv6_addresses, conf_str, nentries_size);
+      ndproxy_conf_str_ipv6_addresses[nentries_size - 1] = 0;
+      return EINVAL;
+    }
+    _ndproxy_conf_ipv6_naddresses++;
+  } while (delim != NULL && (curp = ++delim) < (char *) (ndproxy_conf_str_ipv6_addresses + nentries_size) && _ndproxy_conf_ipv6_naddresses < nentries_max);
+
+  if (delim) {
+      strncpy(ndproxy_conf_str_ipv6_addresses, conf_str, nentries_size);
+      ndproxy_conf_str_ipv6_addresses[nentries_size - 1] = 0;
+      return EINVAL;
+  }
+
+  bcopy(_ndproxy_conf_ipv6_addresses, ndproxy_conf_ipv6_addresses, _ndproxy_conf_ipv6_naddresses * sizeof(struct in6_addr));
+  *ndproxy_conf_ipv6_naddresses = _ndproxy_conf_ipv6_naddresses;
+  
+  return 0;
+}
+
+static int cb_string_list_exception(SYSCTL_HANDLER_ARGS) {
+  return cb_string_list(oidp, arg1, arg2, req, CONF_NEXCEPTIONS_SIZE, CONF_NEXCEPTIONS_MAX, ndproxy_conf_str_exception_ipv6_addresses, ndproxy_conf_exception_ipv6_addresses, &ndproxy_conf_exception_ipv6_naddresses);
+}
+
+static int cb_string_list_uplink(SYSCTL_HANDLER_ARGS) {
+  return cb_string_list(oidp, arg1, arg2, req, CONF_NUPLINK_SIZE, CONF_NUPLINK_MAX, ndproxy_conf_str_uplink_ipv6_addresses, ndproxy_conf_uplink_ipv6_addresses, &ndproxy_conf_uplink_ipv6_naddresses);
+}
+
+// declare the sysctl node named net.inet6.ndproxyconf_exception_ipv6_addresses
+// format: NNNN:NNNN:NNNN:NNNN:NNNN:NNNN:{NNNN:NNNN,XXX.XXX.XXX.XXX};...;...
+SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_exception_ipv6_addresses, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_exception_ipv6_addresses, sizeof ndproxy_conf_str_exception_ipv6_addresses, cb_string_list_exception, "S", "do not proxy this list of IPv6 adresses");
+
+// declare the sysctl node named net.inet6.ndproxyconf_uplink_ipv6_addresses
+// format: NNNN:NNNN:NNNN:NNNN:NNNN:NNNN:{NNNN:NNNN,XXX.XXX.XXX.XXX};...;...
+SYSCTL_OID(_net_inet6, OID_AUTO, ndproxyconf_uplink_ipv6_addresses, CTLTYPE_STRING | CTLFLAG_MPSAFE | CTLFLAG_RW, ndproxy_conf_str_uplink_ipv6_addresses, sizeof ndproxy_conf_str_uplink_ipv6_addresses, cb_string_list_uplink, "S", "uplink router IPv6 adresses");
+
+////////////////////////////////////////////////////////////////////////////////
+// net.inet6.ndproxycount
+
+static int cb_count(SYSCTL_HANDLER_ARGS) {
+  register_hook();
+  
+#ifdef DEBUG_NDPROXY
+    printf("NDPROXY INFO: count\n");
+#endif
+
+    return sysctl_handle_int(oidp, arg1, arg2, req);
+}
+
+SYSCTL_OID(_net_inet6, OID_AUTO, ndproxycount, CTLTYPE_INT | CTLFLAG_MPSAFE | CTLFLAG_RW, &ndproxy_conf_count, 0, cb_count, "I", "fire an event");
diff --git a/ndproxy.h b/ndproxy.h
new file mode 100644 (file)
index 0000000..b37e561
--- /dev/null
+++ b/ndproxy.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2015 Alexandre Fenyo <alex@fenyo.net> - http://www.fenyo.net
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef __NDPROXY_H
+#define __NDPROXY_H
+
+#endif
diff --git a/usr/ports/net/ndproxy/Makefile b/usr/ports/net/ndproxy/Makefile
new file mode 100644 (file)
index 0000000..f8f12ce
--- /dev/null
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+PORTNAME=      ndproxy
+PORTVERSION=   3.3
+CATEGORIES=    net
+
+MAINTAINER=    fbsd.bugzilla@fenyo.net
+COMMENT=       Implementation of IPv6 Neighbor Discovery proxy
+
+LICENSE=       BSD2CLAUSE
+LICENSE_FILE=  ${WRKSRC}/LICENSE
+
+USES=          kmod
+
+SUB_FILES=     pkg-message
+
+USE_RC_SUBR=   ndproxy
+USE_GITHUB=    yes
+GH_ACCOUNT=    AlexandreFenyo
+GH_TAGNAME=    v${PORTVERSION}
+
+# build for a kernel with 'options VIMAGE'
+CFLAGS+=       -DVIMAGE
+
+PLIST_FILES=   ${KMODDIR}/${PORTNAME}.ko man/man4/${PORTNAME}.4.gz
+
+pre-build:
+       (cd ${BUILD_WRKSRC}; ${MAKE} depend)
+
+do-install:
+       ${INSTALL_KLD} ${WRKSRC}/${PORTNAME}.ko ${STAGEDIR}${KMODDIR}
+       ${INSTALL_MAN} ${WRKSRC}/${PORTNAME}.4 ${STAGEDIR}${PREFIX}/man/man4/
+
+.include <bsd.port.mk>
diff --git a/usr/ports/net/ndproxy/distinfo b/usr/ports/net/ndproxy/distinfo
new file mode 100644 (file)
index 0000000..f175206
--- /dev/null
@@ -0,0 +1,3 @@
+TIMESTAMP = 1550595689
+SHA256 (AlexandreFenyo-ndproxy-3.3-v3.3_GH0.tar.gz) = d9d10b519985f8dcde7104f8c74698031ac3caf865f42dd9f67568ea2eff01f2
+SIZE (AlexandreFenyo-ndproxy-3.3-v3.3_GH0.tar.gz) = 10172856
diff --git a/usr/ports/net/ndproxy/files/ndproxy.in b/usr/ports/net/ndproxy/files/ndproxy.in
new file mode 100644 (file)
index 0000000..77d391b
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: ndproxy
+# REQUIRE: NETWORKING sysctl
+# KEYWORD: nojail
+
+. /etc/rc.subr
+
+name="ndproxy"
+rcvar=ndproxy_enable
+start_cmd="ndproxy_start"
+stop_cmd="ndproxy_stop"
+
+ndproxy_start()
+{
+    echo "Starting ndproxy:"
+    sysctl net.inet6.ndproxyconf_uplink_interface > /dev/null 2>&1
+    if [ $? -eq 1 ]; then
+       kldload ndproxy > /dev/null 2>&1
+       if [ $? -eq 1 ]; then
+           echo Failure loading ndproxy.
+           return;
+       fi
+    fi
+
+    sysctl net.inet6.ndproxycount=0
+
+    sysctl net.inet6.ndproxyconf_uplink_interface=${ndproxy_uplink_interface}
+    sysctl net.inet6.ndproxyconf_downlink_mac_address=${ndproxy_downlink_mac_address}
+    sysctl net.inet6.ndproxyconf_exception_ipv6_addresses=${ndproxy_exception_ipv6_addresses}
+    sysctl net.inet6.ndproxyconf_uplink_ipv6_addresses=${ndproxy_uplink_ipv6_addresses}
+
+    if [ -z "${ndproxy_uplink_interface}" ]; then
+       echo "Warning: ndproxy_uplink_interface should be defined in rc.conf (see ndproxy(4))."
+    fi
+
+    if [ -z "${ndproxy_downlink_mac_address}" ]; then
+       echo "Warning: ndproxy_downlink_mac_address should be defined in rc.conf (see ndproxy(4))."
+    fi
+
+    if [ -z "${ndproxy_uplink_ipv6_addresses}" ]; then
+       echo "Warning: ndproxy_uplink_ipv6_addresses should be defined in rc.conf (see ndproxy(4))."
+    fi
+
+    # Note that ndproxy_exception_ipv6_addresses may be left empty.
+    
+    if [ -n "${ndproxy_uplink_interface}" ]; then
+       ifconfig ${ndproxy_uplink_interface} | head -1 | grep PPROMISC > /dev/null
+       if [ $? -eq 1 ]; then
+           echo "Putting interface ${ndproxy_uplink_interface} into permanently promiscuous mode."
+           ifconfig ${ndproxy_uplink_interface} promisc
+       fi
+    fi
+    
+    echo Done.
+}
+
+ndproxy_stop()
+{
+    echo "Stopping ndproxy:"
+
+    sysctl net.inet6.ndproxyconf_uplink_interface > /dev/null 2>&1
+    if [ $? -eq 1 ]; then
+       echo Failure unloading ndproxy.
+    else
+       kldunload ndproxy > /dev/null 2>&1
+       if [ $? -eq 1 ]; then
+           echo Failure unloading ndproxy.
+       else
+           echo Done.
+       fi
+    fi
+}
+
+load_rc_config $name
+run_rc_command "$1"
diff --git a/usr/ports/net/ndproxy/files/pkg-message.in b/usr/ports/net/ndproxy/files/pkg-message.in
new file mode 100644 (file)
index 0000000..4b65b76
--- /dev/null
@@ -0,0 +1,22 @@
+-------------------------------------------------------------------------------
+IMPORTANT! MAKE SURE TO READ THE FOLLOWING!
+
+Please remember to reinstall this port after kernel source update.
+
+ndproxy is configured using four sysctl kernel states. The boot script
+(%%PREFIX%%/etc/rc.d/ndproxy) can set those states using rc.conf variables.
+
+Here is the corresponding rc.conf variable to each sysctl kernel state:
+
+sysctl kernel state                            rc.conf corresponding variable
+-------------------------------------------------------------------------------
+net.inet6.ndproxyconf_uplink_interface         ndproxy_uplink_interface
+net.inet6.ndproxyconf_downlink_mac_address     ndproxy_downlink_mac_address
+net.inet6.ndproxyconf_exception_ipv6_addresses ndproxy_exception_ipv6_addresses
+net.inet6.ndproxyconf_uplink_ipv6_addresses    ndproxy_uplink_ipv6_addresses
+
+The network interface set in ndproxy_uplink_interface will be put into
+permanently promiscuous mode.
+
+IMPORTANT! MAKE SURE TO READ THE ABOVE!
+-------------------------------------------------------------------------------
diff --git a/usr/ports/net/ndproxy/pkg-descr b/usr/ports/net/ndproxy/pkg-descr
new file mode 100644 (file)
index 0000000..694a323
--- /dev/null
@@ -0,0 +1,14 @@
+The ndproxy(4) kernel module implements IPv6 Neighbor Discovery
+proxying with many options to handle several use-cases.
+
+It replies to a neighbor solicitation with a specific neighbor
+advertisement, in order to let the PE uplink router send further
+packets to a CPE downlink router, that may or may not be the same
+node that the one which runs ndproxy.
+
+The main difference with the ndp(8) command-line tool is that, with
+ndproxy(4), the host running ndp can be used only to redirect
+packets to another IPv6 internal router, for instance a dedicated
+one with hardware support of IPv6 routing processes.
+
+WWW: http://www.fenyo.net/newweb/ndproxy.html