From dc8e4f7e9f713ed31aa634bcbf917001aa7f07eb Mon Sep 17 00:00:00 2001 From: Pascal Gloor Date: Sun, 22 Oct 2017 01:08:55 +0200 Subject: [PATCH] Initial v1.1.0 --- LICENSE | 5 +- README.md | 135 +++- clean.sh | 34 + compile.sh | 375 ++++++++++ configure | 3 + etc/piranha_sample.conf | 52 ++ inc/p_config.h | 21 + inc/p_defs.h | 383 +++++++++++ inc/p_dump.h | 45 ++ inc/p_log.h | 24 + inc/p_piranha.h | 546 +++++++++++++++ inc/p_ptoa.h | 24 + inc/p_socket.h | 21 + inc/p_tools.h | 12 + inc/p_undump.h | 22 + man/man1/piranha.1 | 46 ++ man/man1/piranhactl.1 | 52 ++ man/man1/ptoa.1 | 48 ++ man/man5/piranha.conf.5 | 49 ++ src/p_config.c | 371 ++++++++++ src/p_dump.c | 460 +++++++++++++ src/p_log.c | 138 ++++ src/p_piranha.c | 1436 +++++++++++++++++++++++++++++++++++++++ src/p_ptoa.c | 571 ++++++++++++++++ src/p_socket.c | 345 ++++++++++ src/p_tools.c | 131 ++++ src/p_undump.c | 241 +++++++ utils/piranhactl.in | 117 ++++ 28 files changed, 5703 insertions(+), 4 deletions(-) create mode 100755 clean.sh create mode 100755 compile.sh create mode 100755 configure create mode 100644 etc/piranha_sample.conf create mode 100644 inc/p_config.h create mode 100644 inc/p_defs.h create mode 100644 inc/p_dump.h create mode 100644 inc/p_log.h create mode 100644 inc/p_piranha.h create mode 100644 inc/p_ptoa.h create mode 100644 inc/p_socket.h create mode 100644 inc/p_tools.h create mode 100644 inc/p_undump.h create mode 100644 man/man1/piranha.1 create mode 100644 man/man1/piranhactl.1 create mode 100644 man/man1/ptoa.1 create mode 100644 man/man5/piranha.conf.5 create mode 100644 src/p_config.c create mode 100644 src/p_dump.c create mode 100644 src/p_log.c create mode 100644 src/p_piranha.c create mode 100644 src/p_ptoa.c create mode 100644 src/p_socket.c create mode 100644 src/p_tools.c create mode 100644 src/p_undump.c create mode 100755 utils/piranhactl.in diff --git a/LICENSE b/LICENSE index 8dada3e..c129c8f 100644 --- a/LICENSE +++ b/LICENSE @@ -1,3 +1,4 @@ + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -178,7 +179,7 @@ APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -186,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright 2004-2017 Pascal Gloor Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 5560ef7..5a0b018 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,133 @@ -# piranha -BGP route collector written in C +# Piranha +A highly efficient, single threaded, BGP route collector written in C. +Piranha collects routes and dumps them into files for further processing mainly for the purpose of analysis. +Piranha is NOT a BGP router and does NOT have the capability to announce routes nor does it interact with the kernel routing table. + +Piranha supports: +* BGP capabilities negociation. +* BGP 4 octets ASN and AS_PATHs. +* TCP MD5 BGP session protection. +* IPv6 routes over IPv6 sockets. +* IPv4 routes over IPv4 sockets. + +## Installation + +1. Download the latest version at http://xxx +```user@piranha$ wget http://xxx``` +2. Unpack +```user@piranha$ tar -zxvf piranha-1.1.0.tar.gz``` +3. Compilation +```user@piranha$ [sudo] ./compile.sh ``` +```user@piranha$ [sudo] ./compile.sh /opt/piranha/``` +*NOTE: Might need sudo if your destination is not writable by the user.* +4. Done + +## Configuration +Piranha has one configuration file located in /etc/piranha.conf. In the same folder there is a sample configuration name piranha_sample.conf. Copy the file to piranha.conf and edit it. + +### piranha.conf +``` +# your local AS number +local_as + +# Listening IP/Port for IPv4 peers. If omitted, piranha will not listen for IPv4 connections. +local_ip4 +local_port4 + +# Listening IP/Port for IPv6 peers. If omitted, piranha will not listen for IPv6 connections. +local_ip6 +local_port6 + +# Export options: Choose which route attributes will be exported to the dump files +export origin # IGP/EGP/Unknown +export aspath # AS_PATH +export community # COMMUNITY +export extcommunity # EXTENDED COMMUNITY + +# BGP Router Identifier. This MUST be set and may not be 0.0.0.0. +# If you don't know what to put in this option, just copy your public IPv4 +# address. +bgp_router_id + +# The user that piranha will run as. Because piranha needs +# tcp port 179, it must be started as root. Piranha will then +# operator a privilege downgrade to this use for obvious security +# reasons. +user nobody + +# Finally you must configure your BGP neighbors +# You may configure up to 128 neighbors +# (this can be changed in inc/p_defs.h:#define MAX_PEERS 128) +# The password is optional and is implemented as defined in RFC5425 +neighbor [password] +``` +## Usage +### Start/Stop/Restart + /etc/piranhactl +### Status (state of all neighbors) + cat /var/piranha.status +### MAN Pages + man -M /man + +## Reading Piranha DUMP +Piranha dumps the received BGP Updates into dump files located in /dump/. Files are rotated by default every 60 seconds. If there was no BGP message during that time, the dump not created for performance reasons. The 60 seconds interval can be tuned prior to compilation in `inc/p_defs.h:#define DUMPINTERVAL 60`. +Dump files ready to be read have the following format: `YYYYMMddhhmmss`. +With the tool `/bin/ptoa` data from the dump files can be exported in three different formats: + +* `./ptoa -H `: Human readable format +* `./ptoa -m `: Machine readable format +* `./ptoa -j `: JSON format + +### Examples +#### Human readable format +``` +2017-10-21 21:31:54 peer ip 2a03:2260::5 AS 201701 +2017-10-21 21:31:54 prefix announce 2a06:dac0::/29 origin IGP aspath 201701 13030 25180 202939 community 5093:5349 6629:6885 7141:7397 +2017-10-21 21:31:55 eof +``` +#### Machine readable format +``` +1508621514|P|2a03:2260::5|201701 +1508621514|A|2a06:dac0::|29|O|I|AP|201701 13030 25180 202939|C|5093:5349 5605:5861 6629:6885 7141:7397 +1508621515|E +``` +#### JSON format +``` +{ "timestamp": 1508621514, "type": "peer", "msg": { "peer": { "proto": "ipv6", "ip": "2a03:2260::5", "asn": 201701 } } } +{ "timestamp": 1508621514, "type": "announce", "msg": { "prefix": "2a06:dac0::/29", "origin": "IGP", "aspath": [ 201701, 13030, 25180, 202939 ], "community": [ "5093:5349", "5605:5861", "6629:6885", "7141:7397" ] } } +{ "timestamp": 1508621515, "type": "footer" } +``` + +### Message type tags in DUMPs +Colons can be used to align columns. + +| Human | Machine | JSON | Description | +|-|-|-|-| +| peer | P | peer | First message in any dump describing the neighbor | +| announce | A | announce | BGP prefix announce, optional origin (O), aspath (AP), community (C) and extended community (EC) subcomponents | +| withdrawn | W | withdrawn | BGP prefix withdrawn | +| eof | E | footer | Last message in any dump, has no other value | + + +## Conformity +Piranha implements partially or completely the following RFCs: +* RFC1997: BGP Communities Attribute +* RFC4360: BGP Extended Communities Attribute +* RFC4760: Multiprotocol Extensions for BGP-4 +* RFC4271: A Border Gateway Protocol 4 (BGP-4) +* RFC5425: The TCP Authentication Option +* RFC5492: Capabilities Advertisement with BGP-4 +* RFC6793: BGP Support for Four-Octet Autonomous System (AS) Number Space + +## Limitations +* Config reload does not work and may lead to a crash. +* Extended communities are not yet supported. +* Piranha is not able to communicate with BGP speakers not conforming to RFC5492 (old speakers). +* Might not work on 32bits platforms (time_t handling must be improved). + +## Copyright + +*Copyright 2004-2017 Pascal Gloor* +*Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0* +*Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.* + diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..9c89c7c --- /dev/null +++ b/clean.sh @@ -0,0 +1,34 @@ +#!/bin/sh + +# /*******************************************************************************/ +# /* */ +# /* Copyright 2004-2017 Pascal Gloor */ +# /* */ +# /* Licensed under the Apache License, Version 2.0 (the "License"); */ +# /* you may not use this file except in compliance with the License. */ +# /* You may obtain a copy of the License at */ +# /* */ +# /* http://www.apache.org/licenses/LICENSE-2.0 */ +# /* */ +# /* Unless required by applicable law or agreed to in writing, software */ +# /* distributed under the License is distributed on an "AS IS" BASIS, */ +# /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +# /* See the License for the specific language governing permissions and */ +# /* limitations under the License. */ +# /* */ +# /*******************************************************************************/ + + + +# Piranha source cleaning script + +for file in bin/piranha bin/ptoa obj/*.o *.core utils/piranhactl +do + if [ -r "$file" ] + then + echo "deleting $file..." + rm $file + fi +done + +echo "cleaned"; diff --git a/compile.sh b/compile.sh new file mode 100755 index 0000000..b7c0058 --- /dev/null +++ b/compile.sh @@ -0,0 +1,375 @@ +#!/bin/bash -e + +# /*******************************************************************************/ +# /* */ +# /* Copyright 2004-2017 Pascal Gloor */ +# /* */ +# /* Licensed under the Apache License, Version 2.0 (the "License"); */ +# /* you may not use this file except in compliance with the License. */ +# /* You may obtain a copy of the License at */ +# /* */ +# /* http://www.apache.org/licenses/LICENSE-2.0 */ +# /* */ +# /* Unless required by applicable law or agreed to in writing, software */ +# /* distributed under the License is distributed on an "AS IS" BASIS, */ +# /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +# /* See the License for the specific language governing permissions and */ +# /* limitations under the License. */ +# /* */ +# /*******************************************************************************/ + + + +progs="grep awk sed cp mkdir chmod printf"; + +tab_print() +{ + first=1; + + for name in $2 + do + if [ "$first" -eq "1" ] + then + printf "%-9s : %s\n" $1 $name + else + printf " %s\n" $name + + fi + + first=0; + done +} + +addpath() +{ + if [ -r "${1}.in" ] + then + MYPATH=`echo ${DIR} | sed "s/\//\\\\\\\\\\//g"`; + cat ${1}.in | sed "s/\%PATH\%/${MYPATH}/g" > ${1}; + else + echo "error cannot read ${1}.in"; + exit 1; + fi +} + +create_dir() +{ + printf "creating directory "; + if [ -r "${1}" ] + then + if [ -d "${1}" ] + then + echo "${1} ok (exists)"; + else + echo "${1} exist and is NOT a directory, sorry."; + exit 1; + fi + else + mkdir -p ${1}; + if [ "$?" != 0 ] + then + echo "error creating ${1}, sorry."; + exit 1; + fi + echo "${1} ok (created)"; + fi +} + +copy_file() +{ + printf "installing %s (mode %s)\n" ${2} ${3} + + cp -f ${1} ${2}; + if [ "$?" != 0 ]; then echo "error copying file, sorry"; exit 1; fi + + chmod ${3} ${2}; + if [ "$?" != 0 ]; then echo "error changing file mode, sorry"; exit 1; fi +} + +locate_prog() +{ + LOC=`which ${1} 2>&1`; + if [ ! -x "${LOC}" ] + then + echo "looking for ${1}... not found ;("; + echo "Please check your PATH environement variable"; + exit 1; + fi +} + +for myprog in $progs +do + locate_prog $myprog +done + +P_VER_MA=`grep P_VER_MA inc/p_defs.h | awk '{print $3}' | sed "s/\"//g" `; +P_VER_MI=`grep P_VER_MI inc/p_defs.h | awk '{print $3}' | sed "s/\"//g" `; +P_VER_PL=`grep P_VER_PL inc/p_defs.h | awk '{print $3}' | sed "s/\"//g" `; + +if [ -z "$1" ]; then + echo "Piranha v${P_VER_MA}.${P_VER_MI}.${P_VER_PL} BGP Daemon, compilation/installation script."; + echo "" + echo "syntax: $0 [debug]"; + echo "" + echo "directory : the place where you want to install Piranha,"; + echo " /usr/local/piranha sounds a good place."; + echo "debug : optional" + echo ""; + exit 1; +fi + +if [ -n "$2" ]; then DODEBUG=1; fi + +DIR=`echo $1 | sed "s/\/$//" | sed "s/\/\//\//g"`; + +cat <&1`" ] +then + cc="gcc"; + CC="GCC"; + debug="-g -DDEBUG"; + + if [ "$OS" = "FREEBSD" ] + then + warn="-Wall -Wshadow -Wcast-qual -Wcast-align -Wpointer-arith \ + -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \ + -Wredundant-decls -Wnested-externs -Werror"; + opt="-O2 -ansi -pedantic -fsigned-char"; + lib="-pthread -D_THREAD_SAFE"; + elif [ "$OS" = "NETBSD" ] + then + warn="-Wall -Wshadow -Wcast-qual -Wcast-align -Wpointer-arith \ + -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \ + -Wredundant-decls -Wnested-externs -Werror"; + opt="-O2 -ansi -pedantic -fsigned-char"; + lib="-lpthread"; + elif [ "$OS" = "LINUX" ] + then + warn="-Wall -Wshadow -Wcast-qual -Wcast-align -Wpointer-arith \ + -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \ + -Wnested-externs "; + lib="-lpthread"; + opt="-O2 -fsigned-char"; + elif [ "$OS" = "DARWIN" ] + then + warn="-Wall -Wshadow -Wcast-qual -Wpointer-arith \ + -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations \ + -Wnested-externs -Werror"; +# lib="-lpthread"; + opt="-O2 -fsigned-char"; + else + echo "WARNING: You're running an untested OS (${OS} ${OSVER}). " \ + "The compilation might not work."; + warn="-Wall" + opt="-O2 -ansi -pedantic -fsigned-char"; + lib="-lpthread"; + fi + +elif [ -x "`which cc 2>&1`" ] +then +cat < +EOF + exit 1; +else + + echo "ERROR: No C compiler found. Sorry." + exit 1; +fi + +if [ -z "$DODEBUG" ]; then debug=""; fi + +echo "compiler and options we'll use :" +echo "" +echo "system : $OS $OSVER" +echo "compiler : $cc"; +tab_print "options" "$opt" +tab_print "warnings" "$warn" +tab_print "libraries" "$lib" + +if [ -n "${DODEBUG}" ] +then + echo "debug -> $debug"; +fi + +echo "" + + +incdir="-Iinc" +libdir="" +srcdir="src" +objdir="obj" + +src=( \ + "p_tools p_config p_socket p_log p_dump p_piranha" \ + "p_tools p_undump p_ptoa" \ +) + +prog=( \ + "bin/piranha" \ + "bin/ptoa" \ +) + +tmp=( '' '' ) + +opt="$opt -D${OS} -D${CC}"; + +for ((i = 0; i < ${#src[@]}; i++)) +do + for compile in ${src[$i]} + do + if [ -n "${DODEBUG}" ] + then + echo "$cc $debug -DPATH=\"${DIR}\" $libdir $incdir $opt $warn " \ + "-o $objdir/$compile.o -c $srcdir/$compile.c"; + else + printf "compiling %-17s -> %-17s ... " \ + $srcdir/$compile.c $objdir/$compile.o + fi + + $cc $debug -DPATH=\"${DIR}\" $libdir $incdir $opt $warn \ + -o $objdir/$compile.o -c $srcdir/$compile.c + + if [ "$?" -ne "0" ] + then + echo "ERROR: compilation error" + exit 1; + else + echo "ok"; + fi + + tmp[$i]="${tmp[$i]} $objdir/$compile.o" + done +done + +for ((i = 0; i < ${#src[@]}; i++)) +do + if [ -n "${DODEBUG}" ] + then + echo "$cc $debug $libdir $incdir $lib $warn -o ${prog[$i]} ${tmp[$i]}"; + else + echo ""; + tab_print "linking" "${tmp[$i]} [${prog[$i]}]" + fi +done + +for ((i = 0; i < ${#src[@]}; i++)) +do + $cc $debug $libdir $incdir $warn -o ${prog[$i]} ${tmp[$i]} $lib +done + +if [ "$?" -ne "0" ] +then + echo "ERROR: linking error" + exit 1; +else + echo "ok"; +fi + +echo "" +echo "compilation done ;-)" +echo "" + +printf "Install Piranha to ${DIR} ? ([Y]/n) " +read test + +if [ -z "$test" ]; then test="Y"; fi; + +case "$test" in + +[Y]|[y]) + ;; +*) + echo "restart \"$0\" at any time to finish the installation."; + echo "installation aborted"; + exit 1; + ;; +esac + +echo ""; + +create_dir "${DIR}/bin"; +create_dir "${DIR}/etc"; +create_dir "${DIR}/var"; +create_dir "${DIR}/dump"; +create_dir "${DIR}/man/man1"; +create_dir "${DIR}/man/man5"; + +echo "" + +addpath "./utils/piranhactl"; + +for ((i = 0; i < ${#src[@]}; i++)) +do + copy_file ./${prog[$i]} ${DIR}/${prog[$i]} 555 +done + +copy_file ./etc/piranha_sample.conf ${DIR}/etc/piranha_sample.conf 644 +copy_file ./utils/piranhactl ${DIR}/bin/piranhactl 555 + +copy_file ./man/man1/piranha.1 ${DIR}/man/man1/piranha.1 444 +copy_file ./man/man1/piranhactl.1 ${DIR}/man/man1/piranhactl.1 444 +copy_file ./man/man1/ptoa.1 ${DIR}/man/man1/ptoa.1 444 +copy_file ./man/man5/piranha.conf.5 ${DIR}/man/man5/piranha.conf.5 444 + +echo "/-----------------------------------------------------------------"; +echo "|"; +echo "| Piranha has been installed to ${DIR}"; +echo "|"; +echo "| start: ${DIR}/bin/piranhactl start"; +echo "|"; +echo "| Please read the doc/piranha.txt file and the sample configuration"; +echo "| (${DIR}/etc/piranha_sample.conf)"; +echo "|"; +echo "| manpages available!"; +echo "| man -M${DIR}/man piranha"; +echo "| man -M${DIR}/man piranha.conf"; +echo "| man -M${DIR}/man piranhactl"; +echo "| man -M${DIR}/man ptoa"; +echo "|"; +echo "\\------------------------------------------------------------------"; diff --git a/configure b/configure new file mode 100755 index 0000000..586dd2c --- /dev/null +++ b/configure @@ -0,0 +1,3 @@ +#!/bin/sh + +echo "Please use ./compile.sh" diff --git a/etc/piranha_sample.conf b/etc/piranha_sample.conf new file mode 100644 index 0000000..595459e --- /dev/null +++ b/etc/piranha_sample.conf @@ -0,0 +1,52 @@ +# Piranha BGP Daemon configuration file +# +# [local_as] +# Local autonmous system number + +local_as 65500 + + +# [local_ip] +# Local IP Address to listen on. +# must be set in order to work. +# if you do not want to support ipv4 or ipv6 +# comment the local_ipX out. + +local_ip4 10.0.0.1 +local_ip6 fe80::1 + + +# [local_port] (default:179) +# Local port in which you want to listen(). + +local_port4 179 +local_port6 179 + + +# [export] (default: none) +# choose which route attributes to export +# in dump files + +export origin +export aspath +#export community +#export extcommunity + + +# [bgp_router_id] +# BGP Router identifier, MUST be set to something else +# than 0.0.0.0 ! + +bgp_router_id 10.0.0.1 + + +# [user] + +user nobody + + +# [neighbor] +# neighbors/peers definition +# neighbor [optional password] + +neighbor 10.0.0.2 65500 MyPassword diff --git a/inc/p_config.h b/inc/p_config.h new file mode 100644 index 0000000..218dc8b --- /dev/null +++ b/inc/p_config.h @@ -0,0 +1,21 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +int p_config_load(struct config_t *config, struct peer_t *peer, uint32_t mytime); +void p_config_add_peer(struct peer_t *peer, uint8_t af, struct in_addr *peer_ip4, struct in6_addr *peer_ip6, uint32_t as, char *key, uint32_t mytime); diff --git a/inc/p_defs.h b/inc/p_defs.h new file mode 100644 index 0000000..2d0b147 --- /dev/null +++ b/inc/p_defs.h @@ -0,0 +1,383 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + +#include +#include +#include +#include +#include + +#ifdef DARWIN +#include +#define htobe16(x) OSSwapHostToBigInt16(x) +#define htole16(x) OSSwapHostToLittleInt16(x) +#define be16toh(x) OSSwapBigToHostInt16(x) +#define le16toh(x) OSSwapLittleToHostInt16(x) +#define htobe32(x) OSSwapHostToBigInt32(x) +#define htole32(x) OSSwapHostToLittleInt32(x) +#define be32toh(x) OSSwapBigToHostInt32(x) +#define le32toh(x) OSSwapLittleToHostInt32(x) +#define htobe64(x) OSSwapHostToBigInt64(x) +#define htole64(x) OSSwapHostToLittleInt64(x) +#define be64toh(x) OSSwapBigToHostInt64(x) +#define le64toh(x) OSSwapLittleToHostInt64(x) +#else +#include +#endif + +#ifndef PIRANHA_DEFS +#define PIRANHA_DEFS + +#include +#include +#include + +#define P_VER_MA "1" +#define P_VER_MI "1" +#define P_VER_PL "0" + +#define MAX_PEERS 128 +#define DUMPINTERVAL 5 + +#ifdef LINUX +#define MAX_KEY_LEN (TCP_MD5SIG_MAXKEYLEN+1) +#else +#define MAX_KEY_LEN 1 +#endif + +#ifndef PATH +#define PATH "./" +#endif + +#define CHOMP(x) if ( x[strlen(x)-2]=='\r' ) { x[strlen(x)-2] = '\0'; } else if ( x[strlen(x)-1]=='\n' ) { x[strlen(x)-1] = '\0'; } + +#if !defined(s6_addr32) +# if defined(FREEBSD) || defined(NETBSD) || defined(OPENBSD) || defined(DARWIN)|| defined(DRAGONFLY) +# define s6_addr32 __u6_addr.__u6_addr32 +# endif +#endif + +#define LOGFILE PATH "/var/piranha.log" +#define STATUSFILE PATH "/var/piranha.status" +#define STATUSTEMP PATH "/var/piranha.status.temp" +#define PIDFILE PATH "/var/piranha.pid" +#define DUMPDIR PATH "/dump" + +#define INPUT_BUFFER 1048576 +#define OUTPUT_BUFFER 65536 +#define TEMP_BUFFER 65536 + +#define BGP_HEADER_LEN 19 +#define BGP_OPEN_LEN 10 +#define BGP_ERROR_LEN 8 + +#define BGP_DEFAULT_HOLD 180 + +#define BGP_OPEN 1 +#define BGP_UPDATE 2 +#define BGP_ERROR 3 +#define BGP_KEEPALIVE 4 + +#define BGP_ATTR_ORIGIN 1 +#define BGP_ATTR_AS_PATH 2 +#define BGP_ATTR_NEXT_HOP 3 +#define BGP_ATTR_COMMUNITY 8 +#define BGP_ATTR_MP_REACH_NLRI 14 +#define BGP_ATTR_MP_UNREACH_NLRI 15 +#define BGP_ATTR_EXTCOMMUNITY 16 + +#define EXPORT_ORIGIN 0x01 +#define EXPORT_ASPATH 0x02 +#define EXPORT_COMMUNITY 0x04 +#define EXPORT_EXTCOMMUNITY 0x08 + +#define DUMP_OPEN 10 +#define DUMP_CLOSE 11 +#define DUMP_KEEPALIVE 12 + +#define DUMP_HEADER4 40 +#define DUMP_ANNOUNCE4 41 +#define DUMP_WITHDRAWN4 42 + +#define DUMP_HEADER6 60 +#define DUMP_ANNOUNCE6 61 +#define DUMP_WITHDRAWN6 62 + +#define DUMP_FOOTER 255 + +#ifdef SUNCC +#pragma packed() +#endif + +struct bgp_header +{ + char marker[16]; + uint16_t len; + uint8_t type; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct bgp_open +{ + uint8_t version; + uint16_t as; + uint16_t holdtime; + uint32_t bgp_id; + uint8_t param_len; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct bgp_param +{ + uint8_t type; + uint8_t len; + char param[256]; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct bgp_param_capa +{ + uint8_t type; + uint8_t len; + union { + char def[256]; + uint32_t as4; + } u; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct bgp_error +{ + uint8_t code; + uint8_t subcode; + char data[6]; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_msg +{ + uint8_t type; + uint16_t len; + uint64_t ts; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_header4 +{ + uint32_t ip; + uint32_t as; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_header6 +{ + uint8_t ip[16]; + uint32_t as; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_withdrawn4 +{ + uint8_t mask; + uint32_t prefix; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_withdrawn6 +{ + uint8_t mask; + uint8_t prefix[16]; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_announce4 +{ + uint8_t mask; + uint32_t prefix; + uint8_t origin; + uint8_t aspathlen; + uint8_t communitylen; + uint8_t extcommunitylen; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_announce_aspath +{ + uint32_t data[256]; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_announce6 +{ + uint8_t mask; + uint8_t prefix[16]; + uint8_t origin; + uint8_t aspathlen; + uint8_t communitylen; + uint8_t extcommunitylen; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_announce_community +{ + struct { + uint16_t asn; + uint16_t num; + } data[256]; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_announce_extcommunity +{ + struct { + uint32_t ip; + uint32_t num; + } data[256]; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_full_msg +{ + struct dump_msg msg; + union { + struct dump_header4 header4; + struct dump_header6 header6; + struct dump_announce4 announce4; + struct dump_announce6 announce6; + struct dump_withdrawn4 withdrawn4; + struct dump_withdrawn6 withdrawn6; + }; + struct dump_announce_aspath aspath; + struct dump_announce_community community; + struct dump_announce_extcommunity extcommunity; +#ifdef GCC +} __attribute__((packed)); +#else +}; +#endif + +struct dump_file_ctx +{ + char file[PATH_MAX]; + FILE *fh; + int head; + int end; + int pos; +}; + + + + +struct config_t +{ + struct { + struct sockaddr_in listen; + int sock; + int enabled; + } ip4; + struct { + struct sockaddr_in6 listen; + int sock; + int enabled; + } ip6; + uint8_t export; + uint32_t as; + uint32_t routerid; + uint16_t holdtime; + uid_t uid; + gid_t gid; + char *file; + struct peer_t *peer; +}; + +struct peer_t +{ + uint8_t allow; + uint8_t newallow; /* to avoid peer drop during reconfiguration */ + uint8_t status; /* 0 offline, 1 connected, 2 authed */ + uint32_t ucount; /* bgp updates count */ + union { + struct in6_addr ip6; /* peer IPv4 address */ + struct in_addr ip4; /* peer IPv6 address */ + }; + uint8_t af; /* indicates wether peer is v4 or v6 */ + uint32_t as; /* ASN */ + char key[MAX_KEY_LEN]; /* MD5 authentication, null terminated */ + uint32_t rmsg; + uint32_t smsg; + uint64_t cts; + uint64_t rts; + uint64_t sts; + uint16_t rhold; + uint16_t shold; + uint8_t as4; /* neighbor 4 bytes AS advertised capability support. */ + FILE *fh; + uint8_t empty; + char filename[1024]; + uint64_t filets; + int ilen; + int olen; + int sock; +}; + +#endif diff --git a/inc/p_dump.h b/inc/p_dump.h new file mode 100644 index 0000000..e15b8d0 --- /dev/null +++ b/inc/p_dump.h @@ -0,0 +1,45 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +void p_dump_open_file (struct peer_t *peer, int id, uint64_t ts); +void p_dump_add_open (struct peer_t *peer, int id, uint64_t ts); +void p_dump_add_close (struct peer_t *peer, int id, uint64_t ts); +void p_dump_add_keepalive (struct peer_t *peer, int id, uint64_t ts); +void p_dump_add_header4 (struct peer_t *peer, int id, uint64_t ts); +void p_dump_add_header6 (struct peer_t *peer, int id, uint64_t ts); +void p_dump_add_footer (struct peer_t *peer, int id, uint64_t ts); +void p_dump_check_file (struct peer_t *peer, int id, uint64_t ts); +void p_dump_close_file (struct peer_t *peer, int id); + +void p_dump_add_withdrawn4 (struct peer_t *peer, int id, uint64_t ts, + uint32_t prefix, uint8_t mask); +void p_dump_add_withdrawn6 (struct peer_t *peer, int id, uint64_t ts, + uint8_t prefix[16], uint8_t mask); + +void p_dump_add_announce4 (struct peer_t *peer, int id, uint64_t ts, + uint32_t prefix, uint8_t mask, uint8_t origin, + void *aspath, uint8_t aspathlen, + void *community, uint8_t communitylen, + void *extcommunity, uint8_t extcommunitylen ); + +void p_dump_add_announce6 (struct peer_t *peer, int id, uint64_t ts, + uint8_t prefix[16], uint8_t mask, uint8_t origin, + void *aspath, uint8_t aspathlen, + void *community, uint8_t communitylen, + void *extcommunity, uint8_t extcommunitylen ); diff --git a/inc/p_log.h b/inc/p_log.h new file mode 100644 index 0000000..62f613f --- /dev/null +++ b/inc/p_log.h @@ -0,0 +1,24 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + + +void p_log_pid(void); +void p_log_add(time_t mytime, char *line); +void p_log_easytime(time_t mytime, char *timestr, int timestrlen); +void p_log_status(struct config_t *config, struct peer_t *peer, time_t mytime); diff --git a/inc/p_piranha.h b/inc/p_piranha.h new file mode 100644 index 0000000..b119a11 --- /dev/null +++ b/inc/p_piranha.h @@ -0,0 +1,546 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +int main(int argc, char *argv[]); +int p_main_loop(void); +void *p_main_peer(void *data); +void p_main_peer_exit(void *data, int sock); +void p_main_peer_work(char *ibuf, char *obuf, int id); +void p_main_peer_open(int id, char *obuf); +void p_main_peer_send(int id, char *obuf); +void p_main_peer_loop(int id); +void p_main_syntax(char *prog); +void p_main_sighup(int sig); +int mydaemon(int nochdir, int noclose); +int mychown(char *path, uid_t uid, gid_t gid, int depth); + +#ifdef DEBUG +const static char *bgp_origin[256] = { "IGP", "EGP", "UNKNOWN" }; + +const static char *bgp_path_attribute[256] = { + "Reserved", + "ORIGIN", + "AS_PATH", + "NEXT_HOP", + "MULTI_EXIT_DISC", + "LOCAL_PREF", + "ATOMIC_AGGREGATE", + "AGGREGATOR", + "COMMUNITY", + "ORIGINATOR_ID", + "CLUSTER_LIST", + "DPA (deprecated)", + "ADVERTISER (historic, deprecated)", + "RCID_PATH / CLUSTER_ID (historic, deprecated)", + "MP_REACH_NLRI", + "MP_UNREACH_NLRI", + "EXTENDED COMMUNITIES", + "AS4_PATH", + "AS4_AGGREGATOR", + "SAFI Specific Attribute (SSA) (deprecated)", + "Connector Attribute (deprecated)", + "AS_PATHLIMIT (deprecated)", + "PMSI_TUNNEL", + "Tunnel Encapsulation Attribute", + "Traffic Engineering", + "IPv6 Address Specific Extended Community", + "AIGP", + "PE Distinguisher Labels", + "BGP Entropy Label Capability Attribute (deprecated)", + "BGP-LS Attribute", + "Deprecated", + "Deprecated", + "LARGE_COMMUNITY", + "BGPsec_Path", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned" }; + +const static char *bgp_capability[256] = { + "Reserved", + "Multiprotocol Extensions for BGP-4", + "Route Refresh Capability for BGP-4", + "Outbound Route Filtering Capability", + "Multiple routes to a destination capability (deprecated)", + "Extended Next Hop Encoding", + "BGP-Extended Message (TEMPORARY - registered 2015-09-30, extension registered 2017-08-31, expires 2018-09-30)", + "BGPsec Capability", + "Multiple Labels Capability", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Graceful Restart Capability", + "Support for 4-octet AS number capability", + "Deprecated (2003-03-06)", + "Support for Dynamic Capability (capability specific)", + "Multisession BGP Capability", + "ADD-PATH Capability", + "Enhanced Route Refresh Capability", + "Long-Lived Graceful Restart (LLGR) Capability", + "Unassigned", + "FQDN Capability", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Unassigned", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", + "Reserved (Private use)", +}; + +#endif diff --git a/inc/p_ptoa.h b/inc/p_ptoa.h new file mode 100644 index 0000000..3ab8660 --- /dev/null +++ b/inc/p_ptoa.h @@ -0,0 +1,24 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + +enum PTOA_MODE { PTOA_NONE, PTOA_HUMAN, PTOA_JSON, PTOA_MACHINE }; + + +int main(int argc, char *argv[]); +void mytime(time_t ts); +void syntax(char *prog); diff --git a/inc/p_socket.h b/inc/p_socket.h new file mode 100644 index 0000000..0e17649 --- /dev/null +++ b/inc/p_socket.h @@ -0,0 +1,21 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +int p_socket_start(struct config_t *config, struct peer_t *peer); +int p_socket_accept(struct config_t *config); diff --git a/inc/p_tools.h b/inc/p_tools.h new file mode 100644 index 0000000..8675829 --- /dev/null +++ b/inc/p_tools.h @@ -0,0 +1,12 @@ +int p_tools_ip4zero(struct in_addr *ip); +int p_tools_ip6zero(struct in6_addr *ip); + +int p_tools_sameip4(struct in_addr *ip1, struct in_addr *ip2); +int p_tools_sameip6(struct in6_addr *ip1, struct in6_addr *ip2); + +char *p_tools_ip4str(int peerid, struct in_addr *ip); +char *p_tools_ip6str(int peerid, struct in6_addr *ip); + +void p_tools_dump(const char *desc, char *data, int len); + +void p_tools_humantime(char *line, size_t len, time_t ts); diff --git a/inc/p_undump.h b/inc/p_undump.h new file mode 100644 index 0000000..fbd3707 --- /dev/null +++ b/inc/p_undump.h @@ -0,0 +1,22 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +struct dump_file_ctx *p_undump_open(char *file); +int p_undump_close(struct dump_file_ctx *ctx); +int p_undump_readmsg(struct dump_file_ctx *ctx, struct dump_full_msg *fmsg); diff --git a/man/man1/piranha.1 b/man/man1/piranha.1 new file mode 100644 index 0000000..f2fee3e --- /dev/null +++ b/man/man1/piranha.1 @@ -0,0 +1,46 @@ +.\"/*******************************************************************************/ +.\"/* */ +.\"/* Copyright 2004-2017 Pascal Gloor */ +.\"/* */ +.\"/* Licensed under the Apache License, Version 2.0 (the "License"); */ +.\"/* you may not use this file except in compliance with the License. */ +.\"/* You may obtain a copy of the License at */ +.\"/* */ +.\"/* http://www.apache.org/licenses/LICENSE-2.0 */ +.\"/* */ +.\"/* Unless required by applicable law or agreed to in writing, software */ +.\"/* distributed under the License is distributed on an "AS IS" BASIS, */ +.\"/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +.\"/* See the License for the specific language governing permissions and */ +.\"/* limitations under the License. */ +.\"/* */ +.\"/*******************************************************************************/ +.Dd Aug 8, 2004 +.Dt PIRANHA 1 +.Os +.Sh NAME +.Nm piranha +.Nd BGP Route Collector Daemon +.Sh SYNOPSIS +.Nm +.Op Ar configuration file +.Sh DESCRIPTION +The +.Nm +daemon collects bgp updates from neighbors and dump them into files. +.Pp +.Sh SEE ALSO +.Xr ptoa 1 +.Xr piranhactl 1 +.Xr piranha.conf 5 +.Sh STANDARDS +The +.Nm +daemon is expected to be compliant to: +RFC1997(BGP Communities Attribute), +RFC4360(BGP Extended Communities Attribute), +RFC4760(Multiprotocol Extensions for BGP-4), +RFC4271(A Border Gateway Protocol 4 (BGP-4), +RFC5425(The TCP Authentication Option), +RFC5492(Capabilities Advertisement with BGP-4), +RFC6793(BGP Support for Four-Octet Autonomous System (AS) Number Space), diff --git a/man/man1/piranhactl.1 b/man/man1/piranhactl.1 new file mode 100644 index 0000000..889c9bd --- /dev/null +++ b/man/man1/piranhactl.1 @@ -0,0 +1,52 @@ +.\"/*******************************************************************************/ +.\"/* */ +.\"/* Copyright 2004-2017 Pascal Gloor */ +.\"/* */ +.\"/* Licensed under the Apache License, Version 2.0 (the "License"); */ +.\"/* you may not use this file except in compliance with the License. */ +.\"/* You may obtain a copy of the License at */ +.\"/* */ +.\"/* http://www.apache.org/licenses/LICENSE-2.0 */ +.\"/* */ +.\"/* Unless required by applicable law or agreed to in writing, software */ +.\"/* distributed under the License is distributed on an "AS IS" BASIS, */ +.\"/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +.\"/* See the License for the specific language governing permissions and */ +.\"/* limitations under the License. */ +.\"/* */ +.\"/*******************************************************************************/ +.Dd Aug 8, 2004 +.Dt PIRANHACTL 1 +.Os +.Sh NAME +.Nm piranhactl +.Xr piranha 1 +.Nd process control +.Sh SYNOPSIS +.Nm +.Op Ar start stop reload restart status +.Sh DESCRIPTION +The +.Nm +script allows control of the +.Xr piranha 1 +daemon. +.Pp +The following options are available: +.Bl -tag -width indent +.It Ar start +starts the daemon. +.It Ar stop +stops the daemon. +.It Ar restart +stops and starts the daemon. +.It Ar reload +reloads the daemon configuration. equivalent to +.Xr kill 1 +-HUP +.It Ar status +shows neighbors status +.Sh SEE ALSO +.Xr piranha 1 +.Xr piranhactl 1 +.Xr piranha.conf 5 diff --git a/man/man1/ptoa.1 b/man/man1/ptoa.1 new file mode 100644 index 0000000..c769de1 --- /dev/null +++ b/man/man1/ptoa.1 @@ -0,0 +1,48 @@ +.\"/*******************************************************************************/ +.\"/* */ +.\"/* Copyright 2004-2017 Pascal Gloor */ +.\"/* */ +.\"/* Licensed under the Apache License, Version 2.0 (the "License"); */ +.\"/* you may not use this file except in compliance with the License. */ +.\"/* You may obtain a copy of the License at */ +.\"/* */ +.\"/* http://www.apache.org/licenses/LICENSE-2.0 */ +.\"/* */ +.\"/* Unless required by applicable law or agreed to in writing, software */ +.\"/* distributed under the License is distributed on an "AS IS" BASIS, */ +.\"/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +.\"/* See the License for the specific language governing permissions and */ +.\"/* limitations under the License. */ +.\"/* */ +.\"/*******************************************************************************/ +.Dd Aug 8, 2004 +.Dt PTOA 1 +.Os +.Sh NAME +.Nm ptoa +.Xr piranha 1 +.Nd dump file decoder +.Sh SYNOPSIS +.Nm +.Op Fl m H j +.Op Ar dump file +.Sh DESCRIPTION +The +.Nm +daemon collects bgp updates from neighbors and dump them into files. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl m +Machine readable output. +.It Fl H +Human readable output. +.It Fl j +JSON. +.It Ar dump file +.Xr piranha 1 +dump file. +.Sh SEE ALSO +.Xr piranha 1 +.Xr piranhactl 1 +.Xr piranha.conf 5 diff --git a/man/man5/piranha.conf.5 b/man/man5/piranha.conf.5 new file mode 100644 index 0000000..6347039 --- /dev/null +++ b/man/man5/piranha.conf.5 @@ -0,0 +1,49 @@ +.\"/*******************************************************************************/ +.\"/* */ +.\"/* Copyright 2004-2017 Pascal Gloor */ +.\"/* */ +.\"/* Licensed under the Apache License, Version 2.0 (the "License"); */ +.\"/* you may not use this file except in compliance with the License. */ +.\"/* You may obtain a copy of the License at */ +.\"/* */ +.\"/* http://www.apache.org/licenses/LICENSE-2.0 */ +.\"/* */ +.\"/* Unless required by applicable law or agreed to in writing, software */ +.\"/* distributed under the License is distributed on an "AS IS" BASIS, */ +.\"/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +.\"/* See the License for the specific language governing permissions and */ +.\"/* limitations under the License. */ +.\"/* */ +.\"/*******************************************************************************/ +.Dd Aug 8, 2004 +.Dt PIRANHA.CONF 5 +.Os +.Sh NAME +.Nm piranha.conf +.Xr piranha 1 +.Nd configuration file +.Sh DESCRIPTION +.Bl -tag -width indent +.It Ar local_as +Local AS number (MANDATORY, no default value). +.It Ar local_port4 +Local port. Its not recommended to change it as some daemons do not support to specify the remote port (OPTIONAL, default 179). +.It Ar local_port6 +Local port. Its not recommended to change it as some daemons do not support to specify the remote port (OPTIONAL, default 179). +.It Ar local_ip4 +Local IPv4 to listen to. +.It Ar local_ip6 +Local IPv6 to listen to. +.It Ar export [origin|aspath|community|extcommunity] +Choose which attributes to export. +.It Ar bgp_router_id +BGP Router identifier, (MANDATORY, no default value, may NOT be 0.0.0.0). +.It Ar neighbor <(ipv4|ipv6)_address> [password] +Defines a BGP peer/neighbor. You may add as many as you want. The unique identifier is the ip address (OPTIONAL, no default value). +.It Ar user +An unpriviledged user. +.Pp +.Sh SEE ALSO +.Xr piranha 1 +.Xr piranhactl 1 +.Xr ptoa 1 diff --git a/src/p_config.c b/src/p_config.c new file mode 100644 index 0000000..5af93d5 --- /dev/null +++ b/src/p_config.c @@ -0,0 +1,371 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* reading configuration file */ + +int p_config_load(struct config_t *config, struct peer_t *peer, uint32_t mytime) +{ + FILE *fd; + char line[128]; + + /* cleaning 'newallow' */ + { + int a; + for(a=0; auid = -1; + config->gid = -1; + + if ( ( fd = fopen(config->file, "r") ) == NULL ) + { + printf("error: failed to read %s\n",config->file); + return -1; + } + + while(fgets(line, sizeof(line), fd)) + { + char *s = line; + if ( strncmp(s, "#", 1) == 0 ) { continue; } + s = strtok(s, " "); + + if ( !strcmp(s,"bgp_router_id")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 16 ) + { + config->routerid = ntohl(inet_addr(s)); + #ifdef DEBUG + printf("DEBUG: config bgp_router_id %s",s); + #endif + } + } + else if ( !strcmp(s,"user")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 50 ) + { + struct passwd *mypwd; + CHOMP(s); + mypwd = getpwnam(s); + if ( mypwd == NULL ) + { + config->uid = -1; + config->gid = -1; + } + else + { + config->uid = mypwd->pw_uid; + config->gid = mypwd->pw_gid; + } + #ifdef DEBUG + printf("DEBUG: config user %i/%i (uid/gid) %s\n",config->uid,config->gid,s); + #endif + } + } + else if ( !strcmp(s,"local_as")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 6 ) + { + config->as = atol(s); + #ifdef DEBUG + printf("DEBUG: config local_as %s",s); + #endif + } + } + else if ( !strcmp(s,"local_port4")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 6 ) + { + config->ip4.listen.sin_family = AF_INET; + config->ip4.listen.sin_port = htons(atoi(s)); + #ifdef DEBUG + printf("DEBUG: config local_port4 %s",s); + #endif + } + } + else if ( !strcmp(s,"local_port6")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 6 ) + { + config->ip6.listen.sin6_family = AF_INET6; + config->ip6.listen.sin6_port = htons(atoi(s)); + #ifdef DEBUG + printf("DEBUG: config local_port6 %s",s); + #endif + } + } + else if ( !strcmp(s, "local_ip4")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 15 ) + { + CHOMP(s); + if ( inet_pton(AF_INET, s, &config->ip4.listen.sin_addr) == 1 ) + { + config->ip4.enabled=1; + #ifdef DEBUG + printf("DEBUG: config local_ipv4 %s\n", s); + #endif + } + } + } + else if ( !strcmp(s, "local_ip6")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 47 ) + { + CHOMP(s); + if ( inet_pton(AF_INET6, s, &config->ip6.listen.sin6_addr) == 1 ) + { + config->ip6.enabled=1; + #ifdef DEBUG + printf("DEBUG: config local_ipv6 %s\n", s); + #endif + } + } + } + else if ( !strcmp(s,"bgp_holdtime")) + { + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 4 ) + { + config->holdtime = atoi(s); + #ifdef DEBUG + printf("DEBUG: config bgp_holdtime %s",s); + #endif + } + } + else if ( !strcmp(s, "export")) + { + s = strtok(NULL, " "); + CHOMP(s); + if ( s != NULL && strlen(s) > 0 && strlen(s) < 100 ) + { + #ifdef DEBUG + printf("DEBUG: config export %s\n", s); + #endif + + if ( ! strcmp(s, "origin") ) + config->export |= EXPORT_ORIGIN; + else if ( ! strcmp(s, "aspath") ) + config->export |= EXPORT_ASPATH; + else if ( ! strcmp(s, "community") ) + config->export |= EXPORT_COMMUNITY; + else if ( ! strcmp(s, "extcommunity") ) + config->export |= EXPORT_EXTCOMMUNITY; + #ifdef DEBUG + else + printf("DEBUG: Unknown export %s\n", s); + #endif + } + } + else if ( !strcmp(s,"neighbor")) + { + s = strtok(NULL, " "); + if ( s != NULL ) + { + struct in_addr peer_ip4; + struct in6_addr peer_ip6; + uint8_t af = 0; + + if ( inet_pton(AF_INET6, s, &peer_ip6) == 1 ) + { + af=6; + #ifdef DEBUG + printf("DEBUG: config neighbor IP6 %s ", + p_tools_ip6str(MAX_PEERS, &peer_ip6)); + #endif + } + else if ( inet_pton(AF_INET, s, &peer_ip4) == 1 ) + { + af=4; + #ifdef DEBUG + printf("DEBUG: config neighbor IP4 %s ", + p_tools_ip4str(MAX_PEERS, &peer_ip4)); + #endif + } + else + { + #ifdef DEBUG + printf("DEBUG: config neighbor, invalid IP '%s'\n",s); + #endif + } + + s = strtok(NULL, " "); + if ( s != NULL && strlen(s) > 0 && strlen(s) <= 10 ) + { + uint32_t peer_as = strtol(s, NULL, 10); + char peer_key[MAX_KEY_LEN]; + CHOMP(s); + #ifdef DEBUG + printf("as %s",s); + #endif + + s = strtok(NULL," "); + if ( s != NULL && strlen(s) > 0 && strlen(s) < MAX_KEY_LEN ) + { + int len = strlen(s); + if ( len>=2 && s[len-2] == '\r' ) + len-=2; + else if ( len>=1 && s[len-1] == '\n' ) + len-=1; + + s[len] = '\0'; + + if ( len > 0 ) + { + strcpy(peer_key, s); + #ifdef DEBUG + printf(" key %s",s); + #endif + } + } + else + peer_key[0] = '\0'; + + if ( af == 4 || af == 6 ) + p_config_add_peer(peer, af, &peer_ip4, &peer_ip6, peer_as, peer_key, mytime); + + } + #ifdef DEBUG + printf("\n"); + #endif + } + } + } + + fclose(fd); + + /* clearning no more allowed peers */ + { + int a; + for(a=0; arouterid == 0 ) + { + printf("configuration error: no bgp router id set\n"); + return -1; + } + if ( config->as == 0 ) + { + printf("configuration error: no local AS set\n"); + return -1; + } + + if ( config->uid == -1 || config->gid == -1 ) + { + printf("configuration error: could not find user\n"); + return -1; + } + + return 0; +} + +/* add, update of peers */ +void p_config_add_peer(struct peer_t *peer, uint8_t af, struct in_addr *ip4, struct in6_addr *ip6, uint32_t as, char *key, uint32_t mytime) +{ + int a; + if ( as == 0 ) + return; + + if ( af == 4 && p_tools_ip4zero(ip4) == 1 ) + return; + + if ( af == 6 && p_tools_ip6zero(ip6) == 1 ) + return; + + for(a = 0; a +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* opening file */ +void p_dump_open_file(struct peer_t *peer, int id, uint64_t ts) +{ + struct tm *tm; + struct stat sb; + char dirname[1024]; + char filename[1024]; + char mytime[100]; + + peer[id].filets = ts - ( ts % DUMPINTERVAL ); + + tm = gmtime((time_t*)&peer[id].filets); + strftime(mytime, sizeof(mytime), "%Y%m%d%H%M%S" , tm); + + snprintf(dirname, sizeof(dirname), "%s/%s", + DUMPDIR, + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6)); + + snprintf(peer[id].filename, sizeof(peer[id].filename), "%s/%s/%s", + DUMPDIR, + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6), + mytime); + + snprintf(filename, sizeof(filename), "%s/%s/%s", + DUMPDIR, + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6), + "temp.dump"); + + #ifdef DEBUG + printf("opening '%s'\n",peer[id].filename); + #endif + if ( stat(dirname, &sb) == -1 ) + { + mkdir(dirname, 0755); + } + + peer[id].fh = fopen(filename, "wb" ); + peer[id].empty = 1; +} + +/* log keepalive msg */ +void p_dump_add_keepalive(struct peer_t *peer, int id, uint64_t ts) +{ + p_dump_check_file(peer,id,ts); + + if ( peer[id].fh == NULL ) { return; } + peer[id].empty = 0; + { + struct dump_msg msg; + + msg.type = DUMP_KEEPALIVE; + msg.ts = htobe64(ts); + msg.len = htobe16(0); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + } +} + +/* log session close */ +void p_dump_add_close(struct peer_t *peer, int id, uint64_t ts) +{ + if ( peer[id].fh == NULL ) { return; } + peer[id].empty = 0; + { + struct dump_msg msg; + + msg.type = DUMP_CLOSE; + msg.ts = htobe64(ts); + msg.len = htobe16(0); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + } + + p_dump_check_file(peer,id,ts); +} + +/* log session open */ +void p_dump_add_open(struct peer_t *peer, int id, uint64_t ts) +{ + p_dump_check_file(peer,id,ts); + + if ( peer[id].fh == NULL ) { return; } + peer[id].empty = 0; + { + struct dump_msg msg; + + msg.type = DUMP_OPEN; + msg.ts = htobe64(ts); + msg.len = htobe16(0); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + } +} + +/* footer for each EOF */ +void p_dump_add_footer(struct peer_t *peer, int id, uint64_t ts) +{ + if ( peer[id].fh == NULL ) { return; } + { + struct dump_msg msg; + + msg.type = DUMP_FOOTER; + msg.ts = htobe64(ts); + msg.len = htobe64(0); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + } +} + +/* log bgp IPv4 withdrawn msg */ +void p_dump_add_withdrawn4(struct peer_t *peer, int id, uint64_t ts, uint32_t prefix, uint8_t mask) +{ + p_dump_check_file(peer,id,ts); + + if ( peer[id].fh == NULL ) { return; } + peer[id].empty = 0; + { + struct dump_msg msg; + struct dump_withdrawn4 withdrawn; + + msg.type = DUMP_WITHDRAWN4; + msg.ts = htobe64(ts); + + withdrawn.mask = mask; + withdrawn.prefix = htobe32(prefix); + + msg.len = htobe16(sizeof(withdrawn)); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + fwrite(&withdrawn, sizeof(withdrawn), 1, peer[id].fh); + + } +} + +/* log bgp IPv6 withdrawn msg */ +void p_dump_add_withdrawn6(struct peer_t *peer, int id, uint64_t ts, uint8_t prefix[16], uint8_t mask) +{ + p_dump_check_file(peer,id,ts); + + if ( peer[id].fh == NULL ) { return; } + peer[id].empty = 0; + { + struct dump_msg msg; + struct dump_withdrawn6 withdrawn; + + msg.type = DUMP_WITHDRAWN6; + msg.ts = htobe64(ts); + + withdrawn.mask = mask; + + memcpy(withdrawn.prefix, prefix, sizeof(withdrawn.prefix)); + + msg.len = htobe16(sizeof(withdrawn)); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + fwrite(&withdrawn, sizeof(withdrawn), 1, peer[id].fh); + + } +} + +/* log IPv4 bgp announce msg */ +void p_dump_add_announce4(struct peer_t *peer, int id, uint64_t ts, + uint32_t prefix, uint8_t mask, uint8_t origin, + void *aspath, uint8_t aspathlen, + void *community, uint8_t communitylen, + void *extcommunity, uint8_t extcommunitylen ) +{ + p_dump_check_file(peer,id,ts); + + if ( peer[id].fh == NULL ) { return; } + peer[id].empty = 0; + { + struct dump_msg msg; + struct dump_announce4 announce; + struct dump_announce_aspath opt_aspath; + struct dump_announce_community opt_community; + struct dump_announce_extcommunity opt_extcommunity; + + msg.type = DUMP_ANNOUNCE4; + msg.ts = htobe64(ts); + msg.len = htobe16(sizeof(announce) + + sizeof(opt_aspath.data[0]) * aspathlen + + sizeof(opt_community.data[0]) * communitylen + + sizeof(opt_extcommunity.data[0]) * extcommunitylen ); + + announce.mask = mask; + announce.prefix = htobe32(prefix); + announce.origin = origin; + announce.aspathlen = aspathlen; + announce.communitylen = communitylen; + announce.extcommunitylen = extcommunitylen; + + #ifdef DEBUG + { + struct in_addr addr; + addr.s_addr = htonl(announce.prefix); + printf("DUMP ANNOUNCE %s/%u\n",p_tools_ip4str(id, &addr),announce.mask); + } + #endif + + if ( aspathlen > 0 ) + { + int i; + for(i=0; i 0 ) + { + int i; + for(i=0; i 0 ) + { + int i; + for(i=0; i 0 ) + fwrite(&opt_aspath, sizeof(opt_aspath.data[0]), aspathlen, peer[id].fh); + + if ( communitylen > 0 ) + fwrite(&opt_community, sizeof(opt_community.data[0]), communitylen, peer[id].fh); + + if ( extcommunitylen > 0 ) + fwrite(&opt_extcommunity, sizeof(opt_extcommunity.data[0]), extcommunitylen, peer[id].fh); + } +} +/* log IPv6 bgp announce msg */ +void p_dump_add_announce6(struct peer_t *peer, int id, uint64_t ts, + uint8_t prefix[16], uint8_t mask, uint8_t origin, + void *aspath, uint8_t aspathlen, + void *community, uint8_t communitylen, + void *extcommunity, uint8_t extcommunitylen ) +{ + p_dump_check_file(peer,id,ts); + + if ( peer[id].fh == NULL ) { return; } + peer[id].empty = 0; + { + struct dump_msg msg; + struct dump_announce6 announce; + struct dump_announce_aspath opt_aspath; + struct dump_announce_community opt_community; + struct dump_announce_extcommunity opt_extcommunity; + + msg.type = DUMP_ANNOUNCE6; + msg.ts = htobe64(ts); + msg.len = htobe16( sizeof(announce) + + sizeof(opt_aspath.data[0]) * aspathlen + + sizeof(opt_community.data[0]) * communitylen + + sizeof(opt_extcommunity.data[0]) * extcommunitylen ); + + memcpy(announce.prefix, prefix, sizeof(announce.prefix)); + announce.mask = mask; + announce.origin = origin; + announce.aspathlen = aspathlen; + announce.communitylen = communitylen; + announce.extcommunitylen = extcommunitylen; + + #ifdef DEBUG + { + struct in6_addr addr; + memcpy(addr.s6_addr, announce.prefix, sizeof(announce.prefix)); + printf("DUMP ANNOUNCE %s/%u\n",p_tools_ip6str(id, &addr),announce.mask); + } + #endif + + if ( aspathlen > 0 ) + { + int i; + for(i=0; i 0 ) + { + int i; + for(i=0; i 0 ) + { + int i; + for(i=0; i 0 ) + fwrite(&opt_aspath, sizeof(opt_aspath.data[0]), aspathlen, peer[id].fh); + + if ( communitylen > 0 ) + fwrite(&opt_community, sizeof(opt_community.data[0]), communitylen, peer[id].fh); + + if ( extcommunitylen > 0 ) + fwrite(&opt_extcommunity, sizeof(opt_extcommunity.data[0]), extcommunitylen, peer[id].fh); + + } +} + +/* check if need to reopen a new file */ +void p_dump_check_file(struct peer_t *peer, int id, uint64_t ts) +{ + uint64_t mts = ts - ( ts % DUMPINTERVAL ); + + if ( mts == peer[id].filets && peer[id].fh != NULL ) { return; } + + if ( mts != peer[id].filets ) + { + if ( peer[id].fh != NULL ) + { + p_dump_add_footer(peer,id,ts); + p_dump_close_file(peer,id); + } + + if ( peer[id].status != 0 ) + { + p_dump_open_file(peer,id,ts); + + if ( peer[id].af == 4 ) + p_dump_add_header4(peer,id,ts); + else + p_dump_add_header6(peer,id,ts); + } + } + else if ( peer[id].fh == NULL && peer[id].status != 0) + { + p_dump_open_file(peer,id,ts); + if ( peer[id].af == 4 ) + p_dump_add_header4(peer,id,ts); + else + p_dump_add_header6(peer,id,ts); + } +} + +/* file header */ +void p_dump_add_header4(struct peer_t *peer, int id, uint64_t ts) +{ + if ( peer[id].fh == NULL ) { return; } + { + struct dump_msg msg; + struct dump_header4 header; + + msg.type = DUMP_HEADER4; + msg.ts = htobe64(ts); + msg.len = htobe16(sizeof(header)); + + header.ip = peer[id].ip4.s_addr; + header.as = htobe32(peer[id].as); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + fwrite(&header, sizeof(header), 1, peer[id].fh); + } +} + +/* file header */ +void p_dump_add_header6(struct peer_t *peer, int id, uint64_t ts) +{ + if ( peer[id].fh == NULL ) { return; } + { + struct dump_msg msg; + struct dump_header6 header; + + msg.type = DUMP_HEADER6; + msg.ts = htobe64(ts); + msg.len = htobe16(sizeof(header)); + + memcpy(header.ip, peer[id].ip6.s6_addr, sizeof(header.ip)); + header.as = htobe32(peer[id].as); + + fwrite(&msg, sizeof(msg), 1, peer[id].fh); + fwrite(&header, sizeof(header), 1, peer[id].fh); + } +} + +/* close file */ +void p_dump_close_file(struct peer_t *peer, int id) +{ + char filename[1024]; + + if ( peer[id].fh == NULL ) { return; } + + fclose(peer[id].fh); + + snprintf(filename, sizeof(filename), "%s/%s/%s", + DUMPDIR, + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6), + "temp.dump"); + + rename(filename, peer[id].filename); + + if ( peer[id].empty == 1 ) + { + unlink(peer[id].filename); + } +} diff --git a/src/p_log.c b/src/p_log.c new file mode 100644 index 0000000..bd2e2c7 --- /dev/null +++ b/src/p_log.c @@ -0,0 +1,138 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +/* update pid file */ +void p_log_pid() +{ + FILE *fh; + char pidstr[10]; + pid_t pid = getpid(); + + if ( ( fh = fopen(PIDFILE,"w") ) == NULL ) { return; } + + snprintf(pidstr, sizeof(pidstr), "%i\n",pid); + + fwrite(pidstr, strlen(pidstr), 1, fh); + + fclose(fh); +} + +/* add text to logfile */ +void p_log_add(time_t mytime, char *line) +{ + struct tm *tm; + char timestr[40]; + FILE *fh; + + tm = gmtime((time_t*)&mytime); + + strftime(timestr, sizeof(timestr), "[%Y-%m-%d %H:%M:%S] " , tm); + + if ( ( fh = fopen(LOGFILE,"a") ) == NULL ) { return; } + + fwrite(timestr, strlen(timestr), 1, fh); + fwrite(line, strlen(line), 1, fh); + + fclose(fh); +} + +/* convert time_t in weeks,days,hours,mins,sec format */ +void p_log_easytime(time_t mytime, char *timestr, int timestrlen) +{ + uint16_t s = 0; + uint16_t m = 0; + uint16_t h = 0; + uint16_t d = 0; + uint16_t w = 0; + + s = mytime % 60; + mytime = ( mytime - s ) / 60; + + m = mytime % 60; + mytime = ( mytime - m ) / 60; + + h = mytime % 24; + mytime = ( mytime - h ) / 24; + + d = mytime % 7; + mytime = ( mytime - d ) / 7; + + w = mytime; + + if ( w > 0 ) { snprintf(timestr, timestrlen, "%uw%ud", w, d); } + else if ( d > 0 ) { snprintf(timestr, timestrlen, "%ud%uh", d, h); } + else if ( h > 0 ) { snprintf(timestr, timestrlen, "%uh%um", h, m); } + else { snprintf(timestr, timestrlen, "%um%us", m, s); } +} + +/* update status file */ +void p_log_status(struct config_t *config, struct peer_t *peer, time_t mytime) +{ + char data[(MAX_PEERS*64)+256]; + int doff = 0; + int a; + FILE *fh; + static char *bgp_status[] = { "down", "temp", "up", }; + + snprintf(data, sizeof(data), "/----------------------------------------------------------------------------------------------------\\\n"); + doff = strlen(data); + snprintf(data+doff, sizeof(data)-doff, "| neighbor asn recv sent updates status up/down |\n"); + doff = strlen(data); + snprintf(data+doff, sizeof(data)-doff, "|----------------------------------------------------------------------------------------------------|\n"); + doff = strlen(data); + + for(a=0; a +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include +#include +#include + + +/* init the global structures */ +struct config_t config; +struct peer_t peer[MAX_PEERS]; +struct timeval ts; + + +/* 00 BEGIN ;) */ +int main(int argc, char *argv[]) +{ + #ifdef DEBUG + /* say hello */ + printf("Piranha v%s.%s.%s BGP Daemon, Copyright(c) 2004-2017 Pascal Gloor\n",P_VER_MA,P_VER_MI,P_VER_PL); + #endif + + #ifdef DEBUG + setlinebuf(stdout); + #endif + + + /* set initial time */ + gettimeofday(&ts,NULL); + + { + char logline[100]; + snprintf(logline, sizeof(logline), "Piranha v%s.%s.%s started.\n",P_VER_MA,P_VER_MI,P_VER_PL); + p_log_add((time_t)ts.tv_sec, logline); + } + + /* check the cmd line options */ + if ( argc != 2 ) { p_main_syntax(argv[0]); return -1; } + + /* init some stuff and load the config */ + config.file = argv[1]; + + if ( p_config_load((struct config_t*)&config,(struct peer_t*)peer, (time_t)ts.tv_sec) == -1 ) + { fprintf(stderr,"error while parsing configuration file %s\n", config.file); return -1; } + + /* chown working dir */ + mychown(PATH, config.uid, config.gid, 0); + + /* set config reload for signal HUP */ + signal(SIGHUP, p_main_sighup); + + /* init the socket */ + if ( p_socket_start((struct config_t*)&config, (struct peer_t*)&peer) == -1 ) + { + fprintf(stderr,"socket error, aborting\n"); + return -1; + } + + + #ifndef DEBUG + /* we dont use daemon() here, it doesnt exist on solaris/suncc ;-) */ + /* daemon(1,0); */ + if ( mydaemon(1,0) ) { fprintf(stderr,"daemonization error.\n"); } + #endif + + /* log the pid */ + p_log_pid(); + + while ( p_main_loop() == 0 ) + { + #ifdef DEBUG + printf("accept() loop\n"); + #endif + + /* we want to sessions to come up slowly */ + /* therefor we sleep a bit here. */ + usleep(100000); + + p_log_status((struct config_t*)&config,(struct peer_t*)peer, (time_t)ts.tv_sec); + + /* we update a global var with the actual timestamp */ + gettimeofday(&ts,NULL); + } + + #ifdef DEBUG + printf("accept() failed, aborting\n"); + #endif + + return -1; +} + +/* check for accept() and start thread() */ +int p_main_loop() +{ + int sock; + + if ( ( sock = p_socket_accept((struct config_t*)&config) ) == -1 ) + { + return 0; + } + else + { + pthread_t thread; + int *mysock; + mysock = malloc(sizeof(int)); + memcpy(mysock,&sock,sizeof(int)); + + pthread_create(&thread, NULL, p_main_peer, (void *)mysock); + } + return 0; +} + +/* peer thread */ +void *p_main_peer(void *data) +{ + int sock; + int a; + int allow = 0; + int peerid = -1; + struct sockaddr_storage sockaddr; + struct sockaddr_in *addr4 = (struct sockaddr_in *)&sockaddr; + struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&sockaddr; + socklen_t socklen = sizeof(sockaddr); + + memcpy(&sock,data,sizeof(sock)); + + #ifdef DEBUG + printf("I'm thready, my socket is %i\n",sock); + #endif + + if ( getpeername(sock,(struct sockaddr*)&sockaddr, &socklen) == -1 ) + { + #ifdef DEBUG + printf("failed to get peeraddr!\n"); + #endif + p_main_peer_exit(data,sock); + } + + #ifdef DEBUG + if ( sockaddr.ss_family == AF_INET ) + { + char str[INET_ADDRSTRLEN]; + printf("peer ip is: %s\n", inet_ntop(AF_INET, &addr4->sin_addr, str, sizeof(str))); + } + else if ( sockaddr.ss_family == AF_INET6 ) + { + char str[INET6_ADDRSTRLEN]; + printf("peer ip is: %s\n", inet_ntop(AF_INET6, &addr6->sin6_addr, str, sizeof(str))); + } + else + { + printf("peer has an unsupported address family! (%i)\n", sockaddr.ss_family); + } + #endif + + /* does the peer exist? */ + for(a=0; asin_addr) && peer[a].allow == 1 ) || + ( sockaddr.ss_family == AF_INET6 && peer[a].af == 6 && p_tools_sameip6(&peer[a].ip6, &addr6->sin6_addr) && peer[a].allow == 1 ) + ) { + char logline[100]; + if ( peer[a].status == 0 ) + { + snprintf(logline,sizeof(logline), "%s connection (known)\n", + sockaddr.ss_family == AF_INET ? p_tools_ip4str(a, &peer[a].ip4) : p_tools_ip6str(a, &peer[a].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + #ifdef DEBUG + printf("peer ip allowed id %i\n",a); + #endif + allow = 1; + peer[a].sock = sock; + peer[a].status = 1; + peer[a].rhold = BGP_DEFAULT_HOLD; + peer[a].shold = BGP_DEFAULT_HOLD; + peer[a].ilen = 0; + peer[a].olen = 0; + peer[a].rts = ts.tv_sec; + peer[a].sts = ts.tv_sec; + peer[a].cts = ts.tv_sec; + peer[a].rmsg = 0; + peer[a].smsg = 0; + peer[a].filets = 0; + peer[a].fh = NULL; + peer[a].ucount = 0; + peer[a].as4 = 0; + peerid = a; + + } + else + { + snprintf(logline, sizeof(logline), "%s connection (already connected)\n", + sockaddr.ss_family == AF_INET ? p_tools_ip4str(a, &peer[a].ip4) : p_tools_ip6str(a, &peer[a].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + } + + break; + } + } + + if ( allow == 0 ) + { + char logline[100 + INET6_ADDRSTRLEN]; + if ( sockaddr.ss_family == AF_INET ) + inet_ntop(AF_INET, &addr4->sin_addr, logline, INET6_ADDRSTRLEN ); + else + inet_ntop(AF_INET6, &addr6->sin6_addr, logline, INET6_ADDRSTRLEN ); + + snprintf(logline + strlen(logline), sizeof(logline) - strlen(logline), " connection (unknown)\n"); + p_log_add((time_t)ts.tv_sec, logline); + + p_main_peer_exit(data,sock); + } + + #ifdef DEBUG + printf("peerid %i\n",peerid); + #endif + + p_main_peer_loop(peerid); + p_dump_add_close(peer, peerid, ts.tv_sec); + + /* peer disconnected, lets wait a bit to avoid direct reconnection */ + /* we'll wait DUMPINTERVAL time! */ + + peer[peerid].status = 1; + sleep(DUMPINTERVAL); + peer[peerid].status = 0; + + p_main_peer_exit(data, sock); + + return NULL; +} + +/* peer looop */ +void p_main_peer_loop(int id) +{ + char logline[100]; + char *ibuf; + char *obuf; + uint8_t marker[16]; + + { int a; for(a=0; a0)) + { + /* receiving new datas, sleep 1sec if nothing */ + int tlen = 0; + int maxlen = INPUT_BUFFER - peer[id].ilen; + + if ( TEMP_BUFFER < maxlen ) { maxlen = TEMP_BUFFER; } + + tlen = recv(peer[id].sock, ibuf+peer[id].ilen, INPUT_BUFFER-peer[id].ilen, 0); + + if ( tlen == -1 ) + { + p_dump_check_file(peer,id,ts.tv_sec); + sleep(1); + } + else if ( tlen > 0 ) + { + #ifdef DEBUG + printf("got data\n"); + #endif + peer[id].ilen += tlen; + } + else + { + snprintf(logline, sizeof(logline), "%s socket went down\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + #ifdef DEBUG + printf("something failed on recv()\n"); + #endif + peer[id].status = 0; + } + + + /* working on datas */ + if ( peer[id].ilen > 0 ) + { + p_main_peer_work(ibuf, obuf, id); + } + + /* check if the peer timed out (note: 0 == no keepalive!) */ + + if ( peer[id].rhold != 0 && ( (ts.tv_sec - peer[id].rts) > peer[id].rhold ) ) + { + /* timeout ;( */ + snprintf(logline, sizeof(logline), "%s holdtime expired\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + peer[id].status = 0; + } + + /* time to send a keepalive message ? (note: 0 == no keepalive!) */ + + if ( peer[id].shold != 0 && ( (ts.tv_sec - peer[id].sts) > (peer[id].shold / 3) ) ) + { + /* yeah */ + struct bgp_header r_header; + memcpy(&r_header, marker, sizeof(marker)); + r_header.len = htons(BGP_HEADER_LEN); + r_header.type = 4; + + memcpy(obuf+peer[id].olen, &r_header, BGP_HEADER_LEN); + peer[id].olen += BGP_HEADER_LEN; + + peer[id].sts = ts.tv_sec; + peer[id].smsg++; + + p_main_peer_send(id, obuf); + } + + + /* sending */ + while((peer[id].olen>0 && peer[id].status)) + { + peer[id].smsg++; + p_main_peer_send(id, obuf); + if ( peer[id].olen == -1 ) + { + #ifdef DEBUG + printf("failed to send!\n"); + #endif + snprintf(logline, sizeof(logline), "%s failed to send\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + peer[id].status = 0; + } + } + + if ( peer[id].olen == -1 ) + { + printf("%s error while sending\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + } + + + } + + free(ibuf); + free(obuf); + + snprintf(logline, sizeof(logline), "%s down\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + + peer[id].cts = ts.tv_sec; + + #ifdef DEBUG + printf("peer is gone\n"); + #endif +} + +/* sending BGP open and first keepalive */ +void p_main_peer_open(int id, char *obuf) +{ + /* reply with my open msg */ + struct bgp_header r_header; + struct bgp_open r_open; + struct bgp_param r_param; + struct bgp_param_capa *r_capa_as4; + struct bgp_param_capa *r_capa_afi; + uint32_t ugly_afi4 = htonl(0x00010001); + uint32_t ugly_afi6 = htonl(0x00020001); + + + uint8_t marker[16]; + + { int a; for(a=0; atype = 65; + r_capa_as4->len = 4; + r_capa_as4->u.as4 = htonl(config.as); + + if ( peer[id].af == 4 ) + { + /* AFI support IPv4 unicast */ + r_capa_afi = (struct bgp_param_capa*)(r_param.param+r_capa_as4->len+2); + r_capa_afi->type = 1; + r_capa_afi->len = 4; + memcpy(r_capa_afi->u.def,&ugly_afi4,4); /* TO BE IMPROVED, UGLY !!!!! */ + } + else + { + /* AFI support IPv6 unicast */ + r_capa_afi = (struct bgp_param_capa*)(r_param.param+r_capa_as4->len+2); + r_capa_afi->type = 1; + r_capa_afi->len = 4; + memcpy(r_capa_afi->u.def,&ugly_afi6,4); /* TO BE IMPROVED, UGLY !!!!! */ + } + + /* capabilities parameter */ + r_param.type = 2; + r_param.len = r_capa_as4->len + 2 + r_capa_afi->len + 2; + + /* open message */ + r_open.version = 4; + r_open.as = ( config.as > 65535 ) ? 23456 : htons((uint16_t)config.as); + r_open.holdtime = htons(peer[id].shold); + r_open.bgp_id = htonl(config.routerid); + r_open.param_len = r_param.len + 2; + + r_header.len = htons(BGP_HEADER_LEN+BGP_OPEN_LEN+r_param.len+2); + r_header.type = 1; + + memcpy(obuf+peer[id].olen,&r_header, BGP_HEADER_LEN); + peer[id].olen += BGP_HEADER_LEN; + + memcpy(obuf+peer[id].olen,&r_open, BGP_OPEN_LEN); + peer[id].olen += BGP_OPEN_LEN; + + memcpy(obuf+peer[id].olen,&r_param, r_param.len+2); + peer[id].olen += r_param.len+2; + + + peer[id].smsg++; + p_main_peer_send(id,obuf); + + /* we add directly the first keepalive msg */ + + r_header.len = htons(BGP_HEADER_LEN); + r_header.type = 4; + + memcpy(obuf+peer[id].olen, &r_header, BGP_HEADER_LEN); + peer[id].olen += BGP_HEADER_LEN; + + /* update the keepalive sent timestamp */ + peer[id].sts = ts.tv_sec; + + /* send the packet */ + + peer[id].smsg++; + p_main_peer_send(id,obuf); +} + +/* bgp decoding stuff */ +void p_main_peer_work(char *ibuf, char *obuf, int id) +{ + char logline[100]; + uint8_t marker[16] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; + + while(1) + { + int pos = 0; + struct bgp_header *header; + + if ( peer[id].ilen < sizeof(struct bgp_header) ) + { + #ifdef DEBUG + printf("not all header\n"); + #endif + return; + } + + header = (struct bgp_header*)ibuf; + + if ( memcmp(header->marker, marker, sizeof(marker)) != 0 ) + { + snprintf(logline, sizeof(logline), "%s packet decoding error\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + #ifdef DEBUG + printf("invalid marker\n"); + #endif + peer[id].status = 0; + return; + } + + #ifdef DEBUG + printf("len: %u type: %u (buffer %u)\n",htons(header->len),header->type,peer[id].ilen); + #endif + + if ( peer[id].ilen < htons(header->len) ) + { + #ifdef DEBUG + printf("bgp message not complete msg len %u, buffer len %u\n",htons(header->len),peer[id].ilen); + #endif + return; + } + + /* show bgp message */ + #ifdef DEBUG + p_tools_dump("BGP Message", ibuf, htons(header->len)); + #endif + + + pos += BGP_HEADER_LEN; + peer[id].rmsg++; + + peer[id].rts = ts.tv_sec; + + /* BGP OPEN MSG */ + if ( header->type == BGP_OPEN && peer[id].status == 1 ) + { + struct bgp_open *bopen; + bopen = (struct bgp_open*) (ibuf + pos); + int alt_asn = 0; + + if ( bopen->version != 4 ) + { + snprintf(logline, sizeof(logline), "%s wrong bgp version (%u)\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6), + bopen->version); + p_log_add((time_t)ts.tv_sec, logline); + #ifdef DEBUB + printf("invalid BGP version %u\n",bopen->version); + #endif + peer[id].status = 0; + return; + } + #ifdef DEBUG + printf("BGP Version: %u\n",bopen->version); + #endif + + if ( htons(bopen->as) == 23456 ) /* AS_TRANS RFC6793 */ + { + alt_asn=1; + } + else if ( htons(bopen->as) != peer[id].as ) + { + snprintf(logline, sizeof(logline), "%s wrong neighbor as (%u != %u)\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6), + htons(bopen->as),peer[id].as); + p_log_add((time_t)ts.tv_sec, logline); + #ifdef DEBUG + printf("invalid BGP neighbor AS %u\n",htons(bopen->as)); + #endif + peer[id].status = 0; + return; + } + + peer[id].rhold = htons(bopen->holdtime); + + if ( peer[id].rhold < peer[id].shold ) + { + peer[id].shold = peer[id].rhold; + } + + if ( htons(header->len) != BGP_HEADER_LEN + BGP_OPEN_LEN + bopen->param_len ) + { + snprintf(logline, sizeof(logline), "%s parameter parsing error)\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + #ifdef DEBUG + printf("size error in bgp open params, len %u, header %u open %u param %u\n",htons(header->len),BGP_HEADER_LEN,BGP_OPEN_LEN,bopen->param_len); + #endif + peer[id].status = 0; + return; + } + + pos += BGP_OPEN_LEN; + + while(poslen)) + { + struct bgp_param *param; + param = (struct bgp_param*) (ibuf+pos); + #ifdef DEBUG + printf("param code %u len %u\n",param->type,param->len); + #endif + if ( param->type == 2 ) // BGP Capabilities + { + int i = 0; + struct bgp_param_capa *capa; + + while(ilen) + { + capa = (struct bgp_param_capa*) (param->param + i); + + #ifdef DEBUG + int b; + printf("capability %u (%s) len %u : ", capa->type, bgp_capability[capa->type], capa->len); + for(b=0; blen; b++) + printf("%02x ", (uint8_t)capa->u.def[b]); + printf("\n"); + #endif + + if ( capa->type == 65 && capa->len == 4 ) /* Support for 4-octets ASN */ + { + if ( peer[id].as == ntohl(capa->u.as4) ) + { + alt_asn = 0; + peer[id].as4=1; + } + else + { + peer[id].status=0; + snprintf(logline, sizeof(logline), "%s ASN mismatch in 4-octet capability\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + return; + } + } + i += 1 + 1 + capa->len; + } + } + #ifdef DEBUG + else + { + int b; + for(b=0; blen; b++) + printf("%u ",(uint8_t)param->param[b]); + printf("\n"); + } + #endif + pos += 2; + pos += param->len; + if ( param->type > 4 ) + { + #ifdef DEBUG + printf("parameter rejected!\n"); + #endif + peer[id].status = 0; + return; + } + } + + if ( alt_asn == 1 ) /* AS_TRANS was used but we didn't find capa 65 4 octets ASN */ + { + peer[id].status=0; + snprintf(logline, sizeof(logline), "%s AS_TRANS in header but no capa 65 found\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + return; + } + + snprintf(logline, sizeof(logline), "%s established\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + + peer[id].status = 2; + + p_dump_add_open(peer, id, ts.tv_sec); + + } + else if ( header->type == BGP_UPDATE && peer[id].status == 2 ) + { + /* BGP update */ + uint16_t wlen; + uint16_t alen; + uint8_t origin = 0xff; + void *aspath = NULL; + uint16_t aspathlen = 0; + void *community = NULL; + uint16_t communitylen = 0; + void *extcommunity = NULL; + uint8_t extcommunitylen = 0; + + wlen = *(uint16_t *) (ibuf + pos); + wlen = ntohs(wlen); + pos += 2; + + #ifdef DEBUG + printf("Withdrawn Length %u\n",wlen); + #endif + + while(wlen>0) + { + uint8_t plen = *(uint8_t *) (ibuf+pos); + uint32_t prefix = 0; + int blen = 0; + + pos++; + wlen--; + + if ( plen > 24 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos+3); + prefix += o << 0; + blen++; + } + if ( plen > 16 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos+2); + prefix += o << 8; + blen++; + } + if ( plen > 8 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos+1); + prefix += o << 16; + blen++; + } + if ( plen > 0 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos); + prefix += o << 24; + blen++; + } + pos+=blen; + wlen-= blen; + + #ifdef DEBUG + { + struct in_addr addr; + addr.s_addr = htonl(prefix); + printf("withdrawn %s/%u (post-clean)\n",inet_ntoa(addr),plen); + } + #endif + + /* cleanup prefix: zero-ing unused bits */ + if ( plen == 0 ) + prefix = 0; + else + prefix &= ( 0xffffffff ^ ( ( 1 << ( 32 - plen ) ) - 1 ) ); + + #ifdef DEBUG + { + struct in_addr addr; + addr.s_addr = htonl(prefix); + printf("withdrawn %s/%u (post-clean)\n",inet_ntoa(addr),plen); + } + #endif + + p_dump_add_withdrawn4(peer,id,ts.tv_sec,prefix,plen); + peer[id].ucount++; + } + + alen = *(uint16_t *) (ibuf + pos); + alen = ntohs(alen); + + #ifdef DEBUG + printf("announce size: %u\n",alen); + #endif + + pos+=2; + + { + uint16_t attrpos = 0; + struct { + uint8_t flags; + uint16_t pos; + uint16_t len; + } a[256]; + + /* set all positions to 0xFFFF by default */ + { + int i; + for (i=0; i<256; i++) + a[i].pos = 0xffff; + } + + while(alen-attrpos>0) + { + uint8_t flags = *(uint8_t*) (ibuf+pos+attrpos); + uint8_t code = *(uint8_t*) (ibuf+pos+attrpos+1); + uint16_t codelen; + + a[code].flags = flags; + + attrpos+=2; + + if ( flags & 0x10 ) + { + uint16_t clen = *(uint16_t*)(ibuf+pos+attrpos); + codelen = ntohs(clen); + attrpos+=2; + } + else + { + codelen = *(uint8_t*)(ibuf+pos+attrpos); + attrpos++; + } + + a[code].pos = attrpos; + a[code].len = codelen; + attrpos+=codelen; + + #ifdef DEBUG + + printf("code: at offset %u of length %u is %u (%s) (", + a[code].pos, a[code].len, code, bgp_path_attribute[code]); + + if ( flags & 0x80 ) { printf(" optional"); } else { printf(" well-known"); } + if ( flags & 0x40 ) { printf(" transitive"); } else { printf(" non-transitive"); } + if ( flags & 0x20 ) { printf(" partial"); } else { printf(" complete"); } + if ( flags & 0x10 ) { printf(" 2octet"); } else { printf(" 1octet"); } + printf(")\n"); + + p_tools_dump(bgp_path_attribute[code], ibuf+pos+a[code].pos, a[code].len); + + #endif + + } + + if ( a[BGP_ATTR_ORIGIN].pos != 0xffff && config.export & EXPORT_ORIGIN ) + { + uint16_t off = a[BGP_ATTR_ORIGIN].pos; + uint16_t codelen = a[BGP_ATTR_ORIGIN].len; + + if ( codelen == 1 ) + { + origin = *(uint8_t*) (ibuf+pos+off); + #ifdef DEBUG + printf("ORIGIN: %u (%s)\n", origin, bgp_origin[origin]); + #endif + } + } + if ( a[BGP_ATTR_AS_PATH].pos != 0xffff && config.export & EXPORT_ASPATH ) + { + uint16_t off = a[BGP_ATTR_AS_PATH].pos; + uint16_t codelen = a[BGP_ATTR_AS_PATH].len; + + uint8_t aspath_type = *(uint8_t*) (ibuf+pos+off); + uint8_t aspath_len = *(uint8_t*) (ibuf+pos+off+1); + + if ( codelen == 0 ) + { + #ifdef DEBUG + printf("Empty ASPATH (iBGP)\n"); + aspathlen = 0; + #endif + } + else if ( aspath_type == 1 ) + { + #ifdef DEBUG + printf("AS_SET %u\n",aspath_len); + #endif + } + else if ( aspath_type == 2 ) + { + #ifdef DEBUG + printf("AS_PATH %u\n",aspath_len); + if ( peer[id].as4 == 1 ) + { + int b; + for(b=0; blen) - pos)) + { + uint8_t plen = *(uint8_t *) (ibuf+pos); + uint32_t prefix = 0; + int blen = 0; + pos++; + + if ( plen > 24 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos+3); + prefix += o << 0; + blen++; + } + if ( plen > 16 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos+2); + prefix += o << 8; + blen++; + } + if ( plen > 8 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos+1); + prefix += o << 16; + blen++; + } + if ( plen > 0 ) + { + uint32_t o = *(uint8_t *) (ibuf+pos); + prefix += o << 24; + blen++; + } + pos+=blen; + + #ifdef DEBUG + { + struct in_addr addr; + addr.s_addr = htonl(prefix); + printf("announce %s/%u (pre-clean)\n",inet_ntoa(addr),plen); + } + #endif + + /* cleanup prefix: zero-ing unused bits */ + if ( plen == 0 ) + prefix = 0; + else + prefix &= ( 0xffffffff ^ ( ( 1 << ( 32 - plen ) ) - 1 ) ); + + #ifdef DEBUG + { + struct in_addr addr; + addr.s_addr = htonl(prefix); + printf("announce %s/%u (post-clean)\n",inet_ntoa(addr),plen); + } + #endif + + p_dump_add_announce4(peer,id,ts.tv_sec,prefix,plen,origin, + aspath, aspathlen, + community, communitylen, + extcommunity, extcommunitylen ); + + peer[id].ucount++; + } + + } + else if ( header->type == BGP_ERROR ) + { + /* bgp error msg */ + struct bgp_error *error; + + error = (struct bgp_error*) (ibuf + pos); + + #ifdef DEBUG + printf("error code : %i/%i\n",error->code,error->subcode); + #endif + + pos += BGP_ERROR_LEN; + + snprintf(logline, sizeof(logline), "%s notification received code %u/%u\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6), + error->code,error->subcode); + p_log_add((time_t)ts.tv_sec, logline); + peer[id].status = 0; + } + else if ( header->type == BGP_KEEPALIVE && peer[id].status == 2 ) + { + /* keepalive packet */ + peer[id].rts = ts.tv_sec; + p_dump_add_keepalive(peer, id, ts.tv_sec); + #ifdef DEBUG + printf("received keepalive\n"); + #endif + } + else + { + #ifdef DEBUG + printf("invalid header type %u\n",header->type); + #endif + + snprintf(logline, sizeof(logline), "%s invalid message type\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + peer[id].status = 0; + return; + } + + if ( htons(header->len) != pos ) + { + #ifdef DEBUG + printf("something went wrong with the packet size\n"); + #endif + + snprintf(logline, sizeof(logline), "%s error in packet size\n", + peer[id].af == 4 ? p_tools_ip4str(id, &peer[id].ip4) : p_tools_ip6str(id, &peer[id].ip6) ); + p_log_add((time_t)ts.tv_sec, logline); + peer[id].status = 0; + return; + } + + if ( peer[id].ilen <= htons(header->len) ) + { + peer[id].ilen = 0; + return; + } + + #ifdef DEBUG + printf("still got datas!\n"); + #endif + + memmove(ibuf, ibuf+htons(header->len), peer[id].ilen-htons(header->len)); + peer[id].ilen -= pos; + } +} + +/* send() */ +void p_main_peer_send(int id, char *obuf) +{ + /* sending datas */ + if ( peer[id].olen > 0 ) + { + int slen = 0; + + #ifdef DEBUG + printf("something to send\n"); + #endif + + slen = send(peer[id].sock, obuf, peer[id].olen, 0); + + if ( slen == peer[id].olen ) + { + #ifdef DEBUG + printf("send ok\n"); + #endif + peer[id].olen = 0; + } + else if ( slen == 0 ) + { + #ifdef DEBUG + printf("couldnt send anything\n"); + #endif + peer[id].olen = -1; + } + else if ( slen == -1 ) + { + #ifdef DEBUG + printf("failed to send()\n"); + #endif + peer[id].olen = -1; + } + else if ( slen < peer[id].olen ) + { + #ifdef DEBUG + printf("cound not send all\n"); + #endif + memmove(obuf, obuf+slen, peer[id].olen-slen); + peer[id].olen -= slen; + } + else + { + #ifdef DEBUG + printf("impossible send() case!\n"); + #endif + peer[id].olen = -1; + } + } + return; +} + +/* peer exit, thread exit */ +void p_main_peer_exit(void *data, int sock) +{ + #ifdef DEBUG + printf("dead thready with socket %i\n",sock); + #endif + + close(sock); + + free(data); + + pthread_exit(NULL); +} + +/* syntax */ +void p_main_syntax(char *prog) +{ + printf("syntax: %s \n",prog); +} + +/* kill -HUP for config reload */ +void p_main_sighup(int sig) +{ + if ( p_config_load((struct config_t*)&config,(struct peer_t*)peer, (time_t)ts.tv_sec) == -1 ) + { + #ifdef DEBUG + printf("failed to reload config!\n"); + #else + p_log_add((time_t)ts.tv_sec, "failed to reload configuration\n"); + #endif + exit(1); + } + + if ( p_socket_start((struct config_t*)&config, (struct peer_t*)peer) == -1 ) + { + p_log_add((time_t)ts.tv_sec, "socket error, aborting\n"); + exit(1); + } + + p_log_add((time_t)ts.tv_sec, "configuration reloaded\n"); + signal(sig,p_main_sighup); +} + +int mydaemon(int nochdir, int noclose) +{ + int fd; + + /* set new uid/gid */ + if ( setgid(config.gid) == -1 ) + { + #ifdef DEBUG + printf("failed to set setgid()\n"); + #else + p_log_add((time_t)ts.tv_sec, "failed to set setgid()\n"); + #endif + return (-1); + } + + if ( setegid(config.gid) == -1 ) + { + #ifdef DEBUG + printf("failed to set setegid()\n"); + #else + p_log_add((time_t)ts.tv_sec, "failed to set setegid()\n"); + #endif + return (-1); + } + + if ( setuid(config.uid) == -1 ) + { + #ifdef DEBUG + printf("failed to set setuid()\n"); + #else + p_log_add((time_t)ts.tv_sec, "failed to set setuid()\n"); + #endif + return (-1); + } + + if ( seteuid(config.uid) == -1 ) + { + #ifdef DEBUG + printf("failed to set seteuid()\n"); + #else + p_log_add((time_t)ts.tv_sec, "failed to set seteuid()\n"); + #endif + return (-1); + } + + switch (fork()) { + case -1: + return (-1); + case 0: + break; + default: + _exit(0); + } + + if (setsid() == -1) + return (-1); + + if ( !nochdir && chdir("/") == -1 ) + { + #ifdef DEBUG + printf("failed to chdir to /\n"); + #else + p_log_add((time_t)ts.tv_sec, "failed to chdir to /\n"); + #endif + return (-1); + } + + if ( !noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) + { + (void)dup2(fd, STDIN_FILENO); + (void)dup2(fd, STDOUT_FILENO); + (void)dup2(fd, STDERR_FILENO); + if (fd > 2) + (void)close(fd); + } + return (0); +} + +int mychown(char *path, uid_t uid, gid_t gid, int depth) +{ + DIR *dir; + struct dirent *item; + + if ( depth > 1 ) + return (0); + + if ( ( dir = opendir(path) ) == NULL ) + return (-1); + + while((item = readdir(dir))!=NULL) + { + char newpath[PATH_MAX]; + struct stat st; + + if ( item->d_name[0] == '.' ) + continue; + + sprintf(newpath, "%s/%s", path, item->d_name); + + if ( lstat(newpath, &st) == 0 ) + { + if ( st.st_uid != uid || st.st_gid != gid ) + { + if ( chown(newpath, uid, gid) != 0 ) + { + printf("ERROR: Failed to chown('%s', %i, %i)\n", + newpath, uid, gid); + return (-1); + } + } + + if ( S_ISDIR(st.st_mode) ) + mychown(newpath, uid, gid, depth+1); + } + } + + closedir(dir); + + return (0); +} diff --git a/src/p_ptoa.c b/src/p_ptoa.c new file mode 100644 index 0000000..9918959 --- /dev/null +++ b/src/p_ptoa.c @@ -0,0 +1,571 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include +#include +#include + +/* dump decoder tool */ +int main(int argc, char *argv[]) +{ + char *file = NULL; + int mode = PTOA_NONE; + struct dump_file_ctx *ctx; + + if ( argc != 3 ) + syntax(argv[0]); + else if ( strcmp(argv[1],"-m") == 0 ) + mode = PTOA_MACHINE; + else if ( strcmp(argv[1],"-H") == 0 ) + mode = PTOA_HUMAN; + else if ( strcmp(argv[1],"-j") == 0 ) + mode = PTOA_JSON; + else + syntax(argv[0]); + + file = argv[2]; + + if ( ( ctx = p_undump_open(file) ) == NULL ) + { + fprintf(stderr,"error opening '%s'\n",file); + return -1; + } + + while(!ctx->end) + { + struct dump_full_msg msg; + + if ( p_undump_readmsg(ctx, &msg) != 0 ) + { + fprintf(stderr,"error during message parsing of '%s'\n", ctx->file); + break; + } + + switch(mode) { + case PTOA_MACHINE: + { + unsigned long long int ts = msg.msg.ts; + printf("%llu|",ts); + } + break; + case PTOA_HUMAN: + { + char line[100]; + time_t ts = msg.msg.ts; + + p_tools_humantime(line, sizeof(line), ts); + printf("%s ",line); + } + break; + case PTOA_JSON: + { + unsigned long long int ts = msg.msg.ts; + printf("{ \"timestamp\": %llu, ",ts); + } + } + + switch(msg.msg.type) + { + case DUMP_HEADER4: + if ( mode == PTOA_MACHINE ) + printf("P|%u|%u\n",msg.header4.ip,msg.header4.as); + else if ( mode == PTOA_JSON ) + { + struct in_addr addr; + addr.s_addr = htobe32(msg.header4.ip); + printf("\"type\": \"peer\", \"msg\": { \"peer\": { \"proto\": \"ipv4\", \"ip\": \"%s\", \"asn\": %u } } }\n", + inet_ntoa(addr), msg.header4.as); + } + else + { + struct in_addr addr; + addr.s_addr = htobe32(msg.header4.ip); + printf("peer ip %s AS %u\n",inet_ntoa(addr),msg.header4.as); + } + break; + + case DUMP_HEADER6: + if ( mode == PTOA_MACHINE ) + { + struct in6_addr addr; + memcpy(addr.s6_addr, msg.header6.ip, sizeof(msg.header6.ip)); + printf("P|%s|%u\n",p_tools_ip6str(MAX_PEERS, &addr),msg.header6.as); + } + else if ( mode == PTOA_JSON ) + { + struct in6_addr addr; + memcpy(addr.s6_addr, msg.header6.ip, sizeof(msg.header6.ip)); + printf("\"type\": \"peer\", \"msg\": { \"peer\": { \"proto\": \"ipv6\", \"ip\": \"%s\", \"asn\": %u } } }\n", + p_tools_ip6str(MAX_PEERS, &addr),msg.header6.as); + } + else + { + struct in6_addr addr; + memcpy(addr.s6_addr, msg.header6.ip, sizeof(msg.header6.ip)); + printf("peer ip %s AS %u\n",p_tools_ip6str(MAX_PEERS, &addr),msg.header6.as); + } + break; + + case DUMP_OPEN: + if ( mode == PTOA_MACHINE ) + printf("C\n"); + else if ( mode == PTOA_JSON ) + printf("\"type\": \"connect\" }\n"); + else + printf("connected\n"); + break; + + case DUMP_CLOSE: + if ( mode == PTOA_MACHINE ) + printf("D\n"); + else if ( mode == PTOA_JSON ) + printf("\"type\": \"disconnect\" }\n"); + else + printf("disconnected\n"); + break; + + case DUMP_KEEPALIVE: + if ( mode == PTOA_MACHINE ) + printf("K\n"); + else if ( mode == PTOA_JSON ) + printf("\"type\": \"keepalive\" }\n"); + else + printf("keepalive\n"); + break; + + case DUMP_ANNOUNCE4: + if ( mode == PTOA_MACHINE ) + printf("A|%u|%u",msg.announce4.prefix, msg.announce4.mask); + else if ( mode == PTOA_JSON ) + { + struct in_addr addr; + addr.s_addr = htobe32(msg.announce4.prefix); + printf("\"type\": \"announce\", \"msg\": { \"prefix\": \"%s/%u\"", + inet_ntoa(addr),msg.announce4.mask); + } + else + { + struct in_addr addr; + addr.s_addr = htobe32(msg.announce4.prefix); + printf("prefix announce %s/%u",inet_ntoa(addr),msg.announce4.mask); + } + + if ( msg.announce4.origin != 0xff ) + { + if ( mode == PTOA_MACHINE ) + { + char o = '?'; + switch(msg.announce4.origin) + { + case 0: o = 'I'; break; + case 1: o = 'E'; break; + case 2: o = '?'; break; + } + printf("|O|%c", o); + } + else + { + char o[10]; + switch(msg.announce4.origin) + { + case 0: sprintf(o, "IGP"); break; + case 1: sprintf(o, "EGP"); break; + case 2: sprintf(o, "Unknown"); break; + default: sprintf(o, "Error"); break; + } + if ( mode == PTOA_JSON ) + printf(", \"origin\": \"%s\"", o); + else + printf(" origin %s", o); + } + } + + if ( msg.announce4.aspathlen > 0 ) + { + int i; + + if ( mode == PTOA_MACHINE ) + printf("|AP|"); + else if ( mode == PTOA_JSON ) + printf(", \"aspath\": [ "); + else + printf(" aspath"); + + for(i=0; i 0 ) + { + int i; + + if ( mode == PTOA_MACHINE ) + printf("|C|"); + else if ( mode == PTOA_JSON ) + printf(", \"community\": [ "); + else + printf(" community"); + + for(i=0; i 0 ) + { + int i; + + if ( mode == PTOA_MACHINE ) + printf("|EC|"); + else if ( mode == PTOA_JSON ) + printf(", \"extcommunity\": [ "); + else + printf(" extcommunity"); + + for(i=0; i 0 ) + { + int i; + + if ( mode == PTOA_MACHINE ) + printf("|AP|"); + else if ( mode == PTOA_JSON ) + printf(", \"aspath\": [ "); + else + printf(" aspath"); + + for(i=0; i 0 ) + { + int i; + + if ( mode == PTOA_MACHINE ) + printf("|C|"); + else if ( mode == PTOA_JSON ) + printf(", \"community\": [ "); + else + printf(" community"); + + for(i=0; i 0 ) + { + int i; + + if ( mode == PTOA_MACHINE ) + printf("|EC|"); + else if ( mode == PTOA_JSON ) + printf(", \"extcommunity\": [ "); + else + printf(" extcommunity"); + + for(i=0; i \n",prog); + printf("\n"); + printf("-H for human readable output\n"); + printf("\n"); + printf("-j for JSON output\n"); + printf("One JSON elem per line.\n"); + printf("\n"); + printf("-m for machine readable output:\n"); + printf("timestamp|P|peer_ip|peer_as # begin of every file\n"); + printf("timestamp|C # connected (Active -> Established)\n"); + printf("timestamp|D # disconnected (Established -> Active)\n"); + printf("timestamp|K # BGP Keepalive received\n"); + printf("timestamp|A|network|mask|opt id|opt|opt id ...\n"); + printf(" # BGP Announce\n"); + printf("timestamp|W|network|mask # BGP Withdrawn\n"); + printf("\n"); + + exit(-1); +} diff --git a/src/p_socket.c b/src/p_socket.c new file mode 100644 index 0000000..bb916dd --- /dev/null +++ b/src/p_socket.c @@ -0,0 +1,345 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + + +/* init the listening socket */ +int p_socket_start(struct config_t *config, struct peer_t *peer) +{ + #ifdef LINUX + int peerid; + #endif + struct timeval timeout; + + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + + if ( config->ip4.enabled ) + { + #ifdef DEBUG + printf("DEBUG: setting up IPv4 listening socket\n"); + #endif + + if ( config->ip4.sock > 0 ) + { + #ifdef DEBUG + printf("DEBUG: socket4 still open, closing\n"); + #endif + close(config->ip4.sock); + } + + if ( ( config->ip4.sock = socket(PF_INET, SOCK_STREAM, 0) ) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to init socket4\n"); + #endif + return -1; + } + + #ifdef DEBUG + else { printf("DEBUG: socket() ok\n"); } + #endif + + if ( setsockopt(config->ip4.sock, SOL_SOCKET, SO_REUSEADDR, "1", sizeof(int)) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to setsockopt() SO_REUSEADDR\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: setsockopt() (SO_REUSEADDR) ok\n"); } + #endif + + if ( ( setsockopt(config->ip4.sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) ) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to setsockopt() SO_SNDTIMEO\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: setsockopt() (SO_SNDTIMEO) ok\n"); } + #endif + + if ( ( setsockopt(config->ip4.sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) ) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to setsockopt() SO_RCVTIMEO\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: setsockopt() (SO_RCVTIMEO) ok\n"); } + #endif + + if ( fcntl(config->ip4.sock, F_SETFL, O_NONBLOCK) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to fcntl() O_NONBLOCK\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: fcntl() (O_NONBLOCK) ok\n"); } + #endif + + if ( bind(config->ip4.sock, (struct sockaddr*)&config->ip4.listen, sizeof(struct sockaddr_in)) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to bind socket\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: bind() ok\n"); } + #endif + } + + if ( config->ip6.enabled ) + { + #ifdef DEBUG + printf("DEBUG: setting up IPv6 listening socket\n"); + #endif + + if ( config->ip6.sock > 0 ) + { + #ifdef DEBUG + printf("DEBUG: socket4 still open, closing\n"); + #endif + close(config->ip6.sock); + } + + if ( ( config->ip6.sock = socket(PF_INET6, SOCK_STREAM, 0) ) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to init socket4\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: socket() ok\n"); } + #endif + + if ( setsockopt(config->ip6.sock, IPPROTO_IPV6, IPV6_V6ONLY, "1", sizeof(int)) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to setsockopt() IPV6_V6ONLY\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: setsockopt() (IPV6_V6ONLY) ok\n"); } + #endif + + if ( setsockopt(config->ip6.sock, SOL_SOCKET, SO_REUSEADDR, "1", sizeof(int)) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to setsockopt() SO_REUSEADDR\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: setsockopt() (SO_REUSEADDR) ok\n"); } + #endif + + if ( ( setsockopt(config->ip6.sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) ) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to setsockopt() SO_SNDTIMEO\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: setsockopt() (SO_SNDTIMEO) ok\n"); } + #endif + + if ( ( setsockopt(config->ip6.sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) ) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to setsockopt() SO_RCVTIMEO\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: setsockopt() (SO_RCVTIMEO) ok\n"); } + #endif + + if ( fcntl(config->ip6.sock, F_SETFL, O_NONBLOCK) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to fcntl() O_NONBLOCK\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: fcntl() (O_NONBLOCK) ok\n"); } + #endif + + if ( bind(config->ip6.sock, (struct sockaddr*)&config->ip6.listen, sizeof(struct sockaddr_in6)) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to bind socket\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: bind() ok\n"); } + #endif + } + + + // TCP MD5 currently only supported on Linux + #ifdef LINUX + for(peerid=0; peeridip4.enabled ) + { + + #ifdef DEBUG + printf("md5 key %s len %i addr %s\n", + md5.tcpm_key, + md5.tcpm_keylen, + p_tools_ip4str(peerid, &peer[peerid].ip4)); + #endif + + if ( ( r = setsockopt(config->ip4.sock, IPPROTO_TCP, TCP_MD5SIG, &md5, sizeof md5)) != 0 ) + { + #ifdef DEBUG + printf("Activating MD5SIG failed: '%s'\n", strerror(errno)); + #endif + return -1; + } + } + else if ( peer[peerid].af == 6 && config->ip6.enabled ) + { + + #ifdef DEBUG + printf("md5 key %s len %i addr %s\n", + md5.tcpm_key, + md5.tcpm_keylen, + p_tools_ip6str(peerid, &peer[peerid].ip6)); + #endif + + if ( ( r = setsockopt(config->ip6.sock, IPPROTO_TCP, TCP_MD5SIG, &md5, sizeof md5)) != 0 ) + { + #ifdef DEBUG + printf("Activating MD5SIG failed: '%s'\n", strerror(errno)); + #endif + return -1; + } + } + } + } + #endif + + if ( config->ip4.enabled ) + { + if ( listen(config->ip4.sock, 10) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to listen() socket\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: listen() ok\n"); } + #endif + } + + if ( config->ip6.enabled ) + { + + if ( listen(config->ip6.sock, 10) == -1 ) + { + #ifdef DEBUG + printf("DEBUG: failed to listen() socket\n"); + #endif + return -1; + } + #ifdef DEBUG + else { printf("DEBUG: listen() ok\n"); } + #endif + } + + return 0; +} + +/* check for new peers */ +int p_socket_accept(struct config_t *config) +{ + int sock; + struct sockaddr_in sockaddr; + struct sockaddr_in6 sockaddr6; + unsigned int addrlen = sizeof(sockaddr); + unsigned int addrlen6 = sizeof(sockaddr6); + + if ( ( sock = accept(config->ip4.sock, (struct sockaddr*)&sockaddr, &addrlen) ) == -1 ) + { + if ( ( sock = accept(config->ip6.sock, (struct sockaddr*)&sockaddr6, &addrlen6) ) == -1 ) + { + return -1; + } + } + return sock; +} diff --git a/src/p_tools.c b/src/p_tools.c new file mode 100644 index 0000000..d8eb069 --- /dev/null +++ b/src/p_tools.c @@ -0,0 +1,131 @@ +/*******************************************************************************/ +/* */ +/* Copyright 2004-2017 Pascal Gloor */ +/* */ +/* Licensed under the Apache License, Version 2.0 (the "License"); */ +/* you may not use this file except in compliance with the License. */ +/* You may obtain a copy of the License at */ +/* */ +/* http://www.apache.org/licenses/LICENSE-2.0 */ +/* */ +/* Unless required by applicable law or agreed to in writing, software */ +/* distributed under the License is distributed on an "AS IS" BASIS, */ +/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +/* See the License for the specific language governing permissions and */ +/* limitations under the License. */ +/* */ +/*******************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +int p_tools_ip4zero(struct in_addr *ip) +{ + if ( ip != NULL && ip->s_addr == 0 ) + return 1; + + return 0; +} +int p_tools_ip6zero(struct in6_addr *ip) +{ + if ( ip != NULL && + ip->s6_addr32[0] == 0 && + ip->s6_addr32[1] == 0 && + ip->s6_addr32[2] == 0 && + ip->s6_addr32[3] == 0 ) + return 1; + + return 0; +} + +int p_tools_sameip4(struct in_addr *ip1, struct in_addr *ip2) +{ + if ( ip1->s_addr == ip2->s_addr ) + return 1; + + return 0; +} + +int p_tools_sameip6(struct in6_addr *ip1, struct in6_addr *ip2) +{ + if ( ip1->s6_addr32[0] == ip2->s6_addr32[0] && + ip1->s6_addr32[1] == ip2->s6_addr32[1] && + ip1->s6_addr32[2] == ip2->s6_addr32[2] && + ip1->s6_addr32[3] == ip2->s6_addr32[3] ) + return 1; + + return 0; +} + +char *p_tools_ip4str(int peerid, struct in_addr *ip) +{ + static char buf[MAX_PEERS+1][INET_ADDRSTRLEN]; + + assert(peerid <= MAX_PEERS ? 1 : 0); + + inet_ntop(AF_INET, ip, buf[peerid], sizeof(buf[peerid])); + + return buf[peerid]; +} + +char *p_tools_ip6str(int peerid, struct in6_addr *ip) +{ + static char buf[MAX_PEERS+1][INET6_ADDRSTRLEN]; + + assert(peerid <= MAX_PEERS ? 1 : 0); + + inet_ntop(AF_INET6, ip, buf[peerid], sizeof(buf[peerid])); + + return buf[peerid]; +} + +void p_tools_dump(const char *desc, char *data, int len) +{ + int full8 = len / 8 + 1; + int i,j; + + printf("DUMP BEGIN (length: %i octets)\n", len); + printf("-- %s\n", desc); + + for(i=0; i +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include +#include +#include + +struct dump_file_ctx *p_undump_open(char *file) +{ + struct dump_file_ctx *ctx; + + ctx = malloc(sizeof(struct dump_file_ctx)); + assert(ctx); + + memset(ctx, 0, sizeof(struct dump_file_ctx)); + + strcpy(ctx->file, file); + + if ( ( ctx->fh = fopen(file, "r") ) == NULL ) + { + free(ctx); + return NULL; + } + + return ctx; +} + +int p_undump_close(struct dump_file_ctx *ctx) +{ + assert(ctx); + + if ( ctx->fh ) + fclose(ctx->fh); + + free(ctx); + + return (0); +} + +int p_undump_readmsg(struct dump_file_ctx *ctx, struct dump_full_msg *fmsg) +{ + char buffer[65536]; + struct dump_msg msg; + int len; + + if ( ctx == NULL ) + return (-1); + + if ( ctx->fh == NULL ) + return (-1); + + if ( fmsg == NULL ) + return (-1); + + if ( ctx->end ) + return (-1); + + if ( ( len = fread(&msg, 1, sizeof(msg), ctx->fh) ) != sizeof(msg) ) + return (-1); + + #ifdef DEBUG + // p_tools_dump("Header Dump", (char*)&msg, sizeof(msg)); + #endif + + /* convert header fields */ + msg.len = be16toh(msg.len); + msg.ts = be64toh(msg.ts); + memcpy(&fmsg->msg, &msg, sizeof(msg)); + + #ifdef DEBUG + printf("type %u len %u timestamp %llu\n",msg.type,msg.len,(unsigned long long int)msg.ts); + #endif + + + memset(buffer, 0, sizeof(buffer)); + + if ( msg.len && ( len = fread(buffer, 1, msg.len, ctx->fh) ) != msg.len ) + return (-1); + + #ifdef DEBUG + // p_tools_dump("Message Dump", buffer, msg.len); + #endif + + if ( msg.type == DUMP_HEADER4 && !ctx->head ) + { + struct dump_header4 *header = (struct dump_header4 *)(buffer); + fmsg->header4.ip = be32toh(header->ip); + fmsg->header4.as = be32toh(header->as); + ctx->head=1; + } + else if ( msg.type == DUMP_HEADER6 && !ctx->head ) + { + struct dump_header6 *header = (struct dump_header6 *)(buffer); + memcpy(fmsg->header6.ip,header->ip, sizeof(header->ip)); + fmsg->header6.as = be32toh(header->as); + ctx->head=1; + } + else if ( msg.type == DUMP_OPEN && ctx->head ) + { + } + else if ( msg.type == DUMP_CLOSE && ctx->head ) + { + } + else if ( msg.type == DUMP_KEEPALIVE && ctx->head ) + { + } + else if ( ( msg.type == DUMP_ANNOUNCE4 || msg.type == DUMP_ANNOUNCE6 ) && ctx->head ) + { + int jump = 0; + struct dump_announce4 *announce4 = NULL; + struct dump_announce6 *announce6 = NULL; + struct dump_announce_aspath *aspath; + struct dump_announce_community *community; + struct dump_announce_extcommunity *extcommunity; + uint8_t aspathlen = 0; + uint8_t communitylen = 0; + uint8_t extcommunitylen = 0; + + if ( msg.type == DUMP_ANNOUNCE4 ) + { + announce4 = (struct dump_announce4*)buffer; + jump += sizeof(struct dump_announce4); + + fmsg->announce4.mask = announce4->mask; + fmsg->announce4.prefix = be32toh(announce4->prefix); + fmsg->announce4.origin = announce4->origin; + fmsg->announce4.aspathlen = announce4->aspathlen; + fmsg->announce4.communitylen = announce4->communitylen; + fmsg->announce4.extcommunitylen = announce4->extcommunitylen; + + aspathlen = fmsg->announce4.aspathlen; + communitylen = fmsg->announce4.communitylen; + extcommunitylen = fmsg->announce4.extcommunitylen; + + } + else if ( msg.type == DUMP_ANNOUNCE6 ) + { + announce6 = (struct dump_announce6*)buffer; + jump += sizeof(struct dump_announce6); + + memcpy(fmsg->announce6.prefix, announce6->prefix, sizeof(announce6->prefix)); + fmsg->announce6.mask = announce6->mask; + fmsg->announce6.origin = announce6->origin; + fmsg->announce6.aspathlen = announce6->aspathlen; + fmsg->announce6.communitylen = announce6->communitylen; + fmsg->announce6.extcommunitylen = announce6->extcommunitylen; + + aspathlen = fmsg->announce6.aspathlen; + communitylen = fmsg->announce6.communitylen; + extcommunitylen = fmsg->announce6.extcommunitylen; + } + + if ( aspathlen > 0 ) + { + int i; + aspath = (struct dump_announce_aspath*) (buffer+jump); + jump += sizeof(aspath->data[0]) * aspathlen; + + for(i=0; iaspath.data[i] = be32toh(aspath->data[i]); + } + + if ( communitylen > 0 ) + { + int i; + community = (struct dump_announce_community*) (buffer+jump); + jump += sizeof(community->data[0]) * communitylen; + + for(i=0; icommunity.data[i].asn = be16toh(community->data[i].asn); + fmsg->community.data[i].num = be16toh(community->data[i].num); + } + } + + if ( extcommunitylen > 0 ) + { + int i; + extcommunity = (struct dump_announce_extcommunity*) (buffer+jump); + jump += sizeof(extcommunity->data[0]) * extcommunitylen; + + for(i=0; iextcommunity.data[i].ip = be32toh(extcommunity->data[i].ip ); + fmsg->extcommunity.data[i].num = be32toh(extcommunity->data[i].num); + } + } + } + else if ( msg.type == DUMP_WITHDRAWN4 && ctx->head ) + { + struct dump_withdrawn4 *withdrawn4 = (struct dump_withdrawn4*)buffer; + + fmsg->withdrawn4.mask = withdrawn4->mask; + fmsg->withdrawn4.prefix = be32toh(withdrawn4->prefix); + } + else if ( msg.type == DUMP_WITHDRAWN6 && ctx->head ) + { + struct dump_withdrawn6 *withdrawn6 = (struct dump_withdrawn6*)buffer; + + fmsg->withdrawn6.mask = withdrawn6->mask; + memcpy(fmsg->withdrawn6.prefix, withdrawn6->prefix, sizeof(withdrawn6->prefix)); + } + else if ( msg.type == DUMP_FOOTER && ctx->head ) + { + ctx->end=1; + } + else + { + return (-1); + } + + return 0; +} diff --git a/utils/piranhactl.in b/utils/piranhactl.in new file mode 100755 index 0000000..07c2409 --- /dev/null +++ b/utils/piranhactl.in @@ -0,0 +1,117 @@ +#!/bin/sh + +# /*******************************************************************************/ +# /* */ +# /* Copyright 2004-2017 Pascal Gloor */ +# /* */ +# /* Licensed under the Apache License, Version 2.0 (the "License"); */ +# /* you may not use this file except in compliance with the License. */ +# /* You may obtain a copy of the License at */ +# /* */ +# /* http://www.apache.org/licenses/LICENSE-2.0 */ +# /* */ +# /* Unless required by applicable law or agreed to in writing, software */ +# /* distributed under the License is distributed on an "AS IS" BASIS, */ +# /* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */ +# /* See the License for the specific language governing permissions and */ +# /* limitations under the License. */ +# /* */ +# /*******************************************************************************/ + +WDIR="%PATH%"; +PIRANHA="${WDIR}/bin/piranha"; +CONFIG="${WDIR}/etc/piranha.conf"; +PIDFILE="${WDIR}/var/piranha.pid"; +PEERSFILE="${WDIR}/var/piranha.status"; + + +case "$1" in + +start) + printf 'piranha start: '; + + if [ -x ${PIRANHA} ] + then + if [ -r ${CONFIG} ] + then + ${PIRANHA} ${CONFIG} && echo "ok"; + else + echo "piranha fatal error, cannot find ${CONFIG}"; + fi + + else + echo "piranha fatal error cannot exec ${PIRANHA}"; + fi + ;; + +stop) + printf 'piranha stop : '; + + if [ -r ${PIDFILE} ] + then + PID=`cat ${PIDFILE}`; + if [ -n "${PID}" ] + then + kill -9 ${PID} && echo "ok"; + else + echo "piranha fatal error invalid PID '${PID}'"; + fi + rm -f ${PIDFILE}; + else + echo "piranha fatal error cannot find pid file ${PIDFILE}"; + fi + ;; + +reload) + printf 'piranha reload : '; + + echo "Reload does currently not work, please use restart"; + exit 1; + + if [ -r ${PIDFILE} ] + then + PID=`cat ${PIDFILE}`; + if [ -n "${PID}" ] + then + kill -1 ${PID} && echo "ok"; + else + echo "piranha fatal error invalid PID '${PID}'"; + fi + else + echo "piranha fatal error cannot find pid file ${PIDFILE}"; + fi + ;; + +status) + printf 'piranha status : '; + + if [ -r ${PIDFILE} ] + then + PID=`cat ${PIDFILE}`; + if [ -n "${PID}" -a -d "/proc/${PID}" ] + then + echo "running"; + else + echo "not running (crash?)" + exit 1; + fi + else + echo "not running"; + exit 1; + fi + ;; + +show) + $0 status > /dev/null && cat ${PEERSFILE} || echo "piranha is not running"; + ;; + +restart) + $0 stop; + $0 start; + ;; + +*) + echo "$0 { start | stop | reload | restart | status | show }" + ;; + +esac