pax_global_header00006660000000000000000000000064123617470130014516gustar00rootroot0000000000000052 comment=a0ed6f0dcd9f6866d610caaf1ab5b3c5beb76b41 npd6-1.1.0/000077500000000000000000000000001236174701300123645ustar00rootroot00000000000000npd6-1.1.0/INSTALL000066400000000000000000000006611236174701300134200ustar00rootroot00000000000000Building ======== 1) Simple building from source is straightforward: make distclean or make clean make sudo make install 2) [OPTIONAL!] A debian package can be built using make debian however it may be easier to download a pre-built package from the web-site where you got the source code! The debian build has various toolsets required, but odds-on if you're in to building debian packages you probably have those already... npd6-1.1.0/Makefile000066400000000000000000000063721236174701300140340ustar00rootroot00000000000000# # This software is Copyright 2011 by Sean Groarke # All rights reserved. # # This file is part of npd6. # # npd6 is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # npd6 is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with npd6. If not, see . # # $Id$ # $HeadURL$ VERSION=1.1.0 CC=gcc CFLAGS= -Wall -g -O3 LDFLAGS= SOURCES=main.c icmp6.c util.c ip6.c config.c expintf.c exparser.c OBJECTS=$(SOURCES:.c=.o) HEADERS=includes.h npd6.h EXECUTABLE=npd6 INSTALL_PREFIX=/usr MAN_PREFIX=/usr/share/man DEBIAN=debian/ TARGZ=npd6-$(VERSION) DEV:= -D'BUILDREV="$(VERSION).$(shell svnversion -n .)"' all: $(SOURCES) $(EXECUTABLE) $(EXECUTABLE): $(OBJECTS) $(CC) $(LDFLAGS) $(OBJECTS) -o $@ .c.o: $(CC) $(CFLAGS) $(DEV) -c $< -o $@ clean: rm -rf $(OBJECTS) $(EXECUTABLE) distclean: rm -rf $(OBJECTS) $(EXECUTABLE) rm -rf debian/etc/ rm -rf debian/usr/ rm -rf debian/DEBIAN/ rm -rf debian/npd6/ rm -rf debian/files rm -rf debian/npd6.* rm -f npd6*.deb install: all mkdir -p $(DESTDIR)/etc/init.d/ mkdir -p $(DESTDIR)$(INSTALL_PREFIX)/bin/ mkdir -p $(DESTDIR)$(MAN_PREFIX)/man5/ mkdir -p $(DESTDIR)$(MAN_PREFIX)/man8/ cp etc/npd6 $(DESTDIR)/etc/init.d/npd6 cp etc/npd6.conf.sample $(DESTDIR)/etc/npd6.conf.sample cp npd6 $(DESTDIR)$(INSTALL_PREFIX)/bin/ cp man/npd6.conf.5.gz $(DESTDIR)$(MAN_PREFIX)/man5/ cp man/npd6.8.gz $(DESTDIR)$(MAN_PREFIX)/man8/ ubuntu: all mkdir $(DEBIAN)/DEBIAN/ cp $(DEBIAN)/c* $(DEBIAN)/DEBIAN/ mkdir -p $(DEBIAN)/etc/init.d/ mkdir -p $(DEBIAN)$(INSTALL_PREFIX)/bin/ mkdir -p $(DEBIAN)$(MAN_PREFIX)/man5/ mkdir -p $(DEBIAN)$(MAN_PREFIX)/man8/ cp etc/npd6 $(DEBIAN)/etc/init.d/npd6 cp etc/npd6.conf.sample $(DEBIAN)/etc/npd6.conf.sample cp npd6 $(DEBIAN)$(INSTALL_PREFIX)/bin/ cp man/npd6.conf.5.gz $(DEBIAN)$(MAN_PREFIX)/man5/ cp man/npd6.8.gz $(DEBIAN)$(MAN_PREFIX)/man8/ debuild -S -k93C35BB8 debian: all mkdir $(DEBIAN)/DEBIAN/ cp $(DEBIAN)/c* $(DEBIAN)/DEBIAN/ mkdir -p $(DEBIAN)/etc/init.d/ mkdir -p $(DEBIAN)$(INSTALL_PREFIX)/bin/ mkdir -p $(DEBIAN)$(MAN_PREFIX)/man5/ mkdir -p $(DEBIAN)$(MAN_PREFIX)/man8/ cp etc/npd6 $(DEBIAN)/etc/init.d/npd6 cp etc/npd6.conf.sample $(DEBIAN)/etc/npd6.conf.sample cp npd6 $(DEBIAN)$(INSTALL_PREFIX)/bin/ cp man/npd6.conf.5.gz $(DEBIAN)$(MAN_PREFIX)/man5/ cp man/npd6.8.gz $(DEBIAN)$(MAN_PREFIX)/man8/ debuild -I -us -uc debchange: dch -v $(VERSION) $(change) debrelease: dch --release -M targz: mkdir ../$(TARGZ) mkdir ../$(TARGZ)/man mkdir ../$(TARGZ)/etc mkdir ../$(TARGZ)/debian cp man/* ../$(TARGZ)/man cp etc/* ../$(TARGZ)/etc cp *.c ../$(TARGZ)/ cp *.h ../$(TARGZ)/ cp INSTALL ../$(TARGZ) cp debian/c* ../$(TARGZ)/debian cp debian/rules ../$(TARGZ)/debian cp Makefile ../$(TARGZ)/ tar -cvf ../$(TARGZ).tar ../$(TARGZ)/* gzip ../$(TARGZ).tar rm -r ../$(TARGZ)/ npd6-1.1.0/config.c000066400000000000000000000402561236174701300140040ustar00rootroot00000000000000/* * This software is Copyright 2011 by Sean Groarke * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ #include "includes.h" #include "npd6.h" #include "npd6config.h" #include "expintf.h" //******************************************************* // Take supplied filename and open it, then parse the contents. int readConfig(char *configFileName) { char linein[256]; int len; const char delimiters[] = "="; char *lefttoken, *righttoken; char *cp; unsigned int prefixCount=0; // Temp count of prefixes, to match interfaces // Used if building white/blacklist struct in6_addr listEntry; unsigned int check; // For prefix manipulation: char prefixaddrstr[INET6_ADDRSTRLEN]; struct in6_addr prefixaddr; int prefixaddrlen, masklen=0; char interfacestr[INTERFACE_STRLEN]; char *slashMarker; int approxInterfaces = 0; // Ensure global set correctly interfaceCount = 0; pollErrorLimit = 10; // Vaguely sensible default if ((configFileFD = fopen(configFileName, "r")) == NULL) { fprintf(stderr, "Can't open %s: %s\n", configFileName, strerror(errno)); flog(LOG_ERR, "Can't open config file %s: %s", configFileName, strerror(errno)); return (-1); } // First of we need to whip through and count the number of "interface" strings to // give us a worst-case/maximum size for the master data structures. // There will almost always be a few extra instances of this string in the file // and hence this value will typically be slightly bigger than required. // However the amount of memory so "wasted" is pretty tiny, so I really don't care. do { if (fgets(linein, 128, configFileFD) == NULL) break; // Quick check for the presence of the string "interface" if ( strstr( linein, configStrs[NPD6INTERFACE]) ) approxInterfaces++; } while (1); flog(LOG_DEBUG2, "Sizing master interface data structure for %d interfaces.", approxInterfaces); interfaces = calloc( approxInterfaces, sizeof(struct npd6Interface) ); if (interfaces == NULL ) { flog(LOG_ERR, "calloc failed - Terminating"); return (-1); } flog(LOG_DEBUG2, "calloced %d bytes for the master interfaces data-structure", approxInterfaces * sizeof( struct npd6Interface) ); // Go back to the start of the file rewind(configFileFD); // This is real simple config file parsing... do { int strToken, strIdx; len = 0; if (fgets(linein, 128, configFileFD) == NULL) break; // Tidy it up stripwhitespace(linein); // Special mega-hacky thing for blank lines: len = strlen(linein); if (len==0) { len=1; continue; } // Tokenize cp = strdupa(linein); lefttoken = strtok(cp, delimiters); righttoken = strtok(NULL, delimiters); if ( (lefttoken == NULL) || (righttoken == NULL) ) { continue; } // Match token strIdx = -1; for(strToken = 0; strToken < CONFIGTOTAL; strToken++) { if( !strcmp(lefttoken, configStrs[strToken]) ) { strIdx = strToken; // Matched, so drop to next step break; } } flog(LOG_DEBUG2, "Matched config item index: %d", strIdx); // If config params are being added, it should only be required // to update the strings in npd6config.h and then insert a // case XXXXXXX: here with self-contained code inside. switch (strIdx) { case NOMATCH: flog(LOG_DEBUG2, "Found noise in config file. Skipping."); continue; case NPD6PREFIX: strncpy( prefixaddrstr, righttoken, sizeof(prefixaddrstr)); flog(LOG_DEBUG, "Raw prefix: %s", prefixaddrstr); // The prefix may be optionally specified with a mask. // e.g. 1:2:3:: or 1:2:3::/12 slashMarker = strchr( prefixaddrstr, '/'); masklen = NOMASK; if (slashMarker != NULL) { // We found a mask marker masklen = atoi(slashMarker + 1); // Re-terminate prefix flog(LOG_DEBUG2, "Pre: %s", prefixaddrstr); slashMarker[0] = '\0'; flog(LOG_DEBUG2, "Post: %s", prefixaddrstr); } // We need to pad it up and record the length in bits prefixaddrlen = prefixset(prefixaddrstr); flog(LOG_INFO, "Padded prefix: %s, length = %d", prefixaddrstr, prefixaddrlen); if ( prefixaddrlen <= 0 ) { flog(LOG_ERR, "Invalid prefix."); return 1; } // If no mask specified, assume a default value if ( masklen == NOMASK ) { flog(LOG_INFO, "No mask specified. Assuming mask length %d", prefixaddrlen); masklen = prefixaddrlen; } else { flog(LOG_INFO, "Mask length specified: %d", masklen); } // If specified mask length at odds with the prefix itself, flag it // i.e. if the mask specified is not on a 16-bit boundary. Quite legal, but likely // not common if ( masklen != prefixaddrlen ) { flog(LOG_INFO, "Mask of %d correct? Prefix looked like %d. Continuing with your value (%d)", masklen, prefixaddrlen, masklen); } // Build a binary image of it build_addr(prefixaddrstr, &prefixaddr); // Store it interfaces[prefixCount].prefix = prefixaddr; strncpy( interfaces[prefixCount].prefixStr, prefixaddrstr, sizeof(interfaces[prefixCount].prefixStr) ); interfaces[prefixCount].prefixLen = masklen; prefixCount++; break; case NPD6INTERFACE: if ( strlen( righttoken) > INTERFACE_STRLEN ) { flog(LOG_ERR, "Invalid length interface name"); return 1; } strncpy( interfacestr, righttoken, sizeof(interfacestr)); flog(LOG_INFO, "Supplied interface is %s", interfacestr); // Store it strncpy( interfaces[interfaceCount].nameStr, interfacestr, sizeof(interfaces[interfaceCount].nameStr) ); interfaceCount++; break; case NPD6OPTFLAG: if ( !strcmp( righttoken, SET ) ) { flog(LOG_INFO, "linkOption flag SET"); naLinkOptFlag = 1; } else if ( !strcmp( righttoken, UNSET ) ) { flog(LOG_INFO, "linkOption flag UNSET"); naLinkOptFlag = 0; } else { flog(LOG_ERR, "linkOption flag - Bad value"); return 1; } break; case NPD6LOCALIG: if ( !strcmp( righttoken, SET ) ) { flog(LOG_INFO, "ignoreLocal flag SET"); nsIgnoreLocal = 1; } else if ( !strcmp( righttoken, UNSET ) ) { flog(LOG_INFO, "ignoreLocal flag UNSET"); nsIgnoreLocal = 0; } else { flog(LOG_ERR, "ignoreLocal flag - Bad value"); return 1; } break; case NPD6ROUTERNA: if ( !strcmp( righttoken, SET ) ) { flog(LOG_INFO, "routerNA flag SET"); naRouter = 1; } else if ( !strcmp( righttoken, UNSET ) ) { flog(LOG_INFO, "routerNA flag UNSET"); naRouter = 0; } else { flog(LOG_ERR, "routerNA flag - Bad value"); return 1; } break; case NPD6MAXHOPS: maxHops = -1; maxHops = atoi(righttoken); if ( (maxHops < 0) || (maxHops > MAXMAXHOPS) ) { flog(LOG_ERR, "maxHops - invalid value specified in config."); return 1; } else { flog(LOG_INFO, "maxHops set to %d", maxHops); } break; case NPD6TARGETS: // If we arrive here and the tRoot tree already exists, // then we're re-reading the config and so need to zap // the tRoot data first. if (tRoot != NULL) { tdestroy(tRoot, free); tEntries = 0; } collectTargets = -1; tRoot = NULL; collectTargets = atoi(righttoken); if ( (collectTargets < 0) || (collectTargets > MAXTARGETS) ) { flog(LOG_ERR, "collectTargets - invalid value specified in config."); return 1; } else { flog(LOG_INFO, "collectTargets set to %d", collectTargets); } break; case NPD6LISTTYPE: if ( !strcmp( righttoken, NPD6NONE ) ) { flog(LOG_INFO, "List-type = NONE"); listType = NOLIST; } else if ( !strcmp( righttoken, NPD6BLACK ) ) { flog(LOG_INFO, "List-type = BLACK"); listType = BLACKLIST; } else if( !strcmp( righttoken, NPD6WHITE ) ) { flog(LOG_INFO, "List-type = WHITE"); listType = WHITELIST; } else { flog(LOG_ERR, "List-type = - Setting to NONE"); listType = NOLIST; } break; case NPD6LISTADDR: if (build_addr( righttoken, &listEntry) ) { flog(LOG_DEBUG, "Address %s valid.", righttoken); storeListEntry(&listEntry); } else { flog(LOG_ERR, "Address %s invalid.", righttoken); } break; case NPD6ERRORTH: pollErrorLimit = -1; pollErrorLimit = atoi(righttoken); if ( (pollErrorLimit < 0) ) { flog(LOG_ERR, "pollErrorLimit - invalid -ve value specified in config."); return 1; } else { flog(LOG_INFO, "pollErrorLimit set to %d", pollErrorLimit); } break; case NPD6EXPRADDR: flog(LOG_DEBUG, "Address expression %s added.", linein); storeExpression(linein); break; case NPD6LISTLOG: if ( !strcmp( righttoken, ON ) ) { flog(LOG_INFO, "listlogging set to ON"); listLog = 1; } else if ( !strcmp( righttoken, OFF ) ) { flog(LOG_INFO, "listlogging set to OFF"); listLog = 0; } else { flog(LOG_ERR, "listlogging flag - Bad value"); return 1; } break; case NPD6RALOG: if ( !strcmp( righttoken, ON ) ) { flog(LOG_INFO, "RAlogging set to ON"); ralog = 1; } else if ( !strcmp( righttoken, OFF ) ) { flog(LOG_INFO, "RAlogging set to OFF"); ralog = 0; } else { flog(LOG_ERR, "RAlogging flag - Bad value"); return 1; } break; } } while (len); // Basic check: did we have the same number of interfaces as prefixes? if ( interfaceCount != prefixCount ) { flog(LOG_ERR, "Must have same number of prefixes as interfaces. Interfaces = %d, Prefixes = %d", interfaceCount, prefixCount); return 1; } // Did we have ANY interfaces? if ( interfaceCount < 1) { flog(LOG_ERR, "Must define at least one interface/prefix pair."); return 1; } flog(LOG_DEBUG, "Total interfaces defined: %d", interfaceCount); // Work out the interface indices and link addrs for (check = 0; check < interfaceCount; check ++) { unsigned int interfaceIdx; // Interface index number interfaceIdx = if_nametoindex( interfaces[check].nameStr ); if ( !interfaceIdx ) { flog(LOG_ERR, "Could not get ifIndex for interface %s", interfaces[check].nameStr); return 1; } interfaces[check].index = interfaceIdx; flog(LOG_DEBUG2, "i/f name = %s, i/f index = %d", interfaces[check].nameStr, interfaces[check].index); // Interface's link address if (getLinkaddress( interfaces[check].nameStr, interfaces[check].linkAddr) ) { flog(LOG_ERR, "Failed to match interface %s to a link-level address.", interfaces[check].nameStr ); return 1; } } return 0; } npd6-1.1.0/debian/000077500000000000000000000000001236174701300136065ustar00rootroot00000000000000npd6-1.1.0/debian/changelog000066400000000000000000000036051236174701300154640ustar00rootroot00000000000000npd6 (1.1.0) trusty; urgency=medium * Consolidation into 1.1.0 -- Sean Groarke Wed, 16 Jul 2014 16:13:30 +0200 npd6 (1.0.1) trusty; urgency=medium * Fix for bug 60 -- Sean Groarke Wed, 16 Jul 2014 16:12:30 +0200 npd6 (1.0.1) quantal; urgency=low * Massage into Launchpad-suitable form -- Sean Groarke Fri, 04 Jan 2013 15:47:20 +0100 npd6 (1.0.0) quantal; urgency=low * Merge DEV to TRUNK - Lots of changes and fixes. * Version bump to 1.0.0 -- Sean Groarke Thu, 03 Jan 2013 10:03:18 +0100 npd6 (0.9.99) quantal; urgency=low * BETA release! Use with caution! * Bug 48. Allow use of ANY prefix. * Bug 49. No limits interface numbers. * Bug 50. Masks on the prefixes. * Bug 51 * Bug 51 and 53. Substantial changes to building and packaging. * Many minor fixes and debug changes. * Sample config file updated. * MAN pages updated. -- Sean Groarke Wed, 02 Jan 2013 16:17:11 +0100 npd6 (0.7.0) precise; urgency=low * Multi-interface support. * Minor bug fixes and doc changes.. -- Sean Groarke Mon, 14 Nov 2012 07:00:00 +0200 npd6 (0.6.0) precise; urgency=low * Bug fix for issue 42. * Feature fix for issue 43. -- Sean Groarke Mon, 15 Oct 2012 07:00:00 +0200 npd6 (0.5.2) precise; urgency=low * Bug fixes for issue 38 & 40. -- Sean Groarke Fri, 12 Oct 2012 07:00:00 +0200 npd6 (0.5.1) unstable; urgency=low * Packaging tidy-up. -- Sean Groarke Wed, 03 Oct 2012 10:37:23 +0200 npd6 (0.5.0) precise; urgency=low * Bump version -- Craig Miskell Mon, 27 Aug 2012 08:44:59 +1200 npd6 (0.4.6) precise; urgency=low * Initial package. -- Sean Groarke Mon, 21 May 2012 14:30:48 +0200 npd6-1.1.0/debian/compat000066400000000000000000000000021236174701300150040ustar00rootroot000000000000008 npd6-1.1.0/debian/control000066400000000000000000000007001236174701300152060ustar00rootroot00000000000000Source: npd6 Section: net Priority: optional Maintainer: Sean Groarke Build-Depends: debhelper (>= 8.0.0), debhelper (>= 6) Standards-Version: 3.9.3 Homepage: https://code.google.com/p/npd6/ Package: npd6 Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends}, libc6 (>=2.4) Description: IPv6 neighbor proxy daemon Provides a proxy service to respond to neighbor solicitations requests for a pre-defined IPv6 subnet. npd6-1.1.0/debian/copyright000066400000000000000000000016661236174701300155520ustar00rootroot00000000000000Format: http://dep.debian.net/deps/dep5 Upstream-Name: npd6 Source: Files: debian/* Copyright: 2012 Sean Groarke License: GPL-3.0+ This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. . This package is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. . You should have received a copy of the GNU General Public License along with this program. If not, see . . On Debian systems, the complete text of the GNU General Public License version 3 can be found in "/usr/share/common-licenses/GPL-3". npd6-1.1.0/debian/rules000077500000000000000000000007741236174701300146760ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 #export DH_OPTIONS=-v #-$(MAKE) distclean override_dh_usrlocal: %: dh $@ npd6-1.1.0/etc/000077500000000000000000000000001236174701300131375ustar00rootroot00000000000000npd6-1.1.0/etc/npd6000077500000000000000000000042001236174701300137300ustar00rootroot00000000000000#! /bin/sh ### BEGIN INIT INFO # Provides: npd6 # Required-Start: $remote_fs $syslog $network # Required-Stop: $remote_fs $syslog $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 ### END INIT INFO # Author: Sean Groarke, sgroarke@gmail.com> # $HeadURL$ # $Id$ # Do NOT "set -e" PATH=/sbin:/usr/sbin:/bin:/usr/bin DESC="npd6 - Neighbor proxy Daemon IPv6" NAME=npd6 DAEMON=/usr/bin/$NAME # Check man page or --help to get more optional arguments #DAEMON_ARGS="--debug -l /var/log/npd6.log" DAEMON_ARGS="" PIDFILE=/var/run/$NAME.pid SCRIPTNAME=/etc/init.d/$NAME # Exit if the package is not installed [ -x "$DAEMON" ] || exit 0 # Read configuration variable file if it is present [ -r /etc/default/$NAME ] && . /etc/default/$NAME # Load the VERBOSE setting and other rcS variables . /lib/init/vars.sh # Define LSB log_* functions. # Depend on lsb-base (>= 3.0-6) to ensure that this file is present. . /lib/lsb/init-functions do_reload() { # # If the daemon can reload its configuration without # restarting (for example, when it is sent a SIGHUP), # then implement that here. # log_daemon_msg "Doing a daemon reload - USR1" start-stop-daemon --stop --signal USR1 --quiet --pidfile $PIDFILE --name $NAME return 0 } case "$1" in start) [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" #do_start start-stop-daemon --start --exec $DAEMON -- $DAEMON_ARGS case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; stop) [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" #do_stop start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --name $NAME case "$?" in 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; esac ;; status) status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? ;; restart|force-reload) log_daemon_msg "Restarting $DESC" "$NAME" start-stop-daemon --stop --retry=TERM/30/KILL/5 --exec $DAEMON start-stop-daemon --start --exec $DAEMON -- $DAEMON_ARGS ;; *) echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2 exit 3 ;; esac : npd6-1.1.0/etc/npd6.conf.sample000066400000000000000000000063651236174701300161470ustar00rootroot00000000000000// npd6 config file // The prefix portion that we will match on //prefix=2a01:0123:4567:89aa: prefix=2222:1111:2222:3333: // Which interface are we monitoring and using? interface = eth0 // Note that an unlimited number of prefix/interface // pairs can be used. Also note that the prefix can be set to // 0::/0 which in effect matches anything at all. // Router Advertisement Logging // Log and decode options in received Router Advertisements // If 'on', key Router Advertisement prefix-related info will be logged at INFO level // Will also show the src addr of the probable next-hop router to use // If 'on', at DEBUG2 level will log a lot more obscure RA-related stuff too. ralogging = off // **CHECK THE MAN PAGES FOR MORE! ** //*************************************************** //*************** Black/Whitelisting **************** // If 'none' (the default) any NS matching the prefix gets a reply. // If 'black', then any NS *except* those entires given, will // be answered by a Neighbor Advertisement (if the prefix matches) // If 'white', then *only* NS with a target of one of the // entries will be answered by a Neighbor Advertisement (if the // prefix matches) // // Most folks will want the default, at least to get things going. listtype = none // listtype = black // listtype = white //addrlist = 2a01:0123:4567:89aa:aaaa:bbbb:cccc:dddd //addrlist = 2a01:123:4567:89aa:dead:beef:dead:beef //. //. //. (add as many addrlist entires as desired) // Format: must be a 128-bit address, but all formats // accepted, e.g. 2a01::22, 2a01::0022, etc. // Pattern matching is also supported, via use of // exprlist = // *Please* check the man page and/or web site for more details // on this. It's very powerful but needs a little thought first! It's // not just simple regexps. // If we're using black or white lists, this controls whether to // log matches or not (if we're using debug mode, then they get // logged anyway) listlogging = off //************************************************** // Options below this line are normally not changed! // Do so at your own risk... No, really: do not change // these unless you have a precise understanding of what // they do. They can break things in interesting ways.... // // (Default: 100) Collect target data up to this many targets. // Data can be subsequently log-dumped via a USR2. // 0 to disable data collection. collectTargets = 100 // (Default: false) Set to 'false' to disable target link-layer option // on replies to unicasted NS linkOption = false // (Default: true) If we rx NS for the local interface // we ignore it and let the kernel reply itself ignoreLocal = true // (Default: true) Normally outbound NAs should have ROUTER // flag set. routerNA = true // (Default: 255) Normally set to max of 255. If changed, // NAs may be silently ignored! Best left as-is... maxHops = 255 // (Default: 20) Set to 0 to disable this threshold completely. // If more than this number of consecutive poll errors occur // terminate the process. e.g. an interface goes away permanently. // Advice: Don't change this one unless you really understand why...!! pollErrorLimit = 20 // $HeadURL: https://npd6.googlecode.com/svn/trunk/etc/npd6.conf $ // $Id: npd6.conf 98 2012-07-16 07:37:02Z sgroarke $ npd6-1.1.0/exparser.c000066400000000000000000000360041236174701300143640ustar00rootroot00000000000000/***************************************************************************** Expression Parser Copyright (c) 2010,2011,2012 RoyalAnarchy.com, All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ // // Original algorithm/code from Bjarne Stroustrup (bs@research.att.com) // The original published work appears to be free of any copy restrictions. // http://www2.research.att.com/~bs/dc_command_line.c // // Revision History: // - Modified to use unsigned long long instead of double // - Added: %, >>, <<, ~, ==, !=, >=, <=, >, <, &&, ||, ! // - Added: abs() // - Added: mask() // - Added: network to/from unsigned long long conversions // // program: // END // END is end-of-input // expr_list END // // expr_list: // expression PRINT // PRINT is semicolon // expression PRINT expr_list // // expression: // expression + term // expression - term // term // // term: // term / primary // term * primary // term & primary // term | primary // term % primary // term >> primary // term << primary // term ^ primary // term > primary // term >= primary // term < primary // term <= primary // term == primary // term != primary // term && primary // term || primary // primary // // primary: // NUMBER // NAME // NAME = expression // - primary // ~ primary // ! primary // ( expression ) // // Notes: // - This code has only been tested on LITTLE_ENDIAN (Intel) machines. // - If you get unexpected results, consider using more parenthesis. // //***************************************************************************** #define PARSER_LIBRARY_VERSION "0.9.10" #include #include #include #include #include #include #include #include "exparser.h" static unsigned long long s_expression(exp_pstat_t* pstat, int get); static Token_value_t s_get_token(exp_pstat_t* pstat); char* exp_get_library_version(void) { return PARSER_LIBRARY_VERSION; } unsigned long long exp_get_mapped_value(exp_pstat_t* pstat, char* name) { int i; for(i=0; imap[i].name[0] != 0) { if((strcmp(name, pstat->map[i].name)) == 0) { return pstat->map[i].value; } } } return 0; } int exp_set_mapped_value(exp_pstat_t* pstat, char* name, unsigned long long value) { int i; for(i=0; imap[i].name[0] != 0) { if((strcmp(name, pstat->map[i].name)) == 0) { pstat->map[i].value = value; return 0; } } } for(i=0; imap[i].name[0] == 0) { strcpy(pstat->map[i].name, name); pstat->map[i].value = value; return 0; } } return -1; } static unsigned long long s_function_abs(exp_pstat_t* pstat) { unsigned long long v = 0; long long sv = 0; if(s_get_token(pstat) != LP) { fprintf(stderr, "( expected: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); return 0; } v = s_expression(pstat, 1); if(pstat->curr_tok != RP) { fprintf(stderr, ") expected: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); return 0; } s_get_token(pstat); sv = (long long)v; if(sv < 0) { return -sv; } return v; } static unsigned long long s_function_mask(exp_pstat_t* pstat) { long long startbit = 0; long long endbit = 0; long long tmpbit = 0; unsigned long long value = 0; unsigned char* vp = 0; int byte = 0; int shift = 0; int mask = 0; if(s_get_token(pstat) != LP) { fprintf(stderr, "( expected: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); return 0; } startbit = s_expression(pstat, 1); if(pstat->curr_tok != COMMA) { fprintf(stderr, ", expected: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); return 0; } endbit = s_expression(pstat, 1); if(pstat->curr_tok != RP) { fprintf(stderr, ") expected: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); return 0; } s_get_token(pstat); value = 0; vp = (unsigned char*)&value+7; if(startbit > 63) { startbit = 63; } if(endbit < 0) { endbit = 0; } if(endbit > startbit) { tmpbit = endbit; endbit = startbit; startbit = tmpbit; } vp = (unsigned char*)&value+7; for(byte=7; byte>= 0; byte--) { if((startbit >= (byte*8)) && (startbit < (byte+1)*8)) { shift = (((byte+1)*8)-1) - startbit; mask = ((0xffU >> (shift)) & 0xffU); *vp = mask; } else if((startbit > (byte*8))) { *vp = 0xff; } if((endbit >= (byte*8)) && (endbit < (byte+1)*8)) { shift = (((byte+1)*8)) - endbit; mask = ((0xffU >> (shift)) & 0xffU); *vp &= ~mask; break; } vp--; } return value; } static Token_value_t s_get_token(exp_pstat_t* pstat) { char ch = 0; //Skip white space while(1) { if(*pstat->input == 0) { return pstat->curr_tok = END; } if(!(isspace(*pstat->input))) { break; } pstat->input++; } ch = *pstat->input; switch(ch) { case ';': case '\n': pstat->input++; return pstat->curr_tok = PRINT; case '*': case '/': case '+': case '-': case '(': case ')': case '%': case '^': case '~': case ',': pstat->input++; return pstat->curr_tok = (Token_value_t)ch; case '&': pstat->input++; switch(*pstat->input) { case '&': pstat->input++; return pstat->curr_tok = LAND; default: return pstat->curr_tok = AND; } case '|': pstat->input++; switch(*pstat->input) { case '|': pstat->input++; return pstat->curr_tok = LOR; default: return pstat->curr_tok = OR; } case '!': pstat->input++; switch(*pstat->input) { case '=': pstat->input++; return pstat->curr_tok = CNE; default: return pstat->curr_tok = LNOT; } case '=': pstat->input++; switch(*pstat->input) { case '=': pstat->input++; return pstat->curr_tok = CE; default: return pstat->curr_tok = ASSIGN; } case '>': pstat->input++; switch(*pstat->input) { case '=': pstat->input++; return pstat->curr_tok = CGE; case '>': pstat->input++; return pstat->curr_tok = SR; default: return pstat->curr_tok = CGT; } case '<': pstat->input++; switch(*pstat->input) { case '=': pstat->input++; return pstat->curr_tok = CLE; case '<': pstat->input++; return pstat->curr_tok = SL; default: return pstat->curr_tok = CLT; } case '0' ... '9': pstat->number_value = strtoull(pstat->input, &pstat->input, 0); return pstat->curr_tok = NUMBER; default: if((isalpha(ch)) || (ch == '$') || (ch == '.')) { int i = 0; pstat->string_value[i] = 0; while((*pstat->input != 0) && ((isalnum(*pstat->input)) || (*pstat->input == '$') || (*pstat->input == '.'))) { pstat->string_value[i] = *pstat->input; pstat->input++; i++; } pstat->string_value[i] = 0; return pstat->curr_tok = NAME; } fprintf(stderr, "Bad token: %d(0x%x)", ch, ch); pstat->errors++; pstat->input++; return pstat->curr_tok = PRINT; } } static unsigned long long s_primary(exp_pstat_t* pstat, int get) { unsigned long long v = 0; char string_save[256]; if(get) { s_get_token(pstat); } switch(pstat->curr_tok) { case NUMBER: // Unsigned unsigned long long constant v = pstat->number_value; s_get_token(pstat); return v; case NAME: // abs() function? if((strcasecmp(pstat->string_value, EXP_FUNCTION_ABS)) == 0) { return s_function_abs(pstat); } // mask() function? if((strcasecmp(pstat->string_value, EXP_FUNCTION_MASK)) == 0) { return s_function_mask(pstat); } if(s_get_token(pstat) == ASSIGN) { strcpy(string_save, pstat->string_value); v = s_expression(pstat, 1); exp_set_mapped_value(pstat, string_save, v); } else { v = exp_get_mapped_value(pstat, pstat->string_value); } return v; case MINUS: return -s_primary(pstat, 1); case PLUS: return s_primary(pstat, 1); case NOT: return ~s_primary(pstat, 1); case LNOT: v = s_primary(pstat, 1); if(v) { return 0; } return 1; case LP: v = s_expression(pstat, 1); if(pstat->curr_tok != RP) { fprintf(stderr, ") expected: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); pstat->errors++; return 0; } s_get_token(pstat); return v; default: fprintf(stderr, "primary expected: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); pstat->errors++; return 0; } } static unsigned long long s_term(exp_pstat_t* pstat, int get) { unsigned long long left = s_primary(pstat, get); unsigned long long right = 0; while(1) { switch(pstat->curr_tok) { case AND: left &= s_primary(pstat, 1); break; case OR: left |= s_primary(pstat, 1); break; case MUL: left *= s_primary(pstat, 1); break; case DIV: right = s_primary(pstat, 1); if(right != 0) { left /= right; break; } fprintf(stderr, "Divide by zero: %d(0x%x)", pstat->curr_tok, pstat->curr_tok); pstat->errors++; left = 0; break; case SR: left >>= s_primary(pstat, 1); break; case SL: left <<= s_primary(pstat, 1); break; case MOD: left %= s_primary(pstat, 1); break; case XOR: left ^= s_primary(pstat, 1); break; case CE: right = s_primary(pstat, 1); if(left == right) { left = 1; } else { left = 0; } break; case CNE: right = s_primary(pstat, 1); if(left != right) { left = 1; } else { left = 0; } break; case CGT: right = s_primary(pstat, 1); if(left > right) { left = 1; } else { left = 0; } break; case CLT: right = s_primary(pstat, 1); if(left < right) { left = 1; } else { left = 0; } break; case CGE: right = s_primary(pstat, 1); if(left >= right) { left = 1; } else { left = 0; } break; case CLE: right = s_primary(pstat, 1); if(left <= right) { left = 1; } else { left = 0; } break; case LOR: right = s_primary(pstat, 1); left = left || right; break; case LAND: right = s_primary(pstat, 1); left = left && right; break; default: return left; } } } static unsigned long long s_expression(exp_pstat_t* pstat, int get) { unsigned long long left = s_term(pstat, get); while(1) { switch(pstat->curr_tok) { case PLUS: left += s_term(pstat, 1); break; case MINUS: left -= s_term(pstat, 1); break; default: return left; } } } int exp_parse_expression(exp_pstat_t* pstat, char* expr, unsigned long long* result) { pstat->input = expr; while(*pstat->input) { s_get_token(pstat); if(pstat->curr_tok == END) { break; } if(pstat->curr_tok == PRINT) { continue; } *result = s_expression(pstat, 0); } return 0; } unsigned long long exp_ipv6_prefix_to_ull(struct in6_addr* ipv6) { int offset = 0; unsigned long long temp = 0; unsigned char* dst = (unsigned char*)&temp+7; unsigned char* src = (unsigned char*)ipv6; for(offset=0; offset<8; offset++, dst--, src++) { *dst = *src; } return temp; } unsigned long long exp_ipv6_host_to_ull(struct in6_addr* ipv6) { int offset = 0; unsigned long long temp = 0; unsigned char* dst = (unsigned char*)&temp+7; unsigned char* src = (unsigned char*)ipv6+8; for(offset=0; offset<8; offset++, dst--, src++) { *dst = *src; } return temp; } void exp_pull_hull_to_ipv6(unsigned long long prefix, unsigned long long host, struct in6_addr* ipv6) { int offset = 0; unsigned char* src = (unsigned char*)&prefix; unsigned char* dst = (unsigned char*)ipv6+7; for(offset=0; offset<8; offset++, dst--, src++) { *dst = *src; } src = (unsigned char*)&host; dst = (unsigned char*)ipv6+15; for(offset=0; offset<8; offset++, dst--, src++) { *dst = *src; } return; } unsigned long long exp_ipv4_address_to_ull(struct in_addr* ipv4) { unsigned long long temp = ntohl(ipv4->s_addr); return temp; } void exp_ull_to_ipv4_address(unsigned long long ull, struct in_addr* ipv4) { unsigned long temp = ull & 0xffffffff; temp = htonl(temp); memcpy(ipv4, &temp, 4); return; } void exp_ipv6_string_to_addr(char* ipv6_str, struct in6_addr* ipv6) { memset(ipv6, 0, sizeof(struct in6_addr)); inet_pton(AF_INET6, ipv6_str, ipv6); } void exp_ipv6_addr_to_string(struct in6_addr* ipv6, char* ipv6_str, int length) { memset(ipv6_str, 0, length); inet_ntop(AF_INET6, ipv6, ipv6_str, (socklen_t)length); } void exp_ipv4_string_to_addr(char* ipv4_str, struct in_addr* ipv4) { memset(ipv4, 0, sizeof(struct in_addr)); inet_pton(AF_INET, ipv4_str, ipv4); } void exp_ipv4_addr_to_string(struct in_addr* ipv4, char* ipv4_str, int length) { memset(ipv4_str, 0, length); inet_ntop(AF_INET, ipv4, ipv4_str, (socklen_t)length); } npd6-1.1.0/exparser.h000066400000000000000000000065521236174701300143760ustar00rootroot00000000000000/* Expression Parser Copyright (c) 2010,2011,2012 RoyalAnarchy.com, All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _EXPARSER_LIB_H #define _EXPARSER_LIB_H 1 #ifdef __cplusplus extern "C" { #endif #define EXP_MAX_MAPPED_SYMBOLS (32) #define EXP_ARITHMETIC (0x000) #define EXP_LOGICAL (0x100) #define EXP_COMPARE (0x200) #define EXP_RELATIONAL1 (0x400) #define EXP_RELATIONAL2 (0x800) #define EXP_FUNCTION_ABS "abs" #define EXP_FUNCTION_MASK "mask" typedef enum Token_value { NAME, NUMBER, END, PLUS='+', MINUS='-', MUL='*', DIV='/', NOT='~', AND='&', OR='|', SR='>', SL='<', MOD='%', XOR='^', PRINT=';', ASSIGN='=', LP='(', RP=')', COMMA=',', LNOT=EXP_LOGICAL|'!', LOR=EXP_LOGICAL|'|', LAND=EXP_LOGICAL|'&', CNE=EXP_COMPARE|'!', CE=EXP_COMPARE|'=', CGT=EXP_RELATIONAL1|'>', CLT=EXP_RELATIONAL1|'<', CGE=EXP_RELATIONAL2|'>', CLE=EXP_RELATIONAL2|'<', } Token_value_t; typedef struct _exp_parse_map { char name[32]; unsigned long long value; } exp_parse_map_t; typedef struct _exp_pstat { int errors; unsigned long long number_value; char string_value[256]; Token_value_t curr_tok; char* input; exp_parse_map_t map[EXP_MAX_MAPPED_SYMBOLS]; } exp_pstat_t; char* exp_get_library_version(); int exp_parse_expression(exp_pstat_t* pstat, char* expr, unsigned long long* result); int exp_set_mapped_value(exp_pstat_t* pstat, char* name, unsigned long long value); unsigned long long exp_ipv6_prefix_to_ull(struct in6_addr* ipv6); unsigned long long exp_ipv6_host_to_ull(struct in6_addr* ipv6); void exp_pull_hull_to_ipv6(unsigned long long prefix, unsigned long long host, struct in6_addr* ipv6); unsigned long long exp_ipv4_address_to_ull(struct in_addr* ipv4); void exp_ull_to_ipv4_address(unsigned long long ull, struct in_addr* ipv4); void exp_ipv6_string_to_addr(char* ipv6_str, struct in6_addr* ipv6); void exp_ipv6_addr_to_string(struct in6_addr* ipv6, char* ipv6_str, int length); void exp_ipv4_string_to_addr(char* ipv4_str, struct in_addr* ipv4); void exp_ipv4_addr_to_string(struct in_addr* ipv4, char* ipv4_str, int length); #ifdef __cplusplus } #endif #endif // !_EXPARSER_LIB_H npd6-1.1.0/expintf.c000066400000000000000000000026451236174701300142140ustar00rootroot00000000000000// Interface to the expression parser #include #include #include #include "expintf.h" #include "exparser.h" #define MAX_EXPRESSION_LENGTH 128 #define MAX_EXPRESSION_ENTRIES 64 static int sExpression = 0; static char sExpressions[MAX_EXPRESSION_ENTRIES][MAX_EXPRESSION_LENGTH]; int storeExpression(char* expression) { if(sExpression >= MAX_EXPRESSION_ENTRIES) { return -1; } strncpy(&sExpressions[sExpression][0], expression+sizeof("exprlist=")-1, MAX_EXPRESSION_LENGTH); sExpressions[sExpression][MAX_EXPRESSION_LENGTH-1] = 0; sExpression++; return 0; } int compareExpression(struct in6_addr* ipv6) { int expression = 0; unsigned long long prefix = 0; unsigned long long host = 0; unsigned long long result = 0; exp_pstat_t pstat; memset(&pstat, 0, sizeof(exp_pstat_t)); // Convert the IPv6 address into two 64-bit values prefix = exp_ipv6_prefix_to_ull(ipv6); host = exp_ipv6_host_to_ull(ipv6); // Preload variables with the 64-bit values exp_set_mapped_value(&pstat, "PREFIX", prefix); exp_set_mapped_value(&pstat, "HOST", host); // Compare each compression to the values for(expression=0; expression * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ #include "includes.h" #include "npd6.h" #include /***************************************************************************** * open_packet_socket * Opens the packet-level socket, for incoming traffic, * and sets up the appropriate BSD PF. * * Inputs: * Index of the interface we're opening it for. * * Outputs: * none * * Return: * int sock on success, otherwise -1 * */ int open_packet_socket(int ifIndex) { int sock, err; struct sock_fprog fprog; struct sockaddr_ll lladdr; static const struct sock_filter filter[] = { BPF_STMT(BPF_LD|BPF_B|BPF_ABS, ETH_HLEN + sizeof(struct ip6_hdr) + offsetof(struct icmp6_hdr, icmp6_type)), BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ND_NEIGHBOR_SOLICIT, 1, 0), BPF_STMT(BPF_RET|BPF_K, 0), BPF_STMT(BPF_RET|BPF_K, 0xffffffff), }; fprog.filter = (struct sock_filter *)filter; fprog.len = sizeof filter / sizeof filter[0]; sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IPV6) ); if (sock < 0) { flog(LOG_ERR, "Can't create socket(PF_PACKET): %s", strerror(errno)); return (-1); } flog(LOG_DEBUG2, "Created PF_PACKET socket OK."); // Bind the socket to the interface we're interested in memset(&lladdr, 0, sizeof(lladdr)); lladdr.sll_family = PF_PACKET; lladdr.sll_protocol = htons(ETH_P_IPV6); lladdr.sll_ifindex = ifIndex; lladdr.sll_hatype = 0; lladdr.sll_pkttype = 0; lladdr.sll_halen = ETH_ALEN; err=bind(sock, (struct sockaddr *)&lladdr, sizeof(lladdr)); if (err < 0) { flog(LOG_ERR, "packet socket bind to interface %d failed: %s", ifIndex, strerror(errno)); return (-1); } flog(LOG_DEBUG2, "packet socket bind to interface %d OK", ifIndex); // Tie the BSD-PF filter to the socket err = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &fprog, sizeof(fprog)); if (err < 0) { flog(LOG_ERR, "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); return (-1); } flog(LOG_DEBUG2, "setsockopt(SO_ATTACH_FILTER) OK"); return sock; } /***************************************************************************** * open_icmpv6_socket * Opens the ipv6-level socket, for outgoing traffic. * * Inputs: * none * * Outputs: * none * * Return: * int sock on success, otherwise -1 * */ int open_icmpv6_socket(void) { int sock, err, optval = 1; sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6); if (sock < 0) { flog(LOG_ERR, "Can't create socket(AF_INET6): %s", strerror(errno)); return (-1); } err = setsockopt(sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &maxHops, sizeof(maxHops)); if (err < 0) { flog(LOG_ERR, "setsockopt(IPV6_UNICAST_HOPS = %d): %s", maxHops, strerror(errno)); return (-1); } flog(LOG_DEBUG2, "setsockopt(IPV6_UNICAST_HOPS = %d) OK", maxHops); /* Set packet info return via recvmsg() later */ /* Needed to get src addr of received ICMP pkts */ err = setsockopt(sock, IPPROTO_IPV6, IPV6_2292PKTINFO, &optval, sizeof(optval)); if (err < 0) { flog(LOG_ERR, "setsockopt(IPV6_2292PKTINFO): %s", strerror(errno)); return (-1); } flog(LOG_DEBUG2, "setsockopt(IPV6_2292PKTINFO) OK"); return sock; } /***************************************************************************** * get_rx * Called from the dispatcher to pull in the received packet. * * Inputs: * socket is where the data is waiting. * * Outputs: * unsigned char *msg * The data. * * Return: * int length of data received, otherwise -1 on error * * NOTES: * There's a lot of temp data structures needed here, but we really don't * care about them afterwards. Once we've got the raw data and the len * we're good. */ int get_rx(int socket, unsigned char *msg) { struct sockaddr_in6 saddr; struct msghdr mhdr; struct iovec iov; int len; fd_set rfds; FD_ZERO( &rfds ); FD_SET( socket, &rfds ); if( select( socket+1, &rfds, NULL, NULL, NULL ) < 0 ) { if (errno != EINTR) flog(LOG_ERR, "select failed with: %s", strerror(errno)); return -1; } iov.iov_len = MAX_MSG_SIZE; iov.iov_base = (caddr_t) msg; memset(&mhdr, 0, sizeof(mhdr)); mhdr.msg_name = (caddr_t)&saddr; mhdr.msg_namelen = sizeof(saddr); mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = NULL; mhdr.msg_controllen = 0; len = recvmsg(socket, &mhdr, 0); /* Impossible.. But let's not take chances */ if (len > MAX_MSG_SIZE) { flog(LOG_ERR, "Read more data from socket than we can handle. Ignoring it."); return -1; } if (len < 0) { if (errno != EINTR) flog(LOG_ERR, "recvmsg failed with: %s", strerror(errno)); return -1; } return len; } /***************************************************************************** * get_rx_icmp6 * Called from the dispatcher to pull in the received packet. * * Inputs: * socket is where the data is waiting. * * Outputs: * unsigned char *msg * The data. * * Return: * int length of data received, otherwise -1 on error * * NOTES: * There's a lot of temp data structures needed here, but we really don't * care about them afterwards. Once we've got the raw data and the len * we're good. */ int get_rx_icmp6(int socket, unsigned char *msg, struct in6_addr *addr6) { struct sockaddr_in6 saddr; struct msghdr mhdr; struct iovec iov[2]; int len; fd_set rfds; /* For ancillary data */ u_char cbuf[2048]; struct cmsghdr *cm; struct in6_pktinfo *thispkt6; char addr_str[INET6_ADDRSTRLEN]; FD_ZERO( &rfds ); FD_SET( socket, &rfds ); if( select( socket+1, &rfds, NULL, NULL, NULL ) < 0 ) { if (errno != EINTR) flog(LOG_ERR, "select failed with: %s", strerror(errno)); return -1; } iov[0].iov_len = MAX_MSG_SIZE; iov[0].iov_base = (caddr_t) msg; memset(&mhdr, 0, sizeof(mhdr)); mhdr.msg_name = &saddr; mhdr.msg_namelen = sizeof(saddr); mhdr.msg_iov = iov; mhdr.msg_iovlen = 1; mhdr.msg_control = (caddr_t)cbuf; mhdr.msg_controllen = sizeof(cbuf); len = recvmsg(socket, &mhdr, 0); /* Src addr - copy it for caller*/ memcpy(addr6, &(saddr.sin6_addr), sizeof(struct in6_addr)); print_addr(&(saddr.sin6_addr), addr_str); flog( LOG_DEBUG2, "RA received src address: %s", addr_str); /* Walk the chain of cmsg blocks (although unlikely to be more than 1) */ for (cm = CMSG_FIRSTHDR(&mhdr); cm != NULL; cm = CMSG_NXTHDR(&mhdr, cm)) { if (cm->cmsg_level == IPPROTO_IPV6 && cm->cmsg_type == IPV6_2292PKTINFO && cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) { flog(LOG_DEBUG2, "Ancillary data contains in6_pktinfo."); thispkt6 = (struct in6_pktinfo *)CMSG_DATA(cm); // Dst addr char addr_str[INET6_ADDRSTRLEN]; print_addr(&(thispkt6->ipi6_addr), addr_str); flog( LOG_DEBUG2, "RA received dst address: %s", addr_str); } else { flog(LOG_DEBUG2, "Ancillary data was unrecognised."); } } /* Impossible.. But let's not take chances */ if (len > MAX_MSG_SIZE) { flog(LOG_ERR, "Read more data from socket than we can handle. Ignoring it."); return -1; } if (len < 0) { if (errno != EINTR) flog(LOG_ERR, "recvmsg failed with: %s", strerror(errno)); return -1; } return len; } /***************************************************************************** * if_allmulti * Called during startup and shutdown. Set/clear allmulti * as required. * * Inputs: * ifname is interface name * state: 1-> Set (or confirm) flag is enabled * state: 0-> Set flag to unset condition. * * Outputs: * none * * Return: * The previous value of the flag, prior to change. * * Notes: * Miserere mihi peccatori. */ int if_allmulti(char *ifname, unsigned int state) { struct ifreq ifr; int skfd; int current; flog(LOG_DEBUG2, "Requesting that %s be set to state = %d", ifname, state); skfd = socket(AF_INET, SOCK_DGRAM, 0); strncpy(ifr.ifr_name, ifname, IFNAMSIZ); // Get current flags, etc. if (ioctl(skfd, SIOCGIFFLAGS, &ifr) < 0) { flog(LOG_ERR, "Unknown interface: %s, failed err = %s", ifname, strerror(errno)); exit(1); } current = ifr.ifr_flags; if (state) { flog(LOG_DEBUG, "Setting IFFALLMULTI if required."); ifr.ifr_flags |= IFF_ALLMULTI; if (ifr.ifr_flags == current) { // Already set flog(LOG_DEBUG, "Not required, was set at anyway."); goto sinfulexit;; } } else { flog(LOG_DEBUG, "Clearing IFFALLMULTI if required."); ifr.ifr_flags &= ~IFF_ALLMULTI; } if (ioctl(skfd, SIOCSIFFLAGS, &ifr) < 0) { flog(LOG_ERR, "Flag change failed: %s, failed err = %s", ifname, strerror(errno)); exit(1); } sinfulexit: close(skfd); return (current || IFF_ALLMULTI); } /***************************************************************************** * init_sockets * Initialises the tx and rx sockets. Normally just called during startup, * but also to reinitialise sockets if they go bad. * * Inputs: * void * * Outputs: * Set global sockicmp and per i/f rx pkt socket * * Return: * Non-0 if failure, else 0. */ int init_sockets(void) { int errcount = 0; int loop, sock, sockicmp; /* Raw socket for receiving NSs */ for (loop=0; loop < interfaceCount; loop++) { sock = open_packet_socket(interfaces[loop].index); if (sock < 0) { flog(LOG_ERR, "open_packet_socket: failed on iteration %d", loop); errcount++; } interfaces[loop].pktSock = sock; flog(LOG_DEBUG, "open_packet_socket: %d OK.", loop); flog(LOG_DEBUG2, "open_packet_socket value = %d", sock); /* ICMPv6 socket for sending NAs */ sockicmp = open_icmpv6_socket(); if (sockicmp < 0) { flog(LOG_ERR, "open_icmpv6_socket: failed."); errcount++; } flog(LOG_DEBUG, "open_icmpv6_socket: OK."); interfaces[loop].icmpSock = sockicmp; } return errcount; } npd6-1.1.0/includes.h000066400000000000000000000033231236174701300143440ustar00rootroot00000000000000/* * This software is Copyright 2011 by Sean Groarke * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ #ifndef INCLUDES_H #define INCLUDES_H /* On Linux, glibc>=2.8 doesn't expose in6_pktinfo otherwise.. */ #define _GNU_SOURCE /**/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // For tree handling #include #endif /* INCLUDES_H */ npd6-1.1.0/ip6.c000066400000000000000000000460171236174701300132360ustar00rootroot00000000000000/* * This software is Copyright 2011 by Sean Groarke * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ #include "includes.h" #include "npd6.h" #include "expintf.h" /***************************************************************************** * processNS * Takes a received Neighbor Solicitation and handles it. Main logic is: * - bit of extra validation. * - determine who it's asking about. * - see if that matches the prefix we are looking after. * - if it does, send a Neighbor Advertisement. * * For more, see the inline comments - There's a lot going on here. * * Inputs: * char *msg * The received NS. * int len * The length of the received (candidate) NS. * *** This has already been sanity checked back in the callers *** * * Outputs: * Potentially, sends the Neighbor Advertisement. * * Return: * void * */ void processNS( int ifIndex, unsigned char *msg, unsigned int len) { // String representations of the various addresses char targetaddr_str[INET6_ADDRSTRLEN]; char prefixaddr_str[INET6_ADDRSTRLEN]; char srcaddr_str[INET6_ADDRSTRLEN]; char dstaddr_str[INET6_ADDRSTRLEN]; // Offsets into the received packet struct ip6_hdr *ip6h = (struct ip6_hdr *)(msg + ETH_HLEN); struct icmp6_hdr *icmph = (struct icmp6_hdr *)(msg + ETH_HLEN + sizeof( struct ip6_hdr)); struct nd_neighbor_solicit *ns = (struct nd_neighbor_solicit *)(msg + ETH_HLEN + sizeof( struct ip6_hdr)); // For the interfaceIdx struct in6_addr prefixaddr = interfaces[ifIndex].prefix; int prefixaddrlen = interfaces[ifIndex].prefixLen; unsigned char *linkAddr = interfaces[ifIndex].linkAddr; int interfaceIdx = interfaces[ifIndex].index; // Extracted from the received packet struct in6_addr *srcaddr; struct in6_addr *dstaddr; struct in6_addr *targetaddr; unsigned int multicastNS; // For outgoing NA struct in6_addr srcLinkAddr = IN6ADDR_ANY_INIT; struct in6_pktinfo *pkt_info; struct sockaddr_in6 sockaddr; unsigned char nabuff[MAX_PKT_BUFF]; struct nd_neighbor_advert *nad; size_t iovlen=0; struct iovec iov; struct cmsghdr *cmsg; char __attribute__((aligned(8))) chdr[CMSG_SPACE(sizeof(struct in6_pktinfo))]; struct msghdr mhdr; ssize_t err; struct nd_opt_hdr *opthdr; void *optdata; // Validate ICMP packet type, to ensure filter was correct // In theory not required, as the filter CAN'T be wrong...! if ( icmph->icmp6_type == ND_NEIGHBOR_SOLICIT ) { flog(LOG_DEBUG2, "Confirmed packet as icmp6 Neighbor Solicitation."); srcaddr = &ip6h->ip6_src; dstaddr = &ip6h->ip6_dst; if (debug) { print_addr(srcaddr, srcaddr_str); print_addr(dstaddr, dstaddr_str); flog( LOG_DEBUG, "src addr = %s", srcaddr_str); flog( LOG_DEBUG, "dst addr = %s", dstaddr_str); } } else { flog(LOG_ERR, "Received impossible packet... filter failed. Oooops."); return; } // Bug 27 - Handle DAD NS as per RFC4862, 5.4.3 if ( IN6_IS_ADDR_UNSPECIFIED(srcaddr) ) { flog(LOG_DEBUG, "Unspecified src addr - DAD activity. Ignoring NS."); return; } // Based upon the dstaddr, record if this was a unicast or multicast NS. // If unicast, we'll use that later when we decide whether to add the // target link-layer option to any outgoing NA. if ( IN6_IS_ADDR_MULTICAST(dstaddr) ) { // This was a multicast NS flog(LOG_DEBUG2, "Multicast NS"); multicastNS = 1; }else { // This was a unicast NS flog(LOG_DEBUG2, "Unicast NS"); multicastNS=0; } // Within the NS, who are they looking for? targetaddr = (struct in6_addr *)&(ns->nd_ns_target); if (debug || listLog) { print_addr16(targetaddr, targetaddr_str); print_addr16(&prefixaddr, prefixaddr_str); flog(LOG_DEBUG, "NS target addr: %s", targetaddr_str); flog(LOG_DEBUG, "Local prefix: %s", prefixaddr_str); } // If tgt-addr == dst-addr then ignore this, as the automatic mechanisms // will reply themselves - we don't need to. if ( nsIgnoreLocal && IN6_ARE_ADDR_EQUAL(targetaddr, dstaddr) ) { flog(LOG_DEBUG, "tgt==dst - Ignore."); return; } // Check for black or white listing compliance switch (listType) { case NOLIST: flog(LOG_DEBUG2, "Neither white nor black listing in operation."); break; case BLACKLIST: // See if the address matches an expression if((compareExpression(targetaddr) == 1)) { flog(LISTLOGGING, "NS for blacklisted EXPR address: %s", targetaddr_str); return; // Abandon } // If active and tgt is in the list, bail. if ( tfind( (void *)targetaddr, &lRoot, tCompare) ) { flog(LISTLOGGING, "NS for blacklisted specific addr: %s", targetaddr_str); return; //Abandon } break; case WHITELIST: // See if the address matches an expression if((compareExpression(targetaddr) == 1)) { flog(LISTLOGGING, "NS for whitelisted EXPR: %s", targetaddr_str); break; // Don't check further - we got a hit. } // If active and tgt is NOT in the list (and didn't match an expr above), bail. if ( tfind( (void *)targetaddr, &lRoot, tCompare) ) { flog(LISTLOGGING, "NS for specific addr whitelisted: %s", targetaddr_str); break; } else { // We have whitelisting in operation but failed to match either type. // Log it if required. flog(LOG_DEBUG, "No whitelist match for: %s", targetaddr_str); return; } break; } // Does it match our configured prefix that we're interested in? if (! addr6match( targetaddr, &prefixaddr, prefixaddrlen) ) { flog(LOG_DEBUG, "Target/:prefix - Ignore NS."); return; } else { flog(LOG_DEBUG, "Target:prefix - Build NA response."); // If configured, log target to list if (collectTargets) { flog(LOG_DEBUG, "Store target to list."); storeTarget( targetaddr ); } // Start building up the header for the packet memset(( void *)&sockaddr, 0, sizeof(struct sockaddr_in6)); sockaddr.sin6_family = AF_INET6; sockaddr.sin6_port = htons(IPPROTO_ICMPV6); // Set the destination of the NA memcpy(&sockaddr.sin6_addr, srcaddr, sizeof(struct in6_addr)); // Set up the NA itself memset( nabuff, 0, sizeof(nabuff) ); nad = (struct nd_neighbor_advert *)nabuff; nad->nd_na_type = ND_NEIGHBOR_ADVERT; nad->nd_na_code = 0; nad->nd_na_cksum = 0; if (naRouter) { nad->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED | ND_NA_FLAG_ROUTER; } else { nad->nd_na_flags_reserved |= ND_NA_FLAG_SOLICITED; } memcpy(&(nad->nd_na_target), targetaddr, sizeof(struct in6_addr) ); if (multicastNS || naLinkOptFlag) { // If the NS that came in was to a multicast address // or if we have forced the option for all packets anyway // then add a target link-layer option to the outgoing NA. // Per rfc, we must add dest link-addr option for NSs that came // to the multicast group addr. opthdr = (struct nd_opt_hdr *)&nabuff[sizeof(struct nd_neighbor_advert)] ; opthdr->nd_opt_type = ND_OPT_TARGET_LINKADDR; opthdr->nd_opt_len = 1; // Units of 8-octets optdata = (unsigned char *) (opthdr + 1); memcpy( optdata, linkAddr, 6); // Build the io vector iovlen = sizeof(struct nd_neighbor_advert) + sizeof(struct nd_opt_hdr) + ETH_ALEN; iov.iov_len = iovlen; iov.iov_base = (caddr_t) nabuff; } else { // The NS was unicast AND the config option was unset. // Build the io vector iovlen = sizeof(struct nd_neighbor_advert); iov.iov_len = iovlen; iov.iov_base = (caddr_t) nabuff; } // Build the cmsg memset(chdr, 0, sizeof(chdr) ); cmsg = (struct cmsghdr *) chdr; cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo) ); cmsg->cmsg_level = IPPROTO_IPV6; cmsg->cmsg_type = IPV6_PKTINFO; pkt_info = (struct in6_pktinfo *)CMSG_DATA(cmsg); // Set src (sending) addr and outgoing i/f for the datagram memcpy(&pkt_info->ipi6_addr, &srcLinkAddr, sizeof(struct in6_addr) ); pkt_info->ipi6_ifindex = interfaceIdx; // Build the mhdr memset(&mhdr, 0, sizeof(mhdr) ); mhdr.msg_name = (caddr_t)&sockaddr; mhdr.msg_namelen = sizeof(sockaddr); mhdr.msg_iov = &iov; mhdr.msg_iovlen = 1; mhdr.msg_control = (void *) cmsg; mhdr.msg_controllen = sizeof(chdr); flog(LOG_DEBUG2, "Outbound message built"); err = sendmsg( interfaces[ifIndex].icmpSock, &mhdr, 0); if (err < 0) flog(LOG_ERR, "sendmsg returned with error %d = %s", errno, strerror(errno)); else flog(LOG_DEBUG2, "sendmsg completed OK"); } } /***************************************************************************** * processICMP * Takes a received ICMP message and handles it. At first, we don't * do too much. Based upon NFR 60 we are going to look out for RAs, * extract useful data and log it. * * Later on we may go further with that information... * * Inputs: * char *msg * The received ICMP6. * int len * The length of the received data * *** This has already been sanity checked back in the callers *** * * Outputs: * As per above, likely just a log/debug for now. * * Return: * void * */ void processICMP( int ifIndex, unsigned char *msg, unsigned int len, struct in6_addr *addr6) { // Offsets into the received packet struct icmp6_hdr *icmph = (struct icmp6_hdr *)(msg); struct nd_router_advert *ra = (struct nd_router_advert *)(msg + sizeof(struct icmp6_hdr)); uint32_t reachableT = ra->nd_ra_reachable; uint32_t retransmitT = ra->nd_ra_retransmit; int curHopLimit = ra->nd_ra_curhoplimit; int rtrLifetime = ra->nd_ra_router_lifetime; int counter = 0; char addr6_str[INET6_ADDRSTRLEN]; // May not exist - will check struct nd_opt_hdr *optHdr = (struct nd_opt_hdr *)(msg + sizeof(struct nd_router_advert)); flog(LOG_DEBUG, "Check for RA in received ICMP6."); if ( icmph->icmp6_type == ND_ROUTER_ADVERT ) { print_addr(addr6, addr6_str); flog(LOG_INFO, "RA received from address: %s", addr6_str); flog(LOG_DEBUG, "Reachable timer = %ld, retransmit timer = %ld", ntohl(reachableT), ntohl(retransmitT)); flog(LOG_DEBUG, "Cur Hop Limit = %d, Router Lifetime = %d", curHopLimit, rtrLifetime); counter = sizeof(struct nd_router_advert); while (counter < len) { uint8_t optionLen; /* n.b. Octets */ uint8_t prefixLen; uint32_t prefixValidTime, prefixPreferredTime; struct in6_addr prefixPrefix; char prefixPrefix_str[INET6_ADDRSTRLEN]; struct nd_opt_prefix_info *prefixInfo; unsigned char *linkAddr; int watchDog=0; // So offset optHdr is now valid and points to 1 or more options switch(optHdr->nd_opt_type) { case ND_OPT_SOURCE_LINKADDR: flog(LOG_DEBUG, "RA-opt received: Source Link Address"); linkAddr = ((unsigned char *)(optHdr) + 2); flog(LOG_DEBUG, "Link address: %02x:%02x:%02x:%02x:%02x:%02x", linkAddr[0], linkAddr[1], linkAddr[2], linkAddr[3], linkAddr[4], linkAddr[5]); break; case ND_OPT_TARGET_LINKADDR: flog(LOG_DEBUG, "RA-opt received: Target Link Address"); linkAddr = ((unsigned char *)(optHdr) + 2); flog(LOG_DEBUG, "Link address: %02x:%02x:%02x:%02x:%02x:%02x", linkAddr[0], linkAddr[1], linkAddr[2], linkAddr[3], linkAddr[4], linkAddr[5]); break; case ND_OPT_PREFIX_INFORMATION: flog(LOG_INFO, "RA-opt received: Prefix Info"); prefixInfo = (struct nd_opt_prefix_info *)optHdr; prefixLen = prefixInfo->nd_opt_pi_prefix_len; prefixValidTime = prefixInfo->nd_opt_pi_valid_time; prefixPreferredTime = prefixInfo->nd_opt_pi_preferred_time; prefixPrefix = prefixInfo->nd_opt_pi_prefix; print_addr(&prefixPrefix, prefixPrefix_str); flog(LOG_INFO, "Received prefix is: %s", prefixPrefix_str); flog(LOG_INFO, "Prefix length: %d", prefixLen); flog(LOG_INFO, "Valid time: %ld", ntohl(prefixValidTime)); flog(LOG_INFO, "Preferred time: %ld", ntohl(prefixPreferredTime)); break; case ND_OPT_REDIRECTED_HEADER: flog(LOG_DEBUG, "RA-opt received: Redirected Header"); break; case ND_OPT_MTU: flog(LOG_DEBUG, "RA-opt received: MTU"); break; case ND_OPT_RTR_ADV_INTERVAL: flog(LOG_DEBUG, "RA-opt received: RA Interval"); break; case ND_OPT_HOME_AGENT_INFO: flog(LOG_DEBUG, "RA-opt received: Home Agent Info"); break; default: // *** Important default *** // Got an option that we cannot recognise - log and skip it flog(LOG_ERR, "Had option type = %d - do not recognise.", optHdr->nd_opt_type); } // Sanity check to catch runaway situation with corrupt packet (malicious or otherwise!) watchDog++; if(watchDog > 20) // 20 seems enough to say STOP! { flog(LOG_ERR, "Tripped watchdog in ICMP option decoding... Something very odd..."); return; } // Increment to (possible) next opt optionLen = (optHdr->nd_opt_len) * 8; counter += optionLen; // 32/64-bit agnostic optHdr = (struct nd_opt_hdr *) ((char *)optHdr + optionLen); } } else { flog(LOG_ERR, "Received ICMP6 - did not recognise it."); flog(LOG_ERR, "Type was %d", icmph->icmp6_type); return; } } /***************************************************************************** * addr6match * Compare two binary ipv6 addresses and see if they match * in the first N bits. * * Inputs: * a1 & a2 are the addresses to be compared, in form in6_addr. * bits is the number of bits to compare, starting from the left. * * Outputs: * void * * Return: * 1 if we match, else 0. * */ int addr6match( struct in6_addr *a1, struct in6_addr *a2, int bits) { int idx, bdx; unsigned int mask; flog(LOG_DEBUG2, "Called to match up to %d bits.", bits); if (bits > 128) { flog(LOG_ERR, "Bits > 128 (%d) does not make sense.", bits); return 0; } // The approach here is to gallop along the address comparing full octets for as far as possible. // Then when/if we reach a non-octet aligned point, we deal with that. // Since vast majority of folks will have octet aligned prefixes, this is highly efficient for (bdx=bits, idx=0; bdx>0; bdx-=8, idx++) { if (bdx >= 8) { // We can compare a full 8-bit comparison - no masking yet if ( a1->s6_addr[idx] != a2->s6_addr[idx] ) { //Failed to match flog(LOG_DEBUG2, "Failed in 8-bit match test.idx = %d, bdx = %d", idx, bdx); flog(LOG_DEBUG2, "a1 value: %2x a2 value: %2x", a1->s6_addr[idx], a2->s6_addr[idx]); return 0; } } else { // We are in the end-zone - masking required switch (bdx) { case 7: mask=0xfe; break; case 6: mask=0xfc; break; case 5: mask=0xf8; break; case 4: mask=0xf0; break; case 3: mask=0xe0; break; case 2: mask=0xc0; break; case 1: mask=0x80; break; } if ( ((a1->s6_addr[idx])&mask) != a2->s6_addr[idx] ) { //Failed to match flog(LOG_DEBUG2, "Failed in mask match test.idx = %d, bdx = %d, mask = %04x", idx, bdx, mask); flog(LOG_DEBUG2, "a1 value: %04x a2 value: %04x", a1->s6_addr[idx], a2->s6_addr[idx]); return 0; } } } flog(LOG_DEBUG2, "Target and prefix matched up to bit position %d", bits); return 1; } npd6-1.1.0/main.c000066400000000000000000000243621236174701300134630ustar00rootroot00000000000000/* * This software is Copyright 2011 by Sean Groarke * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ #include "includes.h" #include "npd6.h" char usage_str[] = { "\n" " -c, --config=PATH Sets the config file. Default is /etc/npd6.conf.\n" " -d, --debug Sets the normal debug level.\n" " -D, --debug2 Sets full debug level. (Lots!)\n" " -f, --foreground Run in foreground, otherwise daemonize.\n" " -h, --help Show this help screen.\n" " -l --logfile=PATH Sets the log file, else default to syslog. If \"-l -\" then stdout/stderr used.\n" " -v, --version Print the version and quit.\n" }; struct option prog_opt[] = { {"config", required_argument, 0, 'c'}, {"debug", optional_argument, 0, 'd'}, {"debug2", optional_argument, 0, 'D'}, {"help", optional_argument, 0, 'h'}, {"logfile", required_argument, 0, 'l'}, {"version", optional_argument, 0, 'v'}, {"foreground", optional_argument, 0, 'f'}, {0, 0, 0, 0} }; #define OPTIONS_STR "c:dDhl:vf" struct Interface *IfaceList = NULL; int main(int argc, char *argv[]) { char logfile[FILENAME_MAX] = ""; int c, err, loop; // Default some globals strncpy(configfile, NPD6_CONF, FILENAME_MAX); daemonize=1; // Default black/whitelisting to OFF listType = NOLIST; // Default config file values as required naLinkOptFlag = 0; nsIgnoreLocal = 1; naRouter = 1; maxHops = MAXMAXHOPS; // Logging listLog=0; ralog=0; /* Interface info */ interfaceCount = 0; /* Parse the args */ while ((c = getopt_long(argc, argv, OPTIONS_STR, prog_opt, NULL)) > 0) { if (c==-1) break; switch (c) { case 'c': strcpy(configfile, optarg); break; case 'l': strcpy(logfile, optarg); break; case 'd': debug=1; break; case 'D': debug=2; break; case 'f': daemonize=0; break; case 'v': showVersion(); return 0; break; case 'h': showUsage(); return 0; break; } } /* Sort out where to log */ if ( strlen(logfile) ) { if (strlen(logfile) == 1 && (logfile[0]='-') ) logging = USE_STD; else logging = USE_FILE; } else { logging = USE_SYSLOG; } /* Open the log and config*/ if ( (logging == USE_FILE) && (openLog(logfile) < 0) ) { printf("Exiting. Error in setting up logging correctly.\n"); exit (1); } flog(LOG_INFO, "*********************** npd6 *****************************"); if ( readConfig(configfile) ) { flog(LOG_ERR, "Error in config file: %s", configfile); return 1; } err = init_sockets(); if (err) { flog(LOG_ERR, "init_sockets: failed to initialise %d sockets.", err); exit(1); } /* Set allmulti on the interfaces */ for (loop=0; loop 0) { // Most likely event is a valid data item received. for (fdIdx=0; fdIdx < (interfaceCount*2); fdIdx++) { if (fds[fdIdx].revents & POLLIN) { // Was it a packet socket? if(fdIdx < interfaceCount) { consecutivePollErrors = 0; // reset it msglen = get_rx(interfaces[fdIdx].pktSock, msgdata); // msglen is checked for sanity already within get_rx() flog(LOG_DEBUG2, "For packet socket, get_rx() gave msg with len = %d", msglen); processNS(fdIdx, msgdata, msglen); continue; } // Or was it an ICMP socket? if(fdIdx >= interfaceCount) { struct in6_addr icmp6Addr; consecutivePollErrors = 0; // reset it msglen = get_rx_icmp6(interfaces[fdIdx/2].icmpSock, msgdata, &icmp6Addr); flog(LOG_DEBUG2, "For ICMP6 socket, get_rx_icmp6() gave msg with len = %d", msglen); // We do nothing at all with the received data! // Or maybe we do.... Ref. bug/NFR 60: process them // and yank out the RAs for logging. // Decide what to do based upon config file option ralog if (ralog) { processICMP(fdIdx, msgdata, msglen, &icmp6Addr); } continue; } } // If it wasn't a POLLIN, it is likely an significant error if (fds[fdIdx].revents & (POLLERR | POLLHUP | POLLNVAL) ) { flog(LOG_WARNING, "Major socket error on fds %d", fdIdx); // Try and recover... long shot close(interfaces[fdIdx].pktSock); sleep(1); interfaces[fdIdx].pktSock = open_packet_socket(interfaces[fdIdx].index); if ( interfaces[fdIdx].pktSock < 0) { // Drop dead. We're stuffed. flog(LOG_ERR, "dispatcher(): failed to reinit stuck socket. Dead."); exit(1); } fds[fdIdx].fd = interfaces[fdIdx].pktSock; fds[fdIdx].events = POLLIN; fds[fdIdx].revents = 0; // Have we had more consecutive errors than the threshold value? consecutivePollErrors++; if ((pollErrorLimit > 0) && (consecutivePollErrors >= pollErrorLimit) ) { flog(LOG_ERR, "dispatcher(): %d consecutive major poll errors. Terminating.", consecutivePollErrors); dropdead(); } continue; } } continue; } else if ( rc == 0 ) { flog(LOG_DEBUG, "Stale select - Idling....... Low activity........"); consecutivePollErrors = 0; // Using the select timeout as our quantum of error counting // Timer fired? // One day. If we implement timers. } else if ( rc == -1 ) { /* Truly an error or maybe we processed a signal?*/ if ( errno == EINTR ) { flog(LOG_ERR, "Broke out of the poll() via a signal event."); } else { flog(LOG_ERR, "Weird poll error: %s", strerror(errno)); } continue; } flog(LOG_DEBUG, "Timed out of poll(). Timeout was %d ms", DISPATCH_TIMEOUT); } } //******************************************************* // Give advice. void showUsage(void) { fprintf(stderr, "usage: %s %s\n", pname, usage_str); } npd6-1.1.0/man/000077500000000000000000000000001236174701300131375ustar00rootroot00000000000000npd6-1.1.0/man/npd6.8.gz000066400000000000000000000015361236174701300145230ustar00rootroot00000000000000e=Pnpd6.8mUMo8W tJXI[4q۸hm#ridqKIq}Iǖ{1MyCs*t/L ~4ڋʓZ#c t UZ5<7GꄦBݛw9okH/yV~g4m5/wFwnr.eVbLIGⵆ}+t5PkҦZxIj϶Hb+(YI/4KQ:[s#5{?/ ZxKHhgrDJz+|ZzcTu>XD[HG!_![ml|0_#OMB9C=P$>aXg +H @ny(kX8&ˢNtqqK2ž!Pl*v(j`dҲfJX!.(1`C.M"= y 1C0!:+.?ҔMt$4_?T2iwzi"<5(6&f#325aӠORGr^=ڄFI]@'b .=^J(u@(h&ėTw٘0q]9'Ci*"p?Jha00(`ZWAT_/sy[Nt")oӋ8r uM$:t>ry̖~hRZ>LV|,8HdmzJƨJ:xAZnpd6-1.1.0/man/npd6.conf.5.gz000066400000000000000000000076251236174701300154510ustar00rootroot00000000000000=Pnpd6.conf.5ZnG}Wl,䘒c'aʉ+p$A, s%9U=& ErfrT$3?bcd4&},B\&]ԦZ7o˵9M"UW 6JF;ye/hl`/a2M$RQrΜx2/cb/>&yj2?Ժ5.kb$}{rz+}|A|QaҮ] /VfUWb4!CSs/+S&sw0'!49woe {U߫׼4N#)Ɩ䮪Lo\JgYKcqlwq](o p|iR&ͭSXp[!]XlS-6y5vBͭ!Lb;{++=@튗,t9`-KFTMh@4w@ASY(!BoR,7ty0 dg[ǚןNPsSߛ$pWjb[nyǥ Y/|- .17wfp3^Ms-ꕭ< ?Ȗ-e{`=.w"&&_=n6v?֕r9$J ~yKQN!1#U'NQߘ//ֶ'JJ40sFeXAKp{5W ySf{<7tfNf֡|HFP; ͔:2TyTWRMLFlzxr6t:l[NF؟ 4`{lӂ5e\ 1ĜKɟ\F)hmm>V aK6gQi8Ju|0_oS.ٌ4$.ǩt:QM0jqѢ[v ъ᝝ ѧ (gd.!e2eh4J; w_ײW-IGpvt4{r9uj%ܓXzbO05Uu@"Ga06WY4!#[-xa)Zgb8oD@ًׯDڻf1mQ*/|e,g40՟֟_}J1!N 4SEKI`ީ;[3;t}6 /kE| >&7*vjƸF,w]O]UtP^!j wᎥPd5/Gf7S9'ԫBqł(D_\"%.QpKUꅔK@z4AX\Q~#Uвe"7fi>Z~0Y幠Sf`PAؕ? Gs vm~CR"VUP}P-+f "e[Eyb6n3qL[щjUF6mcҙrV,Xt߶r}L7"*C$pbѼ4*iENОl^ETlf,vѬڝYzisB6@,)r[0X} #=Ae1f]PgOůĴQ0C˽Oń<eb{" @Aq H%fA~U_.1Գ]˜; w0c4QҦ(EF+)]/GW|7'*-=(,R,w@$N"2լFI&9D%eV4VRp)w}F["yKӹ˴4[pdQftx|=iy;X(Jh?v}FdkMlR!m` 8yFdB&dԊi g!JwW_^ϟLY_OOWãOFRhfp&X&:Fӟg4 W"D)I~u;Bh`#d{^ *RFB""v 2oZ c%U[cv,_[%R*RJd4!Dr xxɰbnŏ7[̿wF\oSCm"+ 4H$J\Ȝ鎒q#N "> _JO]&!N/T2+PtB7kKN:ڣfglѷM6A7O2^}qt;&-SG%a.eZo>\]is=0MW_G Q`#dԵC1xS7+tyWRUUcFURERΉLV8d2`l#>/@H-8`g!\g90OS(20e;0˔n#$;'XS3}^#n4\BrvQTe($>e @v`x%Ԑ6 Ť24 C4 ٤r孇s[ t+[4Ա"<cA/F[hbфre4霱#wߠ-]2sup*GWgi#18tDtVj}dق(EPaxuއc[0GoW>)ѕnEhA^XԇTy Q0CENZzI'PF 8Exǩ9Z>pyֱuKjKʐ L뭫8aj}c!g$1GSm`+fV|b}:ҸLwh%ߊ[\"k * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ #ifndef NPD6_H #define NPD6_H #include "includes.h" #ifndef NPD6_CONF #define NPD6_CONF "/etc/npd6.conf" #endif #ifndef NPD6_LOG #define NPD6_LOG "/var/log/npd6.log" #endif #ifndef NULL #define NULL 0 #endif #ifndef TRUE #define TRUE 1 #endif // Misc bits and bobs #define HWADDR_MAX 16 #define MAX_PKT_BUFF 1500 #define MAX_MSG_SIZE 2048 #define LOGTIMEFORMAT "%b %d %H:%M:%S" #define INTERFACE_STRLEN 12 #define NULLSTR "null" #define min(a,b) (((a) < (b)) ? (a) : (b)) #define MAXMAXHOPS 255 #define HWADDR_MAX 16 #define DISPATCH_TIMEOUT 30000 // milliseconds 30000 = 30 sec #define flog(pri, ...) npd6log(__FUNCTION__, pri, __VA_ARGS__) #define LOG_DEBUG2 8 #define USE_FILE 1 #define USE_SYSLOG 2 #define USE_STD 3 #define MAXTARGETS 1000000 // Ultimate sane limit #define LISTLOGGING (listLog==1?LOG_INFO:LOG_DEBUG) #define NOMASK 9999 //***************************************************************************** // Globals // char *pname; char *paramName; int sockpkt; int debug; int daemonize; FILE *logFileFD; int logging; char configfile[FILENAME_MAX]; FILE *configFileFD; int initialIFFlags; // Record of interfaces, prefix, indices, etc. struct npd6Interface { char nameStr[INTERFACE_STRLEN]; unsigned int index; char prefixStr[INET6_ADDRSTRLEN]; struct in6_addr prefix; int prefixLen; unsigned char linkAddr[6]; unsigned int multiStatus; int pktSock; int icmpSock; }; unsigned int interfaceCount; // Total number of interface/prefix combos // We dynaimcally size this at run-time struct npd6Interface *interfaces; // Key behaviour int naLinkOptFlag; // From config file NPD6OPTFLAG int nsIgnoreLocal; // From config file NPD6LOCALIG int naRouter; // From config file NPD6ROUTERNA int maxHops; // From config file NPD6MAXHOPS int collectTargets; // From config file NPD6TARGETS // Target tree data structures etc void *tRoot; int tCompare(const void *, const void *); void tDump(const void *, const VISIT , const int); int tEntries; // Black/whitelisting data void *lRoot; int listType; #define NOLIST 0 #define BLACKLIST 1 #define WHITELIST 2 int listLog; // From config file NPD6LISTLOG // Logging - various int ralog; // From config file NPD6RALOG // Error handling int pollErrorLimit; // From config file //***************************************************************************** // Prototypes // // main.c void dispatcher(void); void showUsage(void); // config.c int readConfig(char *); // util.c int npd6log(const char *, int , char *, ...); void usersignal(int ); void print_addr(struct in6_addr *, char *); void print_addr16(const struct in6_addr * , char * ); int build_addr(char *, struct in6_addr *); int prefixset(char []); void stripwhitespace(char *); void dumpHex(unsigned char *, unsigned int); int getLinkaddress( char *, unsigned char *); void showVersion(void); int openLog(char *); void dropdead(void); void dumpAddressData(void); void storeTarget( struct in6_addr *); int tCompare(const void *, const void *); void tDump(const void *, const VISIT, const int); void storeListEntry(struct in6_addr *); // icmp6.c int open_packet_socket(int); int open_icmpv6_socket(void); int get_rx(int, unsigned char *); int get_rx_icmp6(int, unsigned char *, struct in6_addr *); int if_allmulti(char *, unsigned int); int init_sockets(void); // ip6.c void processNS(int, unsigned char *, unsigned int); void processICMP(int, unsigned char *, unsigned int, struct in6_addr *); int addr6match( struct in6_addr *, struct in6_addr *, int); #endif npd6-1.1.0/npd6config.h000066400000000000000000000035211236174701300145730ustar00rootroot00000000000000/* * This software is Copyright 2011 by Sean Groarke * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ // Order of #defines must equal order in the string array #define NPD6PREFIX 0 #define NPD6INTERFACE 1 #define NPD6OPTFLAG 2 #define NPD6LOCALIG 3 #define NPD6ROUTERNA 4 #define NPD6MAXHOPS 5 #define NPD6TARGETS 6 #define NPD6LISTTYPE 7 #define NPD6LISTADDR 8 #define NPD6EXPRADDR 9 #define NPD6LISTLOG 10 #define NPD6ERRORTH 11 #define NPD6RALOG 12 #define CONFIGTOTAL 13 #define NOMATCH -1 char *configStrs[CONFIGTOTAL] = { "prefix", "interface", "linkOption", "ignoreLocal", "routerNA", "maxHops", "collectTargets", "listtype", "addrlist", "exprlist", "listlogging", "pollErrorLimit", "ralogging" }; // For logging system #define NPD6LOGGING "logging" #define NPD6FILE "file" #define NPD6SYSLOG "syslog" #define SET "true" #define UNSET "false" #define ON "on" #define OFF "off" #define NPD6NONE "none" #define NPD6BLACK "black" #define NPD6WHITE "white" npd6-1.1.0/util.c000066400000000000000000000444721236174701300135200ustar00rootroot00000000000000/* * This software is Copyright 2011 by Sean Groarke * All rights reserved. * * This file is part of npd6. * * npd6 is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * npd6 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with npd6. If not, see . */ /* $Id$ * $HeadURL$ */ #include "includes.h" #include "npd6.h" //******************************************************* // When we receive sigusrN, do something awesome. /***************************************************************************** * usersignal * When dispatcher picks up a user signal, do something with it. * * Inputs: * int mysig * The signal received. * * Outputs: * Various, depending upon the sig. * * Return: * void * */ void usersignal(int mysig) { switch(mysig) { case SIGUSR1: signal(SIGUSR1, usersignal); flog(LOG_DEBUG, "called with USR1"); flog(LOG_INFO, "SIGUSR1 received: rereading config"); if ( readConfig(configfile) ) { flog(LOG_ERR, "Error in config file: %s", configfile); exit(1); } break; case SIGUSR2: signal(SIGUSR2, usersignal); flog(LOG_DEBUG, "called with USR2"); dumpAddressData(); break; case SIGHUP: signal(SIGUSR2, usersignal); flog(LOG_DEBUG, "called with HUP"); /* action? */ break; case SIGINT: case SIGTERM: signal(SIGUSR2, usersignal); flog(LOG_DEBUG, "called with INT"); /* We're dying, so handle directly here*/ dropdead(); break; default: flog(LOG_DEBUG, "Why am I here? Confused."); break; } } /***************************************************************************** * npd6log * Log as per level to the previously defined log file or system.. * * Inputs: * char * fn name * From the caller's __FUNCTION__ * int pri * Log level: e.g. LOG_ERR, LOG_INFO, LOG_DEBUG, etc. * char *format * Standard format string. * ... * Variable number of params to accompany the format string. * GLOBAL * logFileFD * Previously opened lof device. * * Outputs: * Via fprintf to the log file. * * Return: * 0 on success * * Notes: * This will be called from the macro expansion of "flog()" * This gets called a lot, but much of the time only does * anything if the debug options are turned on. Hence it's * written in a slightly odd manner to avoid data allocation * or work unless we have debug turned on of it's a non-debug * call. */ int npd6log(const char *function, int pri, char *format, ...) { // Pick up debug requests and decide if we are logging them if (!debug && pri>=LOG_DEBUG) { return 0; // Silent... } // Check for extra super power debug if ( (debug!=2) && (pri == LOG_DEBUG2) ) { return 0; // Silent... } // Normalise the debug level if( pri==LOG_DEBUG2) { pri=LOG_DEBUG; // Since only we understand the two levels } // Artificial blocking of code to improve efficiency... if the compiler plays ball. :-) { time_t now; struct tm *timenow; char timestamp[128], obuff[2048]; va_list param; va_start(param, format); vsnprintf(obuff, sizeof(obuff), format, param); now = time(NULL); timenow = localtime(&now); (void) strftime(timestamp, sizeof(timestamp), LOGTIMEFORMAT, timenow); switch (logging) { case USE_FILE: fprintf(logFileFD, "[%s] %s: %s\n", timestamp, function, obuff); break; case USE_SYSLOG: syslog(pri, "%s: %s\n", function, obuff); break; case USE_STD: if (pri <= LOG_ERR) fprintf(stderr, "[%s] %s: %s\n", timestamp, function, obuff); else fprintf(stdout, "[%s] %s: %s\n", timestamp, function, obuff); break; } va_end(param); return 0; } } /***************************************************************************** * print_addr * Convert ipv6 address to string representation. * * Inputs: * const struct in6_addr * addr * Binary ipv6 address * * Outputs: * char * str * String representation, *not* fully padded. * * Return: * void * * Notes: * Compare with print_addr16 - this version does not pad. */ void print_addr(struct in6_addr *addr, char *str) { const char *res; res = inet_ntop(AF_INET6, (void *)addr, str, INET6_ADDRSTRLEN); if (res == NULL) { flog(LOG_ERR, "print_addr: inet_ntop: %s", strerror(errno)); strcpy(str, "[invalid address]"); } } /***************************************************************************** * build_addr * Convert char represetation of ipv6 addr to binary form. * * Inputs: * char * str * String representation, fully padded. * * Outputs: * const struct in6_addr * addr * Binary ipv6 address * * Return: * return code from inet_pton * 1 => Good, else Bad */ int build_addr(char *str, struct in6_addr *addr) { int ret; flog(LOG_DEBUG2, "called with address %s", str); ret = inet_pton(AF_INET6, str, (void *)addr); if (ret == 1) flog(LOG_DEBUG2, "inet_pton OK"); else if(ret == 0) flog(LOG_ERR, "invalid input address"); else flog(LOG_ERR, "inet_pton: %s", strerror(errno)); return ret; } /***************************************************************************** * print_addr16 * Print ipv6 address in fully expanded 64-bit char form * * Inputs: * const struct in6_addr * addr * Binary ipv6 address * * Outputs: * char * str * String representation, fully padded. * * Return: * void */ void print_addr16(const struct in6_addr * addr, char * str) { sprintf(str, "%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x", (int)addr->s6_addr[0], (int)addr->s6_addr[1], (int)addr->s6_addr[2], (int)addr->s6_addr[3], (int)addr->s6_addr[4], (int)addr->s6_addr[5], (int)addr->s6_addr[6], (int)addr->s6_addr[7], (int)addr->s6_addr[8], (int)addr->s6_addr[9], (int)addr->s6_addr[10], (int)addr->s6_addr[11], (int)addr->s6_addr[12], (int)addr->s6_addr[13], (int)addr->s6_addr[14], (int)addr->s6_addr[15]); } /***************************************************************************** * prefixset * Take prefix in the form 1111:2222:3333:4444: and pad it to the * full length * * Inputs: * char * px * String representation, fully padded. * * Outputs: * As input * * Return: * -1 on error, else bit length of unpadded prefix. * * Note: * IMPORTANT! px *must* be big enough to hold full ipv6 string */ int prefixset(char px[]) { size_t len; int missing, c1, c2; char suffix[INET6_ADDRSTRLEN]; // First we must ensure fully padded with leading 0s for(c1=0; c1=1) && (missing<=3) ) { strcpy( suffix, &px[c1]); // Grab the tail memset(&px[c1], '0', missing); // pad it with missing zeros px[c1+missing] = '\0'; // Re-terminate strcat(px, suffix); // Add the tail back } } flog(LOG_DEBUG2, "String after padding: %s", px); // Do we have a '::' on the end we need to trim? len = strlen(px); if ( (px[len-1] == ':') && (px[len-2] == ':') ) { flog(LOG_DEBUG2, "Spotted double :: termination of prefix."); px[len-1] = '\0'; } len = strlen(px); switch (len) { case 5: strcat(px, "0000:0000:0000:0000:0000:0000:0000"); return 16; case 10: strcat(px, "0000:0000:0000:0000:0000:0000"); return 32; case 15: strcat(px, "0000:0000:0000:0000:0000"); return 48; case 20: strcat(px, "0000:0000:0000:0000"); return 64; case 25: strcat(px, "0000:0000:0000"); return 80; case 30: strcat(px, "0000:0000"); return 96; case 35: strcat(px, "0000"); return 112; case 39: flog(LOG_ERR, "Full 128-bits defined as the configured prefix. Sure???"); return 128; default: flog(LOG_ERR, "configured prefix not correctly formatted (len = %d)", len); return -1; } } /***************************************************************************** * stripwhitespace * Tidy up lines of text from the config file. * * Inputs: * char * str * String to be tidied. * * Outputs: * As input * * Return: * void */ void stripwhitespace(char *str) { char *p1 = str; char *p2 = str; p1=str; while(*p1 != 0) { if(isspace(*p1)) ++p1; else *p2++ = *p1++; } *p2=0; } /***************************************************************************** * dumpHex * Take a lump of binary data (like an ethernet frame!) and print it in hex. * Useful for debugging! * * Inputs: * unsigned char *data * Lump of data * unsigned int len * Amount of data * * Outputs: * Via printf to stdio * * Return: * void */ void dumpHex(unsigned char *data, unsigned int len) { int ix; printf("Dumping %d bytes of hex:\n>>>>>", len); for(ix=0; ix < len; ix++) printf("%02x", data[ix]); printf("<<<<<\n"); } /***************************************************************************** * getLinkaddress * Get the link-level address (i.e. MAC address) for an interface. * * Inputs: * char * iface * String containing the interface name (e.g. "eth1") * * Outputs: * unsigned char * link * String of the form "00:12:34:56:78:90" * * Return: * 0 is successful, * 1 if error. */ int getLinkaddress( char * iface, unsigned char * link) { int sockfd, io; struct ifreq ifr; strncpy( ifr.ifr_name, iface, INTERFACE_STRLEN ); sockfd = socket(AF_INET, SOCK_STREAM, 0); if(sockfd < 0) { flog(LOG_ERR, "failed to open a test SOCK_STREAM."); return 1; } io = ioctl(sockfd, SIOCGIFHWADDR, (char *)&ifr); if(io < 0) { flog(LOG_ERR, "octl failed to obtain link address"); return 1; } memcpy(link, (unsigned char *)ifr.ifr_ifru.ifru_hwaddr.sa_data, 6); close(sockfd); return 0; } //******************************************************* // Take the supplied filename and open it for logging use. // Upon return, logFileFD set unless we failed. int openLog(char *logFileName) { if (logging == USE_FILE) { if ((logFileFD = fopen(logFileName, "a")) == NULL) { fprintf(stderr, "Can't open %s: %s\n", logFileName, strerror(errno)); return (-1); } // Set line-buffering so we don't need to explicitly fflush() // when we write via npd6log setlinebuf(logFileFD); return 0; } if (logging == USE_SYSLOG) { openlog("npd6", (LOG_NDELAY|LOG_PID),LOG_DAEMON); return 0; } // Error return -1; } //******************************************************* // Just display the version and return. void showVersion(void) { printf("npd6 - version %s\n", BUILDREV); printf("\nCopyright (C) 2011-2013 Sean Groarke\n\n"); } void dropdead(void) { int loop; /* We're dying, so tidy up*/ /* Restore interface flags and close sockets */ for (loop=0; loop= collectTargets) { flog(LOG_INFO, "Reached max threshold of recorded targets (%d). Not recording.", collectTargets); return; } // New entry flog(LOG_DEBUG2, "New entry - recording."); if ( tsearch( (void *)ptr, &tRoot, tCompare) == NULL) { flog(LOG_ERR, "tsearch failed. Cannot record entry."); return; } else { tEntries++; } } else { flog(LOG_DEBUG2, "Entry already recorded. Ignoring."); } } /***************************************************************************** * tCompare * This is the compare fn used by the tree handler. * * Inputs: * The two generic pointers are struct in6_addr *. * * Outputs: * None. * * Return: * 0 if item already present, 1 if it was new. * * Notes: * Need to compare two 128 bit numbers! Yucky. * On 64-bit, we could assume long int => 64 bit, but * given we may be on 32-bit, need to assume long int * is max 32 bit, as per ANSI. * * We also make use of the underlying structure of in6_addr, * which is int[16] * * Needs to be moderately efficient, since if we're recording * a lot of addresses we call this via tfind/tsearch quite a lot, * so we do minimal-comparison. */ int tCompare(const void *pa, const void *pb) { int paI=0, pbI=0; int idx; for(idx=0; idx<=15; idx++) { paI = ((struct in6_addr *)pa)->s6_addr[idx]; pbI = ((struct in6_addr *)pb)->s6_addr[idx]; if (paI == pbI) continue; if (paI < pbI) return -1; else return 1; }; // If we reach here, the items were identical return 0; } /***************************************************************************** * tDump * This is the action used when walking tRoot from dumpData() * * Inputs: * node, type of visit, depth - Check the man page for more... * * Outputs: * Data dumped to log. * * Return: * void */ void tDump(const void *nodep, const VISIT which, const int depth) { struct in6_addr *data; char addressString[INET6_ADDRSTRLEN]; switch (which) { case preorder: case endorder: break; case postorder: case leaf: data = *(struct in6_addr **) nodep; print_addr(data, addressString); flog(LOG_INFO, "Address: %s", addressString); break; } } /***************************************************************************** * storeListEntry * * Inputs: * in6_addr *Target - this is the newly seen target to check * * Outputs: * lRoot has a new item added if the address was new. * * Return: * Void */ void storeListEntry(struct in6_addr *newEntry) { struct in6_addr *ptr; // Take a permanenet copy of the target ptr = (struct in6_addr *)malloc(sizeof(struct in6_addr) ); if (!ptr) { flog(LOG_ERR, "Malloc failed. Ignoring."); return; } memcpy(ptr, newEntry, sizeof(struct in6_addr) ); if ( tfind( (void *)ptr, &lRoot, tCompare) == NULL ) { // New entry flog(LOG_DEBUG2, "New list entry"); if ( tsearch( (void *)ptr, &lRoot, tCompare) == NULL) { flog(LOG_ERR, "tsearch failed. Cannot record entry."); return; } } else { flog(LOG_ERR, "Dupe list entry. Ignoring."); } }