Imported Upstream version 0.2
Vincent Bernat
15 years ago
0 | lldpd (0.2) | |
1 | ||
2 | * Add privilege separation | |
3 | * Add FDP support | |
4 | * Support CDP encapsulated into native VLAN | |
5 | * Various fixes | |
6 | ||
7 | -- Vincent Bernat <bernat@luffy.cx> | |
8 | ||
9 | lldpd (0.1) | |
10 | ||
11 | * Initial release | |
12 | ||
13 | -- Vincent Bernat <bernat@luffy.cx> | |
14 |
19 | 19 | be mapped on the bonding device. You can bridge vlan but not add vlans |
20 | 20 | on bridges. More complex setups may give false results. |
21 | 21 | |
22 | lldpctl allows to query information collected through the command line. | |
22 | lldpd uses privilege separation to increase its security. Two | |
23 | processes, one running as root and doing minimal stuff and the other | |
24 | running as an unprivileged user into a chroot doing most of the stuff, | |
25 | are cooperating. You need to create a user called "_lldpd" in a group | |
26 | "_lldpd" (this can be change with ./configure). You also need to | |
27 | create an empty directory "/var/run/lldpd" (it needs to be owned by | |
28 | root, not "_lldpd"!). | |
23 | 29 | |
24 | lldpd also implements CDP (Cisco Discovery Protocol), SONMP (Nortel | |
25 | Discovery Protocol) and EDP (Extreme Discovery Protocol). However, | |
26 | recent versions of IOS should support LLDP and most Extreme stuff | |
27 | support LLDP. When a EDP, CDP or SONMP frame is received on a given | |
28 | interface, lldpd starts sending EDP, CDP or SONMP frame on this | |
29 | interface. Informations collected through EDP/CDP/SONMP are integrated | |
30 | with other informations and can be queried with lldpctl or through | |
31 | SNMP. | |
30 | lldpctl allows to query information collected through the command | |
31 | line. If you don't want to run it as root, just install it setuid or | |
32 | setgid _lldpd. | |
33 | ||
34 | lldpd also implements CDP (Cisco Discovery Protocol), FDP (Foundry | |
35 | Discovery Protocol), SONMP (Nortel Discovery Protocol) and EDP | |
36 | (Extreme Discovery Protocol). However, recent versions of IOS should | |
37 | support LLDP and most Extreme stuff support LLDP. When a EDP, CDP or | |
38 | SONMP frame is received on a given interface, lldpd starts sending | |
39 | EDP, CDP, FDP or SONMP frame on this interface. Informations collected | |
40 | through EDP/CDP/FDP/SONMP are integrated with other informations and | |
41 | can be queried with lldpctl or through SNMP. | |
32 | 42 | |
33 | 43 | For bonding, you need 2.6.24 (in previous version, PACKET_ORIGDEV |
34 | 44 | affected only non multicast packets). See: |
0 | 0 | /* config.h.in. Generated from configure.ac by autoheader. */ |
1 | ||
2 | /* Define to 1 if you have the declaration of `ADVERTISED_2500baseX_Full', and | |
3 | to 0 if you don't. */ | |
4 | #undef HAVE_DECL_ADVERTISED_2500BASEX_FULL | |
5 | ||
6 | /* Define to 1 if you have the declaration of `ETHERTYPE_VLAN', and to 0 if | |
7 | you don't. */ | |
8 | #undef HAVE_DECL_ETHERTYPE_VLAN | |
9 | ||
10 | /* Define to 1 if you have the declaration of `PACKET_ORIGDEV', and to 0 if | |
11 | you don't. */ | |
12 | #undef HAVE_DECL_PACKET_ORIGDEV | |
1 | 13 | |
2 | 14 | /* Define to 1 if you have the declaration of `TAILQ_EMPTY', and to 0 if you |
3 | 15 | don't. */ |
32 | 44 | |
33 | 45 | /* Define to indicate the Net-SNMP library */ |
34 | 46 | #undef HAVE_NETSNMP |
47 | ||
48 | /* Define to 1 if `f_create_from_tstring_new' is member of `netsnmp_tdomain'. | |
49 | */ | |
50 | #undef HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW | |
35 | 51 | |
36 | 52 | /* Define to 1 if you have the <stdint.h> header file. */ |
37 | 53 | #undef HAVE_STDINT_H |
84 | 100 | /* Define to the version of this package. */ |
85 | 101 | #undef PACKAGE_VERSION |
86 | 102 | |
103 | /* Chroot directory */ | |
104 | #undef PRIVSEP_CHROOT | |
105 | ||
106 | /* Group for privilege separation */ | |
107 | #undef PRIVSEP_GROUP | |
108 | ||
109 | /* User for privilege separation */ | |
110 | #undef PRIVSEP_USER | |
111 | ||
87 | 112 | /* Define to 1 if you have the ANSI C header files. */ |
88 | 113 | #undef STDC_HEADERS |
89 | 114 |
0 | 0 | #! /bin/sh |
1 | 1 | # Guess values for system-dependent variables and create Makefiles. |
2 | # Generated by GNU Autoconf 2.61 for lldpd 0.1. | |
2 | # Generated by GNU Autoconf 2.61 for lldpd 0.2. | |
3 | 3 | # |
4 | 4 | # Report bugs to <bernat@luffy.cx>. |
5 | 5 | # |
573 | 573 | # Identity of this package. |
574 | 574 | PACKAGE_NAME='lldpd' |
575 | 575 | PACKAGE_TARNAME='lldpd' |
576 | PACKAGE_VERSION='0.1' | |
577 | PACKAGE_STRING='lldpd 0.1' | |
576 | PACKAGE_VERSION='0.2' | |
577 | PACKAGE_STRING='lldpd 0.2' | |
578 | 578 | PACKAGE_BUGREPORT='bernat@luffy.cx' |
579 | 579 | |
580 | 580 | ac_unique_file="src/lldpd.c" |
1210 | 1210 | # Omit some internal or obsolete options to make the list less imposing. |
1211 | 1211 | # This message is too long to be a string in the A/UX 3.1 sh. |
1212 | 1212 | cat <<_ACEOF |
1213 | \`configure' configures lldpd 0.1 to adapt to many kinds of systems. | |
1213 | \`configure' configures lldpd 0.2 to adapt to many kinds of systems. | |
1214 | 1214 | |
1215 | 1215 | Usage: $0 [OPTION]... [VAR=VALUE]... |
1216 | 1216 | |
1276 | 1276 | |
1277 | 1277 | if test -n "$ac_init_help"; then |
1278 | 1278 | case $ac_init_help in |
1279 | short | recursive ) echo "Configuration of lldpd 0.1:";; | |
1279 | short | recursive ) echo "Configuration of lldpd 0.2:";; | |
1280 | 1280 | esac |
1281 | 1281 | cat <<\_ACEOF |
1282 | 1282 | |
1290 | 1290 | --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] |
1291 | 1291 | --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) |
1292 | 1292 | --with-snmp Enable the use of SNMP |
1293 | --with-privsep-user Which user to use for privilege separation | |
1294 | --with-privsep-group Which group to use for privilege separation | |
1295 | --with-privsep-chroot Which directory to use to chroot lldpd | |
1293 | 1296 | |
1294 | 1297 | Some influential environment variables: |
1295 | 1298 | CC C compiler command |
1365 | 1368 | test -n "$ac_init_help" && exit $ac_status |
1366 | 1369 | if $ac_init_version; then |
1367 | 1370 | cat <<\_ACEOF |
1368 | lldpd configure 0.1 | |
1371 | lldpd configure 0.2 | |
1369 | 1372 | generated by GNU Autoconf 2.61 |
1370 | 1373 | |
1371 | 1374 | Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, |
1379 | 1382 | This file contains any messages produced by compilers while |
1380 | 1383 | running configure, to aid debugging if configure makes a mistake. |
1381 | 1384 | |
1382 | It was created by lldpd $as_me 0.1, which was | |
1385 | It was created by lldpd $as_me 0.2, which was | |
1383 | 1386 | generated by GNU Autoconf 2.61. Invocation command line was |
1384 | 1387 | |
1385 | 1388 | $ $0 $@ |
2069 | 2072 | |
2070 | 2073 | # Define the identity of the package. |
2071 | 2074 | PACKAGE='lldpd' |
2072 | VERSION='0.1' | |
2075 | VERSION='0.2' | |
2073 | 2076 | |
2074 | 2077 | |
2075 | 2078 | cat >>confdefs.h <<_ACEOF |
3331 | 3334 | fi |
3332 | 3335 | |
3333 | 3336 | |
3337 | ||
3338 | # Check whether --with-privsep-user was given. | |
3339 | if test "${with_privsep_user+set}" = set; then | |
3340 | withval=$with_privsep_user; | |
3341 | cat >>confdefs.h <<_ACEOF | |
3342 | #define PRIVSEP_USER "$withval" | |
3343 | _ACEOF | |
3344 | ||
3345 | else | |
3346 | ||
3347 | cat >>confdefs.h <<_ACEOF | |
3348 | #define PRIVSEP_USER "_lldpd" | |
3349 | _ACEOF | |
3350 | ||
3351 | fi | |
3352 | ||
3353 | ||
3354 | # Check whether --with-privsep-group was given. | |
3355 | if test "${with_privsep_group+set}" = set; then | |
3356 | withval=$with_privsep_group; | |
3357 | cat >>confdefs.h <<_ACEOF | |
3358 | #define PRIVSEP_GROUP "$withval" | |
3359 | _ACEOF | |
3360 | ||
3361 | else | |
3362 | ||
3363 | cat >>confdefs.h <<_ACEOF | |
3364 | #define PRIVSEP_GROUP "_lldpd" | |
3365 | _ACEOF | |
3366 | ||
3367 | fi | |
3368 | ||
3369 | ||
3370 | # Check whether --with-privsep-chroot was given. | |
3371 | if test "${with_privsep_chroot+set}" = set; then | |
3372 | withval=$with_privsep_chroot; | |
3373 | cat >>confdefs.h <<_ACEOF | |
3374 | #define PRIVSEP_CHROOT "$withval" | |
3375 | _ACEOF | |
3376 | ||
3377 | else | |
3378 | ||
3379 | cat >>confdefs.h <<_ACEOF | |
3380 | #define PRIVSEP_CHROOT "/var/run/lldpd" | |
3381 | _ACEOF | |
3382 | ||
3383 | fi | |
3384 | ||
3385 | ||
3334 | 3386 | # Checks for header files. |
3335 | 3387 | |
3336 | 3388 | { echo "$as_me:$LINENO: checking whether TAILQ_FIRST is declared" >&5 |
3656 | 3708 | fi |
3657 | 3709 | { echo "$as_me:$LINENO: result: $ac_cv_have_decl_PACKET_ORIGDEV" >&5 |
3658 | 3710 | echo "${ECHO_T}$ac_cv_have_decl_PACKET_ORIGDEV" >&6; } |
3711 | if test $ac_cv_have_decl_PACKET_ORIGDEV = yes; then | |
3712 | ||
3713 | cat >>confdefs.h <<_ACEOF | |
3714 | #define HAVE_DECL_PACKET_ORIGDEV 1 | |
3715 | _ACEOF | |
3716 | ||
3717 | ||
3718 | else | |
3719 | cat >>confdefs.h <<_ACEOF | |
3720 | #define HAVE_DECL_PACKET_ORIGDEV 0 | |
3721 | _ACEOF | |
3722 | ||
3723 | ||
3724 | fi | |
3725 | ||
3659 | 3726 | |
3660 | 3727 | { echo "$as_me:$LINENO: checking whether ADVERTISED_2500baseX_Full is declared" >&5 |
3661 | 3728 | echo $ECHO_N "checking whether ADVERTISED_2500baseX_Full is declared... $ECHO_C" >&6; } |
3710 | 3777 | fi |
3711 | 3778 | { echo "$as_me:$LINENO: result: $ac_cv_have_decl_ADVERTISED_2500baseX_Full" >&5 |
3712 | 3779 | echo "${ECHO_T}$ac_cv_have_decl_ADVERTISED_2500baseX_Full" >&6; } |
3780 | if test $ac_cv_have_decl_ADVERTISED_2500baseX_Full = yes; then | |
3781 | ||
3782 | cat >>confdefs.h <<_ACEOF | |
3783 | #define HAVE_DECL_ADVERTISED_2500BASEX_FULL 1 | |
3784 | _ACEOF | |
3785 | ||
3786 | ||
3787 | else | |
3788 | cat >>confdefs.h <<_ACEOF | |
3789 | #define HAVE_DECL_ADVERTISED_2500BASEX_FULL 0 | |
3790 | _ACEOF | |
3791 | ||
3792 | ||
3793 | fi | |
3794 | ||
3795 | ||
3796 | { echo "$as_me:$LINENO: checking whether ETHERTYPE_VLAN is declared" >&5 | |
3797 | echo $ECHO_N "checking whether ETHERTYPE_VLAN is declared... $ECHO_C" >&6; } | |
3798 | if test "${ac_cv_have_decl_ETHERTYPE_VLAN+set}" = set; then | |
3799 | echo $ECHO_N "(cached) $ECHO_C" >&6 | |
3800 | else | |
3801 | cat >conftest.$ac_ext <<_ACEOF | |
3802 | /* confdefs.h. */ | |
3803 | _ACEOF | |
3804 | cat confdefs.h >>conftest.$ac_ext | |
3805 | cat >>conftest.$ac_ext <<_ACEOF | |
3806 | /* end confdefs.h. */ | |
3807 | #include <net/ethernet.h> | |
3808 | ||
3809 | int | |
3810 | main () | |
3811 | { | |
3812 | #ifndef ETHERTYPE_VLAN | |
3813 | (void) ETHERTYPE_VLAN; | |
3814 | #endif | |
3815 | ||
3816 | ; | |
3817 | return 0; | |
3818 | } | |
3819 | _ACEOF | |
3820 | rm -f conftest.$ac_objext | |
3821 | if { (ac_try="$ac_compile" | |
3822 | case "(($ac_try" in | |
3823 | *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; | |
3824 | *) ac_try_echo=$ac_try;; | |
3825 | esac | |
3826 | eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 | |
3827 | (eval "$ac_compile") 2>conftest.er1 | |
3828 | ac_status=$? | |
3829 | grep -v '^ *+' conftest.er1 >conftest.err | |
3830 | rm -f conftest.er1 | |
3831 | cat conftest.err >&5 | |
3832 | echo "$as_me:$LINENO: \$? = $ac_status" >&5 | |
3833 | (exit $ac_status); } && { | |
3834 | test -z "$ac_c_werror_flag" || | |
3835 | test ! -s conftest.err | |
3836 | } && test -s conftest.$ac_objext; then | |
3837 | ac_cv_have_decl_ETHERTYPE_VLAN=yes | |
3838 | else | |
3839 | echo "$as_me: failed program was:" >&5 | |
3840 | sed 's/^/| /' conftest.$ac_ext >&5 | |
3841 | ||
3842 | ac_cv_have_decl_ETHERTYPE_VLAN=no | |
3843 | fi | |
3844 | ||
3845 | rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext | |
3846 | fi | |
3847 | { echo "$as_me:$LINENO: result: $ac_cv_have_decl_ETHERTYPE_VLAN" >&5 | |
3848 | echo "${ECHO_T}$ac_cv_have_decl_ETHERTYPE_VLAN" >&6; } | |
3849 | if test $ac_cv_have_decl_ETHERTYPE_VLAN = yes; then | |
3850 | ||
3851 | cat >>confdefs.h <<_ACEOF | |
3852 | #define HAVE_DECL_ETHERTYPE_VLAN 1 | |
3853 | _ACEOF | |
3854 | ||
3855 | ||
3856 | else | |
3857 | cat >>confdefs.h <<_ACEOF | |
3858 | #define HAVE_DECL_ETHERTYPE_VLAN 0 | |
3859 | _ACEOF | |
3860 | ||
3861 | ||
3862 | fi | |
3863 | ||
3713 | 3864 | |
3714 | 3865 | |
3715 | 3866 | # Checks for typedefs, structures, and compiler characteristics. |
4846 | 4997 | { (exit 1); exit 1; }; } |
4847 | 4998 | fi |
4848 | 4999 | |
5000 | { echo "$as_me:$LINENO: checking for netsnmp_tdomain.f_create_from_tstring_new" >&5 | |
5001 | echo $ECHO_N "checking for netsnmp_tdomain.f_create_from_tstring_new... $ECHO_C" >&6; } | |
5002 | if test "${ac_cv_member_netsnmp_tdomain_f_create_from_tstring_new+set}" = set; then | |
5003 | echo $ECHO_N "(cached) $ECHO_C" >&6 | |
5004 | else | |
5005 | cat >conftest.$ac_ext <<_ACEOF | |
5006 | /* confdefs.h. */ | |
5007 | _ACEOF | |
5008 | cat confdefs.h >>conftest.$ac_ext | |
5009 | cat >>conftest.$ac_ext <<_ACEOF | |
5010 | /* end confdefs.h. */ | |
5011 | ||
5012 | #include <net-snmp/net-snmp-config.h> | |
5013 | #include <net-snmp/net-snmp-includes.h> | |
5014 | #include <net-snmp/library/snmp_transport.h> | |
5015 | ||
5016 | ||
5017 | int | |
5018 | main () | |
5019 | { | |
5020 | static netsnmp_tdomain ac_aggr; | |
5021 | if (ac_aggr.f_create_from_tstring_new) | |
5022 | return 0; | |
5023 | ; | |
5024 | return 0; | |
5025 | } | |
5026 | _ACEOF | |
5027 | rm -f conftest.$ac_objext | |
5028 | if { (ac_try="$ac_compile" | |
5029 | case "(($ac_try" in | |
5030 | *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; | |
5031 | *) ac_try_echo=$ac_try;; | |
5032 | esac | |
5033 | eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 | |
5034 | (eval "$ac_compile") 2>conftest.er1 | |
5035 | ac_status=$? | |
5036 | grep -v '^ *+' conftest.er1 >conftest.err | |
5037 | rm -f conftest.er1 | |
5038 | cat conftest.err >&5 | |
5039 | echo "$as_me:$LINENO: \$? = $ac_status" >&5 | |
5040 | (exit $ac_status); } && { | |
5041 | test -z "$ac_c_werror_flag" || | |
5042 | test ! -s conftest.err | |
5043 | } && test -s conftest.$ac_objext; then | |
5044 | ac_cv_member_netsnmp_tdomain_f_create_from_tstring_new=yes | |
5045 | else | |
5046 | echo "$as_me: failed program was:" >&5 | |
5047 | sed 's/^/| /' conftest.$ac_ext >&5 | |
5048 | ||
5049 | cat >conftest.$ac_ext <<_ACEOF | |
5050 | /* confdefs.h. */ | |
5051 | _ACEOF | |
5052 | cat confdefs.h >>conftest.$ac_ext | |
5053 | cat >>conftest.$ac_ext <<_ACEOF | |
5054 | /* end confdefs.h. */ | |
5055 | ||
5056 | #include <net-snmp/net-snmp-config.h> | |
5057 | #include <net-snmp/net-snmp-includes.h> | |
5058 | #include <net-snmp/library/snmp_transport.h> | |
5059 | ||
5060 | ||
5061 | int | |
5062 | main () | |
5063 | { | |
5064 | static netsnmp_tdomain ac_aggr; | |
5065 | if (sizeof ac_aggr.f_create_from_tstring_new) | |
5066 | return 0; | |
5067 | ; | |
5068 | return 0; | |
5069 | } | |
5070 | _ACEOF | |
5071 | rm -f conftest.$ac_objext | |
5072 | if { (ac_try="$ac_compile" | |
5073 | case "(($ac_try" in | |
5074 | *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; | |
5075 | *) ac_try_echo=$ac_try;; | |
5076 | esac | |
5077 | eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 | |
5078 | (eval "$ac_compile") 2>conftest.er1 | |
5079 | ac_status=$? | |
5080 | grep -v '^ *+' conftest.er1 >conftest.err | |
5081 | rm -f conftest.er1 | |
5082 | cat conftest.err >&5 | |
5083 | echo "$as_me:$LINENO: \$? = $ac_status" >&5 | |
5084 | (exit $ac_status); } && { | |
5085 | test -z "$ac_c_werror_flag" || | |
5086 | test ! -s conftest.err | |
5087 | } && test -s conftest.$ac_objext; then | |
5088 | ac_cv_member_netsnmp_tdomain_f_create_from_tstring_new=yes | |
5089 | else | |
5090 | echo "$as_me: failed program was:" >&5 | |
5091 | sed 's/^/| /' conftest.$ac_ext >&5 | |
5092 | ||
5093 | ac_cv_member_netsnmp_tdomain_f_create_from_tstring_new=no | |
5094 | fi | |
5095 | ||
5096 | rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext | |
5097 | fi | |
5098 | ||
5099 | rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext | |
5100 | fi | |
5101 | { echo "$as_me:$LINENO: result: $ac_cv_member_netsnmp_tdomain_f_create_from_tstring_new" >&5 | |
5102 | echo "${ECHO_T}$ac_cv_member_netsnmp_tdomain_f_create_from_tstring_new" >&6; } | |
5103 | if test $ac_cv_member_netsnmp_tdomain_f_create_from_tstring_new = yes; then | |
5104 | ||
5105 | cat >>confdefs.h <<_ACEOF | |
5106 | #define HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW 1 | |
5107 | _ACEOF | |
5108 | ||
5109 | ||
5110 | fi | |
5111 | ||
4849 | 5112 | |
4850 | 5113 | # Checks for library functions. |
4851 | 5114 | |
5622 | 5885 | # report actual input values of CONFIG_FILES etc. instead of their |
5623 | 5886 | # values after options handling. |
5624 | 5887 | ac_log=" |
5625 | This file was extended by lldpd $as_me 0.1, which was | |
5888 | This file was extended by lldpd $as_me 0.2, which was | |
5626 | 5889 | generated by GNU Autoconf 2.61. Invocation command line was |
5627 | 5890 | |
5628 | 5891 | CONFIG_FILES = $CONFIG_FILES |
5675 | 5938 | _ACEOF |
5676 | 5939 | cat >>$CONFIG_STATUS <<_ACEOF |
5677 | 5940 | ac_cs_version="\\ |
5678 | lldpd config.status 0.1 | |
5941 | lldpd config.status 0.2 | |
5679 | 5942 | configured by $0, generated by GNU Autoconf 2.61, |
5680 | 5943 | with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" |
5681 | 5944 |
1 | 1 | # Process this file with autoconf to produce a configure script. |
2 | 2 | |
3 | 3 | AC_PREREQ(2.61) |
4 | AC_INIT(lldpd, 0.1, bernat@luffy.cx) | |
4 | AC_INIT(lldpd, 0.2, bernat@luffy.cx) | |
5 | 5 | AM_INIT_AUTOMAKE([foreign]) |
6 | 6 | AC_CONFIG_SRCDIR([src/lldpd.c]) |
7 | 7 | AC_CONFIG_HEADER([config.h]) |
21 | 21 | ) |
22 | 22 | AM_CONDITIONAL([USE_SNMP], [test "${with_snmp}" != "no"]) |
23 | 23 | |
24 | AC_ARG_WITH(privsep-user, | |
25 | AC_HELP_STRING([--with-privsep-user], | |
26 | [Which user to use for privilege separation]), | |
27 | AC_DEFINE_UNQUOTED([PRIVSEP_USER], "$withval", [User for privilege separation]), | |
28 | AC_DEFINE_UNQUOTED([PRIVSEP_USER], "_lldpd", [User for privilege separation])) | |
29 | AC_ARG_WITH(privsep-group, | |
30 | AC_HELP_STRING([--with-privsep-group], | |
31 | [Which group to use for privilege separation]), | |
32 | AC_DEFINE_UNQUOTED([PRIVSEP_GROUP], "$withval", [Group for privilege separation]), | |
33 | AC_DEFINE_UNQUOTED([PRIVSEP_GROUP], "_lldpd", [Group for privilege separation])) | |
34 | AC_ARG_WITH(privsep-chroot, | |
35 | AC_HELP_STRING([--with-privsep-chroot], | |
36 | [Which directory to use to chroot lldpd]), | |
37 | AC_DEFINE_UNQUOTED([PRIVSEP_CHROOT], "$withval", [Chroot directory]), | |
38 | AC_DEFINE_UNQUOTED([PRIVSEP_CHROOT], "/var/run/lldpd", [Chroot directory])) | |
39 | ||
24 | 40 | # Checks for header files. |
25 | 41 | AC_CHECK_DECLS([TAILQ_FIRST, TAILQ_NEXT, TAILQ_FOREACH, TAILQ_EMPTY],[],[],[[#include <sys/queue.h>]]) |
26 | AC_CHECK_DECL([PACKET_ORIGDEV],[],[],[[#include <linux/if_packet.h>]]) | |
27 | AC_CHECK_DECL([ADVERTISED_2500baseX_Full],[],[],[[#include <linux/ethtool.h>]]) | |
42 | AC_CHECK_DECLS([PACKET_ORIGDEV],[],[],[[#include <linux/if_packet.h>]]) | |
43 | AC_CHECK_DECLS([ADVERTISED_2500baseX_Full],[],[],[[#include <linux/ethtool.h>]]) | |
44 | AC_CHECK_DECLS([ETHERTYPE_VLAN],[],[],[[#include <net/ethernet.h>]]) | |
28 | 45 | |
29 | 46 | # Checks for typedefs, structures, and compiler characteristics. |
30 | 47 | AC_C_CONST |
31 | 48 | AC_CHECK_TYPES([int16_t, u_int16_t, int8_t, u_int8_t, int32_t, u_int32_t],[],[AC_MSG_ERROR([mandatory type not found])]) |
49 | AC_CHECK_MEMBERS([netsnmp_tdomain.f_create_from_tstring_new],,, | |
50 | [ | |
51 | #include <net-snmp/net-snmp-config.h> | |
52 | #include <net-snmp/net-snmp-includes.h> | |
53 | #include <net-snmp/library/snmp_transport.h> | |
54 | ]) | |
32 | 55 | |
33 | 56 | # Checks for library functions. |
34 | 57 | AC_REPLACE_FUNCS([strlcpy]) |
20 | 20 | .Nd LLDP daemon |
21 | 21 | .Sh SYNOPSIS |
22 | 22 | .Nm |
23 | .Op Fl dxcse | |
23 | .Op Fl dvxcse | |
24 | 24 | .Op Fl m Ar management |
25 | 25 | .Op Fl p Ar probe time |
26 | 26 | .Sh DESCRIPTION |
53 | 53 | will run in the foreground and log to |
54 | 54 | .Em stderr . |
55 | 55 | This option can be specified many times to increase verbosity. |
56 | .It Fl v | |
57 | Listen on VLAN as well. This option might be needed if your equipment | |
58 | send frames on VLAN instead of physical interface. This option enables | |
59 | .Nm | |
60 | to receive frames on VLAN interfaces as well. If you don't need this | |
61 | option, do not set it. | |
56 | 62 | .It Fl x |
57 | 63 | Enable SNMP subagent |
58 | 64 | With this option, |
61 | 67 | information about local system and remote systems through SNMP. |
62 | 68 | .It Fl c |
63 | 69 | Enable the support of CDP protocol to deal with Cisco routers that do |
70 | not speak LLDP. | |
71 | .It Fl f | |
72 | Enable the support of FDP protocol to deal with Foundry routers that do | |
64 | 73 | not speak LLDP. |
65 | 74 | .It Fl s |
66 | 75 | Enable the support of SONMP protocol to deal with Nortel routers and |
0 | 0 | sbin_PROGRAMS = lldpd lldpctl |
1 | 1 | |
2 | 2 | COMMON = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h llc.h edp.h |
3 | lldpd_SOURCES = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c features.c $(COMMON) | |
3 | lldpd_SOURCES = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c features.c client.c priv.c privsep_fdpass.c $(COMMON) | |
4 | 4 | lldpctl_SOURCES = lldpctl.c $(COMMON) |
5 | 5 | |
6 | 6 | lldpd_LDADD = @LIBOBJS@ |
7 | 7 | lldpctl_LDADD = @LIBOBJS@ |
8 | 8 | |
9 | 9 | if USE_SNMP |
10 | lldpd_SOURCES += agent.c | |
10 | lldpd_SOURCES += agent.c agent_priv.c | |
11 | 11 | lldpd_LDADD += @NETSNMP_LIB@ |
12 | 12 | endif |
30 | 30 | PRE_UNINSTALL = : |
31 | 31 | POST_UNINSTALL = : |
32 | 32 | sbin_PROGRAMS = lldpd$(EXEEXT) lldpctl$(EXEEXT) |
33 | @USE_SNMP_TRUE@am__append_1 = agent.c | |
33 | @USE_SNMP_TRUE@am__append_1 = agent.c agent_priv.c | |
34 | 34 | @USE_SNMP_TRUE@am__append_2 = @NETSNMP_LIB@ |
35 | 35 | subdir = src |
36 | 36 | DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in strlcpy.c |
49 | 49 | lldpctl_OBJECTS = $(am_lldpctl_OBJECTS) |
50 | 50 | lldpctl_DEPENDENCIES = @LIBOBJS@ |
51 | 51 | am__lldpd_SOURCES_DIST = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c \ |
52 | features.c log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h \ | |
53 | llc.h edp.h agent.c | |
54 | @USE_SNMP_TRUE@am__objects_2 = agent.$(OBJEXT) | |
52 | features.c client.c priv.c privsep_fdpass.c log.c ctl.c \ | |
53 | lldpd.h lldp.h cdp.h compat.h sonmp.h llc.h edp.h agent.c \ | |
54 | agent_priv.c | |
55 | @USE_SNMP_TRUE@am__objects_2 = agent.$(OBJEXT) agent_priv.$(OBJEXT) | |
55 | 56 | am_lldpd_OBJECTS = lldpd.$(OBJEXT) lldp.$(OBJEXT) cdp.$(OBJEXT) \ |
56 | 57 | sonmp.$(OBJEXT) edp.$(OBJEXT) iov.$(OBJEXT) features.$(OBJEXT) \ |
58 | client.$(OBJEXT) priv.$(OBJEXT) privsep_fdpass.$(OBJEXT) \ | |
57 | 59 | $(am__objects_1) $(am__objects_2) |
58 | 60 | lldpd_OBJECTS = $(am_lldpd_OBJECTS) |
59 | 61 | am__DEPENDENCIES_1 = |
158 | 160 | top_srcdir = @top_srcdir@ |
159 | 161 | COMMON = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h llc.h edp.h |
160 | 162 | lldpd_SOURCES = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c features.c \ |
161 | $(COMMON) $(am__append_1) | |
163 | client.c priv.c privsep_fdpass.c $(COMMON) $(am__append_1) | |
162 | 164 | lldpctl_SOURCES = lldpctl.c $(COMMON) |
163 | 165 | lldpd_LDADD = @LIBOBJS@ $(am__append_2) |
164 | 166 | lldpctl_LDADD = @LIBOBJS@ |
233 | 235 | |
234 | 236 | @AMDEP_TRUE@@am__include@ @am__quote@$(DEPDIR)/strlcpy.Po@am__quote@ |
235 | 237 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agent.Po@am__quote@ |
238 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/agent_priv.Po@am__quote@ | |
236 | 239 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cdp.Po@am__quote@ |
240 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Po@am__quote@ | |
237 | 241 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ctl.Po@am__quote@ |
238 | 242 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/edp.Po@am__quote@ |
239 | 243 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/features.Po@am__quote@ |
242 | 246 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpctl.Po@am__quote@ |
243 | 247 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lldpd.Po@am__quote@ |
244 | 248 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/log.Po@am__quote@ |
249 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/priv.Po@am__quote@ | |
250 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/privsep_fdpass.Po@am__quote@ | |
245 | 251 | @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sonmp.Po@am__quote@ |
246 | 252 | |
247 | 253 | .c.o: |
435 | 435 | return (u_char *)&long_ret; |
436 | 436 | case LLDP_SNMP_STATS_RX_DISCARDED: |
437 | 437 | case LLDP_SNMP_STATS_RX_ERRORS: |
438 | /* We discard only frame with errors. Therefore, the two values | |
439 | * are equal */ | |
438 | 440 | long_ret = hardware->h_rx_discarded_cnt; |
439 | 441 | return (u_char *)&long_ret; |
440 | 442 | case LLDP_SNMP_STATS_RX_TLVDISCARDED: |
441 | 443 | case LLDP_SNMP_STATS_RX_TLVUNRECOGNIZED: |
442 | /* Not really handled */ | |
443 | long_ret = 0; | |
444 | /* We discard only unrecognized TLV. Malformed TLV | |
445 | implies dropping the whole frame */ | |
446 | long_ret = hardware->h_rx_unrecognized_cnt; | |
444 | 447 | return (u_char *)&long_ret; |
445 | 448 | case LLDP_SNMP_STATS_RX_AGEOUTS: |
446 | 449 | long_ret = hardware->h_rx_ageout_cnt; |
794 | 797 | |
795 | 798 | scfg = cfg; |
796 | 799 | |
800 | /* We are chrooted, we don't want to handle persistent states */ | |
801 | netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, | |
802 | NETSNMP_DS_LIB_DONT_PERSIST_STATE, TRUE); | |
803 | /* Do not load any MIB */ | |
804 | setenv("MIBS", "", 1); | |
805 | ||
806 | /* We provide our UNIX domain transport */ | |
807 | agent_priv_register_domain(); | |
808 | ||
797 | 809 | init_agent("lldpAgent"); |
798 | ||
799 | 810 | REGISTER_MIB("lldp", lldp_vars, variable8, lldp_oid); |
800 | ||
801 | 811 | init_snmp("lldpAgent"); |
802 | 812 | |
803 | 813 | if ((rc = register_sysORTable(lldp_oid, OID_LENGTH(lldp_oid), |
0 | /* | |
1 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | /* Some of the code here (agent_priv_unix_*) has been adapted from code from | |
17 | * Net-SNMP project (snmplib/snmpUnixDomain.c). Net-SNMP project is licensed | |
18 | * using BSD and BSD-like licenses. I don't know the exact license of the file | |
19 | * snmplib/snmpUnixDomain.c. */ | |
20 | ||
21 | #include "lldpd.h" | |
22 | ||
23 | #include <unistd.h> | |
24 | #include <errno.h> | |
25 | ||
26 | #include <net-snmp/net-snmp-config.h> | |
27 | #include <net-snmp/net-snmp-includes.h> | |
28 | #include <net-snmp/agent/net-snmp-agent-includes.h> | |
29 | #include <net-snmp/agent/snmp_vars.h> | |
30 | #include <net-snmp/agent/util_funcs.h> | |
31 | #include <net-snmp/library/snmpUnixDomain.h> | |
32 | ||
33 | oid netsnmp_UnixDomain[] = { TRANSPORT_DOMAIN_LOCAL }; | |
34 | static netsnmp_tdomain unixDomain; | |
35 | ||
36 | static char * | |
37 | agent_priv_unix_fmtaddr(netsnmp_transport *t, void *data, int len) | |
38 | { | |
39 | /* We don't bother to implement the full function */ | |
40 | return strdup("Local Unix socket with privilege separation: unknown"); | |
41 | } | |
42 | ||
43 | static int | |
44 | agent_priv_unix_recv(netsnmp_transport *t, void *buf, int size, | |
45 | void **opaque, int *olength) | |
46 | { | |
47 | int rc = -1; | |
48 | socklen_t tolen = sizeof(struct sockaddr_un); | |
49 | struct sockaddr *to = NULL; | |
50 | ||
51 | if (t == NULL || t->sock < 0) | |
52 | goto recv_error; | |
53 | to = (struct sockaddr *)malloc(sizeof(struct sockaddr_un)); | |
54 | if (to == NULL) | |
55 | goto recv_error; | |
56 | memset(to, 0, tolen); | |
57 | if (getsockname(t->sock, to, &tolen) != 0) | |
58 | goto recv_error; | |
59 | while (rc < 0) { | |
60 | rc = recv(t->sock, buf, size, 0); | |
61 | if (rc < 0 && errno != EINTR) { | |
62 | LLOG_WARN("unable to receive from fd %d", | |
63 | t->sock); | |
64 | goto recv_error; | |
65 | } | |
66 | } | |
67 | *opaque = (void*)to; | |
68 | *olength = sizeof(struct sockaddr_un); | |
69 | return rc; | |
70 | ||
71 | recv_error: | |
72 | free(to); | |
73 | *opaque = NULL; | |
74 | *olength = 0; | |
75 | return -1; | |
76 | } | |
77 | ||
78 | static int | |
79 | agent_priv_unix_send(netsnmp_transport *t, void *buf, int size, | |
80 | void **opaque, int *olength) | |
81 | { | |
82 | int rc = -1; | |
83 | if (t != NULL && t->sock >= 0) { | |
84 | while (rc < 0) { | |
85 | rc = send(t->sock, buf, size, 0); | |
86 | if (rc < 0 && errno != EINTR) { | |
87 | break; | |
88 | } | |
89 | } | |
90 | } | |
91 | return rc; | |
92 | } | |
93 | ||
94 | static int | |
95 | agent_priv_unix_close(netsnmp_transport *t) | |
96 | { | |
97 | int rc = 0; | |
98 | ||
99 | if (t->sock >= 0) { | |
100 | rc = close(t->sock); | |
101 | t->sock = -1; | |
102 | return rc; | |
103 | } | |
104 | return -1; | |
105 | } | |
106 | ||
107 | static int | |
108 | agent_priv_unix_accept(netsnmp_transport *t) | |
109 | { | |
110 | LLOG_WARNX("should not have been called"); | |
111 | return -1; | |
112 | } | |
113 | ||
114 | netsnmp_transport * | |
115 | agent_priv_unix_transport(const char *string, int len, int local) | |
116 | { | |
117 | struct sockaddr_un addr; | |
118 | netsnmp_transport *t = NULL; | |
119 | ||
120 | if (local) { | |
121 | LLOG_WARNX("should not have been called for local transport"); | |
122 | return NULL; | |
123 | } | |
124 | if (!string) | |
125 | return NULL; | |
126 | if (len > 0 && len < (sizeof(addr.sun_path) - 1)) { | |
127 | addr.sun_family = AF_UNIX; | |
128 | memset(addr.sun_path, 0, sizeof(addr.sun_path)); | |
129 | strncpy(addr.sun_path, string, len); | |
130 | } else { | |
131 | LLOG_WARNX("path too long for Unix domain transport"); | |
132 | return NULL; | |
133 | } | |
134 | ||
135 | if ((t = (netsnmp_transport *) | |
136 | malloc(sizeof(netsnmp_transport))) == NULL) | |
137 | return NULL; | |
138 | ||
139 | memset(t, 0, sizeof(netsnmp_transport)); | |
140 | ||
141 | t->domain = netsnmp_UnixDomain; | |
142 | t->domain_length = | |
143 | sizeof(netsnmp_UnixDomain) / sizeof(netsnmp_UnixDomain[0]); | |
144 | ||
145 | if ((t->sock = priv_snmp_socket(&addr)) < 0) { | |
146 | netsnmp_transport_free(t); | |
147 | return NULL; | |
148 | } | |
149 | ||
150 | t->flags = NETSNMP_TRANSPORT_FLAG_STREAM; | |
151 | ||
152 | if ((t->remote = (u_char *) | |
153 | malloc(strlen(addr.sun_path))) == NULL) { | |
154 | agent_priv_unix_close(t); | |
155 | netsnmp_transport_free(t); | |
156 | return NULL; | |
157 | } | |
158 | memcpy(t->remote, addr.sun_path, strlen(addr.sun_path)); | |
159 | t->remote_length = strlen(addr.sun_path); | |
160 | ||
161 | t->msgMaxSize = 0x7fffffff; | |
162 | t->f_recv = agent_priv_unix_recv; | |
163 | t->f_send = agent_priv_unix_send; | |
164 | t->f_close = agent_priv_unix_close; | |
165 | t->f_accept = agent_priv_unix_accept; | |
166 | t->f_fmtaddr = agent_priv_unix_fmtaddr; | |
167 | ||
168 | return t; | |
169 | } | |
170 | ||
171 | netsnmp_transport * | |
172 | #if !HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW | |
173 | agent_priv_unix_create_tstring(const char *string, int local) | |
174 | #else | |
175 | agent_priv_unix_create_tstring(const char *string, int local, char *default_target) | |
176 | #endif | |
177 | { | |
178 | #if HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW | |
179 | if ((!string || *string == '\0') && default_target && | |
180 | *default_target != '\0') { | |
181 | string = default_target; | |
182 | } | |
183 | #endif | |
184 | if (!string) | |
185 | return NULL; | |
186 | return agent_priv_unix_transport(string, strlen(string), local); | |
187 | } | |
188 | ||
189 | netsnmp_transport * | |
190 | agent_priv_unix_create_ostring(const u_char * o, size_t o_len, int local) | |
191 | { | |
192 | return agent_priv_unix_transport((char *)o, o_len, local); | |
193 | } | |
194 | ||
195 | void | |
196 | agent_priv_register_domain() | |
197 | { | |
198 | unixDomain.name = netsnmp_UnixDomain; | |
199 | unixDomain.name_length = sizeof(netsnmp_UnixDomain) / sizeof(oid); | |
200 | unixDomain.prefix = (const char**)calloc(2, sizeof(char *)); | |
201 | unixDomain.prefix[0] = "unix"; | |
202 | #if !HAVE_NETSNMP_TDOMAIN_F_CREATE_FROM_TSTRING_NEW | |
203 | unixDomain.f_create_from_tstring = agent_priv_unix_create_tstring; | |
204 | #else | |
205 | unixDomain.f_create_from_tstring_new = agent_priv_unix_create_tstring; | |
206 | #endif | |
207 | unixDomain.f_create_from_ostring = agent_priv_unix_create_ostring; | |
208 | netsnmp_tdomain_register(&unixDomain); | |
209 | } |
13 | 13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
14 | 14 | */ |
15 | 15 | |
16 | /* We also supports FDP which is very similar to CDPv1 */ | |
16 | 17 | #include "lldpd.h" |
17 | 18 | |
18 | 19 | #include <errno.h> |
24 | 25 | { |
25 | 26 | struct cdp_header ch; |
26 | 27 | struct ethllc llc; |
27 | const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; | |
28 | const u_int8_t llcorg[] = LLC_ORG_CISCO; | |
28 | u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; | |
29 | u_int8_t llcorg[] = LLC_ORG_CISCO; | |
29 | 30 | struct iovec *iov = NULL; |
30 | 31 | struct cdp_tlv_head device; |
31 | 32 | struct cdp_tlv_head port; |
40 | 41 | if ((iov = (struct iovec*)realloc(iov, (++c + 1) * \ |
41 | 42 | sizeof(struct iovec))) == NULL) \ |
42 | 43 | fatal(NULL); |
44 | ||
45 | /* Handle FDP */ | |
46 | if (version == 0) { | |
47 | const u_int8_t fdpmcastaddr[] = FDP_MULTICAST_ADDR; | |
48 | const u_int8_t fdpllcorg[] = LLC_ORG_FOUNDRY; | |
49 | memcpy(mcastaddr, fdpmcastaddr, sizeof(mcastaddr)); | |
50 | memcpy(llcorg, fdpllcorg, sizeof(llcorg)); | |
51 | } | |
43 | 52 | |
44 | 53 | /* Ether + LLC */ |
45 | 54 | memset(&llc, 0, sizeof(llc)); |
57 | 66 | |
58 | 67 | /* CDP header */ |
59 | 68 | memset(&ch, 0, sizeof(ch)); |
60 | ch.version = version; | |
69 | if (version == 0) | |
70 | ch.version = 1; | |
71 | else | |
72 | ch.version = version; | |
61 | 73 | ch.ttl = chassis->c_ttl; |
62 | 74 | IOV_NEW; |
63 | 75 | iov[c].iov_base = &ch; |
103 | 115 | iov[c].iov_base = hardware->h_lport.p_descr; |
104 | 116 | iov[c].iov_len = strlen(hardware->h_lport.p_descr); |
105 | 117 | |
106 | /* Capaibilities */ | |
107 | memset(&cap, 0, sizeof(cap)); | |
108 | cap.head.tlv_type = htons(CDP_TLV_CAPABILITIES); | |
109 | cap.head.tlv_len = htons(sizeof(cap)); | |
110 | cap.cap = 0; | |
111 | if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) | |
112 | cap.cap |= CDP_CAP_ROUTER; | |
113 | if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) | |
114 | cap.cap |= CDP_CAP_BRIDGE; | |
115 | cap.cap = htonl(cap.cap); | |
116 | IOV_NEW; | |
117 | iov[c].iov_base = ∩ | |
118 | iov[c].iov_len = sizeof(cap); | |
119 | ||
118 | /* Capabilities */ | |
119 | if (version != 0) { | |
120 | memset(&cap, 0, sizeof(cap)); | |
121 | cap.head.tlv_type = htons(CDP_TLV_CAPABILITIES); | |
122 | cap.head.tlv_len = htons(sizeof(cap)); | |
123 | cap.cap = 0; | |
124 | if (chassis->c_cap_enabled & LLDP_CAP_ROUTER) | |
125 | cap.cap |= CDP_CAP_ROUTER; | |
126 | if (chassis->c_cap_enabled & LLDP_CAP_BRIDGE) | |
127 | cap.cap |= CDP_CAP_BRIDGE; | |
128 | cap.cap = htonl(cap.cap); | |
129 | IOV_NEW; | |
130 | iov[c].iov_base = ∩ | |
131 | iov[c].iov_len = sizeof(cap); | |
132 | } | |
133 | ||
120 | 134 | /* Software version */ |
121 | 135 | memset(&soft, 0, sizeof(soft)); |
122 | 136 | soft.tlv_type = htons(CDP_TLV_SOFTWARE); |
164 | 178 | return 0; |
165 | 179 | } |
166 | 180 | |
181 | /* cdp_decode also decodes FDP */ | |
167 | 182 | int |
168 | 183 | cdp_decode(struct lldpd *cfg, char *frame, int s, |
169 | 184 | struct lldpd_hardware *hardware, |
181 | 196 | char *software = NULL, *platform = NULL; |
182 | 197 | int software_len = 0, platform_len = 0; |
183 | 198 | const unsigned char cdpaddr[] = CDP_MULTICAST_ADDR; |
184 | int i, f, len, rlen; | |
199 | const unsigned char fdpaddr[] = CDP_MULTICAST_ADDR; | |
200 | int i, f, len, rlen, fdp = 0; | |
185 | 201 | |
186 | 202 | if ((chassis = calloc(1, sizeof(struct lldpd_chassis))) == NULL) { |
187 | 203 | LLOG_WARN("failed to allocate remote chassis"); |
201 | 217 | |
202 | 218 | llc = (struct ethllc *)frame; |
203 | 219 | if (memcmp(&llc->ether.dhost, cdpaddr, sizeof(cdpaddr)) != 0) { |
204 | LLOG_INFO("frame not targeted at CDP multicast address received on %s", | |
205 | hardware->h_ifname); | |
206 | goto malformed; | |
220 | if (memcmp(&llc->ether.dhost, fdpaddr, sizeof(fdpaddr)) != 0) | |
221 | fdp = 1; | |
222 | else { | |
223 | LLOG_INFO("frame not targeted at CDP/FDP multicast address received on %s", | |
224 | hardware->h_ifname); | |
225 | goto malformed; | |
226 | } | |
207 | 227 | } |
208 | 228 | if (ntohs(llc->ether.size) > s - sizeof(struct ieee8023)) { |
209 | 229 | LLOG_WARNX("incorrect 802.3 frame size reported on %s", |
211 | 231 | goto malformed; |
212 | 232 | } |
213 | 233 | if (llc->protoid != htons(LLC_PID_CDP)) { |
214 | LLOG_DEBUG("incorrect LLC protocol ID received on %s", | |
215 | hardware->h_ifname); | |
234 | if ((llc->protoid != htons(LLC_PID_DRIP)) && | |
235 | (llc->protoid != htons(LLC_PID_PAGP)) && | |
236 | (llc->protoid != htons(LLC_PID_PVSTP)) && | |
237 | (llc->protoid != htons(LLC_PID_UDLD)) && | |
238 | (llc->protoid != htons(LLC_PID_VTP)) && | |
239 | (llc->protoid != htons(LLC_PID_DTP)) && | |
240 | (llc->protoid != htons(LLC_PID_STP))) | |
241 | LLOG_DEBUG("incorrect LLC protocol ID received on %s", | |
242 | hardware->h_ifname); | |
216 | 243 | goto malformed; |
217 | 244 | } |
218 | 245 | f = sizeof(struct ethllc); |
219 | 246 | ch = (struct cdp_header *)(frame + f); |
220 | 247 | if ((ch->version != 1) && (ch->version != 2)) { |
221 | LLOG_WARNX("incorrect CDP version (%d) for frame received on %s", | |
248 | LLOG_WARNX("incorrect CDP/FDP version (%d) for frame received on %s", | |
222 | 249 | ch->version, hardware->h_ifname); |
223 | 250 | goto malformed; |
224 | 251 | } |
228 | 255 | cksum = iov_checksum(&iov, 1, 1); |
229 | 256 | /* An off-by-one error may happen. Just ignore it */ |
230 | 257 | if ((cksum != 0) && (cksum != 0xfffe)) { |
231 | LLOG_INFO("incorrect CDP checksum for frame received on %s (%d)", | |
258 | LLOG_INFO("incorrect CDP/FDP checksum for frame received on %s (%d)", | |
232 | 259 | hardware->h_ifname, cksum); |
233 | 260 | goto malformed; |
234 | 261 | } |
236 | 263 | f += sizeof(struct cdp_header); |
237 | 264 | while (f < s) { |
238 | 265 | if (f + sizeof(struct cdp_tlv_head) > s) { |
239 | LLOG_WARNX("CDP TLV header is too large for " | |
266 | LLOG_WARNX("CDP/FDP TLV header is too large for " | |
240 | 267 | "frame received on %s", |
241 | 268 | hardware->h_ifname); |
242 | 269 | goto malformed; |
244 | 271 | tlv = (struct cdp_tlv_head *)(frame + f); |
245 | 272 | len = ntohs(tlv->tlv_len) - sizeof(struct cdp_tlv_head); |
246 | 273 | if ((len < 0) || (f + sizeof(struct cdp_tlv_head) + len > s)) { |
247 | LLOG_WARNX("incorrect size in CDP TLV header for frame " | |
274 | LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame " | |
248 | 275 | "received on %s", |
249 | 276 | hardware->h_ifname); |
250 | 277 | goto malformed; |
268 | 295 | break; |
269 | 296 | case CDP_TLV_ADDRESSES: |
270 | 297 | if (len < 4) { |
271 | LLOG_WARNX("incorrect size in CDP TLV header for frame " | |
298 | LLOG_WARNX("incorrect size in CDP/FDP TLV header for frame " | |
272 | 299 | "received on %s", |
273 | 300 | hardware->h_ifname); |
274 | 301 | goto malformed; |
324 | 351 | f += len; |
325 | 352 | break; |
326 | 353 | case CDP_TLV_CAPABILITIES: |
354 | if (fdp) { | |
355 | /* Capabilities are ignored with FDP */ | |
356 | f += sizeof(struct cdp_tlv_head) + len; | |
357 | chassis->c_cap_enabled = chassis->c_cap_available = LLDP_CAP_BRIDGE; | |
358 | break; | |
359 | } | |
327 | 360 | f += sizeof(struct cdp_tlv_head); |
328 | 361 | if (len != 4) { |
329 | 362 | LLOG_WARNX("incorrect size for capabilities TLV " |
353 | 386 | f += len; |
354 | 387 | break; |
355 | 388 | default: |
356 | LLOG_DEBUG("unknown CDP TLV type (%d) received on %s", | |
389 | LLOG_DEBUG("unknown CDP/FDP TLV type (%d) received on %s", | |
357 | 390 | ntohs(tlv->tlv_type), hardware->h_ifname); |
358 | 391 | f += sizeof(struct cdp_tlv_head) + len; |
392 | hardware->h_rx_unrecognized_cnt++; | |
359 | 393 | } |
360 | 394 | } |
361 | 395 | if (!software && platform) { |
393 | 427 | (port->p_descr == NULL) || |
394 | 428 | (chassis->c_ttl == 0) || |
395 | 429 | (chassis->c_cap_enabled == 0)) { |
396 | LLOG_WARNX("some mandatory tlv are missing for frame received on %s", | |
430 | LLOG_WARNX("some mandatory CDP/FDP tlv are missing for frame received on %s", | |
397 | 431 | hardware->h_ifname); |
398 | 432 | goto malformed; |
399 | 433 | } |
428 | 462 | } |
429 | 463 | |
430 | 464 | int |
465 | fdp_send(struct lldpd *global, struct lldpd_chassis *chassis, | |
466 | struct lldpd_hardware *hardware) | |
467 | { | |
468 | return cdp_send(global, chassis, hardware, 0); | |
469 | } | |
470 | ||
471 | int | |
431 | 472 | cdp_guess(char *frame, int len, int version) |
432 | 473 | { |
433 | 474 | const u_int8_t mcastaddr[] = CDP_MULTICAST_ADDR; |
19 | 19 | #define CDP_MULTICAST_ADDR { \ |
20 | 20 | 0x01, 0x00, 0x0c, 0xcc, 0xcc, 0xcc \ |
21 | 21 | } |
22 | #define FDP_MULTICAST_ADDR { \ | |
23 | 0x01, 0xe0, 0x52, 0xcc, 0xcc, 0xcc, \ | |
24 | } | |
22 | 25 | #define LLC_ORG_CISCO { 0x00, 0x00, 0x0c } |
26 | #define LLC_ORG_FOUNDRY { 0x00, 0xe0, 0x52 } | |
23 | 27 | #define LLC_PID_CDP 0x2000 |
28 | /* Other protocols */ | |
29 | #define LLC_PID_DRIP 0x102 | |
30 | #define LLC_PID_PAGP 0x104 | |
31 | #define LLC_PID_PVSTP 0x10b | |
32 | #define LLC_PID_UDLD 0x111 | |
33 | #define LLC_PID_VTP 0x2003 | |
34 | #define LLC_PID_DTP 0x2004 | |
35 | #define LLC_PID_STP 0x200a | |
24 | 36 | |
25 | 37 | struct cdp_header { |
26 | 38 | u_int8_t version; |
0 | /* | |
1 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | #include "lldpd.h" | |
17 | ||
18 | struct client_handle client_handles[] = { | |
19 | { HMSG_NONE, client_handle_none }, | |
20 | { HMSG_GET_INTERFACES, client_handle_get_interfaces }, | |
21 | { HMSG_GET_CHASSIS, client_handle_get_port_related }, | |
22 | { HMSG_GET_PORT, client_handle_get_port_related }, | |
23 | { HMSG_GET_VLANS, client_handle_get_port_related }, | |
24 | { HMSG_SHUTDOWN, client_handle_shutdown }, | |
25 | { 0, NULL } }; | |
26 | ||
27 | void | |
28 | client_handle_client(struct lldpd *cfg, struct lldpd_client *client, | |
29 | char *buffer, int n) | |
30 | { | |
31 | struct hmsg *h; /* Reception */ | |
32 | struct hmsg *t; /* Sending */ | |
33 | struct client_handle *ch; | |
34 | ||
35 | if (n < sizeof(struct hmsg_hdr)) { | |
36 | LLOG_WARNX("too short message request received"); | |
37 | return; | |
38 | } | |
39 | h = (struct hmsg *)buffer; | |
40 | n -= sizeof(struct hmsg_hdr); | |
41 | if (n != h->hdr.len) { | |
42 | LLOG_WARNX("incorrect message size received from %d", | |
43 | h->hdr.pid); | |
44 | return; | |
45 | } | |
46 | ||
47 | if ((t = (struct hmsg*)calloc(1, MAX_HMSGSIZE)) == NULL) { | |
48 | LLOG_WARNX("unable to allocate memory to answer to %d", | |
49 | h->hdr.pid); | |
50 | return; | |
51 | } | |
52 | ctl_msg_init(t, h->hdr.type); | |
53 | for (ch = client_handles; ch->handle != NULL; ch++) { | |
54 | if (ch->type == h->hdr.type) { | |
55 | ch->handle(cfg, h, t); | |
56 | if (t->hdr.len == -1) { | |
57 | t->hdr.len = 0; | |
58 | t->hdr.type = HMSG_NONE; | |
59 | } | |
60 | if (ctl_msg_send(client->fd, t) == -1) | |
61 | LLOG_WARN("unable to send answer to client %d", | |
62 | h->hdr.pid); | |
63 | free(t); | |
64 | return; | |
65 | } | |
66 | } | |
67 | ||
68 | LLOG_WARNX("unknown message request (%d) received from %d", | |
69 | h->hdr.type, h->hdr.pid); | |
70 | free(t); | |
71 | return; | |
72 | } | |
73 | ||
74 | void | |
75 | client_handle_shutdown(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
76 | { | |
77 | LLOG_INFO("received shutdown request from client %d", | |
78 | r->hdr.pid); | |
79 | exit(0); | |
80 | } | |
81 | ||
82 | void | |
83 | client_handle_none(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
84 | { | |
85 | LLOG_INFO("received noop request from client %d", | |
86 | r->hdr.pid); | |
87 | s->hdr.len = -1; | |
88 | } | |
89 | ||
90 | void | |
91 | client_handle_get_interfaces(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
92 | { | |
93 | struct lldpd_interface *iff, *iff_next; | |
94 | struct lldpd_hardware *hardware; | |
95 | void *p; | |
96 | ||
97 | /* Build the list of interfaces */ | |
98 | TAILQ_HEAD(, lldpd_interface) ifs; | |
99 | TAILQ_INIT(&ifs); | |
100 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
101 | if ((iff = (struct lldpd_interface*)malloc(sizeof( | |
102 | struct lldpd_interface))) == NULL) | |
103 | fatal(NULL); | |
104 | iff->name = hardware->h_ifname; | |
105 | TAILQ_INSERT_TAIL(&ifs, iff, next); | |
106 | } | |
107 | ||
108 | p = &s->data; | |
109 | if (ctl_msg_pack_list(STRUCT_LLDPD_INTERFACE, &ifs, | |
110 | sizeof(struct lldpd_interface), s, &p) == -1) { | |
111 | LLOG_WARNX("unable to pack list of interfaces"); | |
112 | s->hdr.len = -1; | |
113 | } | |
114 | ||
115 | /* Free the temporary list */ | |
116 | for (iff = TAILQ_FIRST(&ifs); | |
117 | iff != NULL; | |
118 | iff = iff_next) { | |
119 | iff_next = TAILQ_NEXT(iff, next); | |
120 | TAILQ_REMOVE(&ifs, iff, next); | |
121 | free(iff); | |
122 | } | |
123 | } | |
124 | ||
125 | void | |
126 | client_handle_get_port_related(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
127 | { | |
128 | char *ifname; | |
129 | struct lldpd_hardware *hardware; | |
130 | void *p; | |
131 | ||
132 | ifname = (char*)(&r->data); | |
133 | if (ifname[r->hdr.len - 1] != 0) { | |
134 | LLOG_WARNX("bad message format for get port related message"); | |
135 | s->hdr.len = -1; | |
136 | return; | |
137 | } | |
138 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
139 | if (strncmp(ifname, hardware->h_ifname, IFNAMSIZ) == 0) { | |
140 | if ((hardware->h_rport == NULL) || | |
141 | (hardware->h_rchassis == NULL)) { | |
142 | s->hdr.len = 0; | |
143 | s->hdr.type = HMSG_NONE; | |
144 | return; | |
145 | } | |
146 | p = &s->data; | |
147 | switch (r->hdr.type) { | |
148 | case HMSG_GET_VLANS: | |
149 | if (ctl_msg_pack_list(STRUCT_LLDPD_VLAN, | |
150 | &hardware->h_rport->p_vlans, | |
151 | sizeof(struct lldpd_vlan), s, &p) == -1) { | |
152 | LLOG_WARNX("unable to send vlans information for " | |
153 | "interface %s for %d", ifname, r->hdr.pid); | |
154 | s->hdr.len = -1; | |
155 | return; | |
156 | } | |
157 | break; | |
158 | case HMSG_GET_PORT: | |
159 | if (ctl_msg_pack_structure(STRUCT_LLDPD_PORT, | |
160 | hardware->h_rport, | |
161 | sizeof(struct lldpd_port), s, &p) == -1) { | |
162 | LLOG_WARNX("unable to send port information for " | |
163 | "interface %s for %d", ifname, r->hdr.pid); | |
164 | s->hdr.len = -1; | |
165 | return; | |
166 | } | |
167 | break; | |
168 | case HMSG_GET_CHASSIS: | |
169 | if (ctl_msg_pack_structure(STRUCT_LLDPD_CHASSIS, | |
170 | hardware->h_rchassis, | |
171 | sizeof(struct lldpd_chassis), s, &p) == -1) { | |
172 | LLOG_WARNX("unable to send chassis information for " | |
173 | "interface %s for %d", ifname, r->hdr.pid); | |
174 | s->hdr.len = -1; | |
175 | return; | |
176 | } | |
177 | break; | |
178 | default: | |
179 | LLOG_WARNX("don't know what to do"); | |
180 | s->hdr.len = -1; | |
181 | return; | |
182 | } | |
183 | return; | |
184 | } | |
185 | } | |
186 | LLOG_WARNX("requested interface %s by %d was not found", | |
187 | ifname, r->hdr.pid); | |
188 | s->hdr.len = -1; | |
189 | return; | |
190 | } |
54 | 54 | #if !HAVE_DECL_PACKET_ORIGDEV |
55 | 55 | #define PACKET_ORIGDEV 9 |
56 | 56 | #endif |
57 | ||
58 | #if !HAVE_DECL_ETHERTYPE_VLAN | |
59 | #define ETHERTYPE_VLAN 0x8100 | |
60 | #endif |
22 | 22 | #include <sys/un.h> |
23 | 23 | |
24 | 24 | int |
25 | ctl_create(struct lldpd *cfg, char *name) | |
25 | ctl_create(char *name) | |
26 | 26 | { |
27 | 27 | int s; |
28 | 28 | struct sockaddr_un su; |
40 | 40 | rc = errno; close(s); errno = rc; |
41 | 41 | return -1; |
42 | 42 | } |
43 | TAILQ_INIT(&cfg->g_clients); | |
44 | 43 | return s; |
45 | 44 | } |
46 | 45 | |
138 | 137 | } |
139 | 138 | |
140 | 139 | void |
141 | ctl_cleanup(int c, char *name) | |
142 | { | |
143 | close(c); | |
140 | ctl_cleanup(char *name) | |
141 | { | |
144 | 142 | if (unlink(name) == -1) |
145 | 143 | LLOG_WARN("unable to unlink %s", name); |
146 | 144 | } |
109 | 109 | break; |
110 | 110 | } |
111 | 111 | } |
112 | if (info.port == -1) { | |
112 | if (deviceslot[i] == NULL) { | |
113 | 113 | info.slot = htons(8); |
114 | 114 | info.port = htons(if_nametoindex(hardware->h_ifname)); |
115 | 115 | } |
407 | 407 | default: |
408 | 408 | LLOG_DEBUG("unknown EDP TLV type (%d) received on %s", |
409 | 409 | tlv->tlv_type, hardware->h_ifname); |
410 | hardware->h_rx_unrecognized_cnt++; | |
410 | 411 | } |
411 | 412 | f += len; |
412 | 413 | } |
63 | 63 | if ((snprintf(path, SYSFS_PATH_MAX, |
64 | 64 | SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_FDB, name)) >= SYSFS_PATH_MAX) |
65 | 65 | LLOG_WARNX("path truncated"); |
66 | if ((f = open(path, 0)) < 0) { | |
66 | if ((f = priv_open(path)) < 0) { | |
67 | 67 | return old_iface_is_bridge(cfg, name); |
68 | 68 | } |
69 | 69 | close(f); |
116 | 116 | SYSFS_CLASS_NET "%s/" SYSFS_BRIDGE_PORT_ATTR, |
117 | 117 | name)) >= SYSFS_PATH_MAX) |
118 | 118 | LLOG_WARNX("path truncated"); |
119 | if ((f = open(path, 0)) < 0) { | |
119 | if ((f = priv_open(path)) < 0) { | |
120 | 120 | return old_iface_is_bridged(cfg, name); |
121 | 121 | } |
122 | 122 | close(f); |
522 | 522 | default: |
523 | 523 | /* Unknown Dot3 TLV, ignore it */ |
524 | 524 | f += size; |
525 | hardware->h_rx_unrecognized_cnt++; | |
525 | 526 | } |
526 | 527 | } else { |
527 | LLOG_WARNX("unknown org tlv received on %s", | |
528 | hardware->h_ifname); | |
529 | goto malformed; | |
528 | LLOG_INFO("unknown org tlv received on %s", | |
529 | hardware->h_ifname); | |
530 | hardware->h_rx_unrecognized_cnt++; | |
531 | f += size; | |
530 | 532 | } |
531 | 533 | break; |
532 | 534 | default: |
23 | 23 | #include <fcntl.h> |
24 | 24 | #include <fnmatch.h> |
25 | 25 | #include <time.h> |
26 | #include <netdb.h> | |
26 | #include <libgen.h> | |
27 | 27 | #include <sys/utsname.h> |
28 | 28 | #include <sys/types.h> |
29 | 29 | #include <sys/socket.h> |
31 | 31 | #include <sys/time.h> |
32 | 32 | #include <sys/ioctl.h> |
33 | 33 | #include <arpa/inet.h> |
34 | #include <netpacket/packet.h> | |
35 | 34 | #include <ifaddrs.h> |
36 | 35 | #include <net/if_arp.h> |
37 | 36 | #include <linux/filter.h> |
38 | 37 | #include <linux/if_vlan.h> |
38 | #include <linux/if_packet.h> | |
39 | 39 | #include <linux/sockios.h> |
40 | #include <linux/ethtool.h> | |
41 | 40 | |
42 | 41 | #ifdef USE_SNMP |
43 | 42 | #include <net-snmp/net-snmp-config.h> |
49 | 48 | void usage(void); |
50 | 49 | |
51 | 50 | int lldpd_iface_init(struct lldpd *, struct lldpd_hardware *); |
51 | int lldpd_iface_init_vlan(struct lldpd *, struct lldpd_vif *); | |
52 | void lldpd_iface_init_mtu(struct lldpd *, struct lldpd_hardware *); | |
52 | 53 | int lldpd_iface_close(struct lldpd *, struct lldpd_hardware *); |
53 | 54 | void lldpd_iface_multicast(struct lldpd *, const char *, int); |
54 | 55 | |
63 | 64 | { 0x6, 0, 0, 0x0000ffff }, \ |
64 | 65 | { 0x6, 0, 0, 0x00000000 }, |
65 | 66 | struct sock_filter lldpd_filter_lldp_f[] = { LLDPD_FILTER_LLDP_F }; |
67 | /* "ether dst 01:e0:52:cc:cc:cc" */ | |
68 | #define LLDPD_FILTER_FDP_F \ | |
69 | { 0x20, 0, 0, 0x00000002 }, \ | |
70 | { 0x15, 0, 3, 0x52cccccc }, \ | |
71 | { 0x28, 0, 0, 0x00000000 }, \ | |
72 | { 0x15, 0, 1, 0x000001e0 }, \ | |
73 | { 0x6, 0, 0, 0x0000ffff }, \ | |
74 | { 0x6, 0, 0, 0x00000000 }, | |
75 | struct sock_filter lldpd_filter_fdp_f[] = { LLDPD_FILTER_FDP_F }; | |
66 | 76 | /* "ether dst 01:00:0c:cc:cc:cc" */ |
67 | 77 | #define LLDPD_FILTER_CDP_F \ |
68 | 78 | { 0x20, 0, 0, 0x00000002 }, \ |
96 | 106 | { 0x20, 0, 0, 0x00000002 }, \ |
97 | 107 | { 0x15, 0, 2, 0xc200000e }, \ |
98 | 108 | { 0x28, 0, 0, 0x00000000 }, \ |
99 | { 0x15, 8, 9, 0x00000180 }, \ | |
109 | { 0x15, 11, 12, 0x00000180 }, \ | |
100 | 110 | { 0x20, 0, 0, 0x00000002 }, \ |
101 | 111 | { 0x15, 0, 2, 0x2b000000 }, \ |
102 | 112 | { 0x28, 0, 0, 0x00000000 }, \ |
103 | { 0x15, 4, 5, 0x000000e0 }, \ | |
113 | { 0x15, 7, 8, 0x000000e0 }, \ | |
104 | 114 | { 0x15, 1, 0, 0x0ccccccc }, \ |
105 | { 0x15, 0, 3, 0x81000100 }, \ | |
115 | { 0x15, 0, 2, 0x81000100 }, \ | |
106 | 116 | { 0x28, 0, 0, 0x00000000 }, \ |
107 | { 0x15, 0, 1, 0x00000100 }, \ | |
117 | { 0x15, 3, 4, 0x00000100 }, \ | |
118 | { 0x15, 0, 3, 0x52cccccc }, \ | |
119 | { 0x28, 0, 0, 0x00000000 }, \ | |
120 | { 0x15, 0, 1, 0x000001e0 }, \ | |
108 | 121 | { 0x6, 0, 0, 0x0000ffff }, \ |
109 | 122 | { 0x6, 0, 0, 0x00000000 }, |
110 | 123 | struct sock_filter lldpd_filter_any_f[] = { LLDPD_FILTER_ANY_F }; |
121 | 134 | SONMP_MULTICAST_ADDR, lldpd_filter_sonmp_f, sizeof(lldpd_filter_sonmp_f) }, |
122 | 135 | { LLDPD_MODE_EDP, 0, "EDP", 'e', edp_send, edp_decode, NULL, |
123 | 136 | EDP_MULTICAST_ADDR, lldpd_filter_edp_f, sizeof(lldpd_filter_edp_f) }, |
137 | { LLDPD_MODE_FDP, 0, "FDP", 'f', fdp_send, cdp_decode, NULL, | |
138 | FDP_MULTICAST_ADDR, lldpd_filter_fdp_f, sizeof(lldpd_filter_fdp_f) }, | |
124 | 139 | { 0, 0, "any", ' ', NULL, NULL, NULL, |
125 | 140 | {0,0,0,0,0,0}, lldpd_filter_any_f, sizeof(lldpd_filter_any_f) } |
126 | 141 | }; |
129 | 144 | struct lldpd_hardware *); |
130 | 145 | struct lldpd_hardware *lldpd_port_add(struct lldpd *, struct ifaddrs *); |
131 | 146 | void lldpd_loop(struct lldpd *); |
132 | void lldpd_hangup(int); | |
133 | 147 | void lldpd_shutdown(int); |
134 | 148 | void lldpd_exit(); |
135 | 149 | void lldpd_send_all(struct lldpd *); |
137 | 151 | int lldpd_guess_type(struct lldpd *, char *, int); |
138 | 152 | void lldpd_decode(struct lldpd *, char *, int, |
139 | 153 | struct lldpd_hardware *, int); |
140 | void lldpd_handle_client(struct lldpd *, struct lldpd_client *, | |
141 | char *, int); | |
142 | ||
143 | void lldpd_handle_none(struct lldpd *, struct hmsg *, | |
144 | struct hmsg *); | |
145 | void lldpd_handle_get_interfaces(struct lldpd *, struct hmsg *, | |
146 | struct hmsg *); | |
147 | void lldpd_handle_get_port_related(struct lldpd *, struct hmsg *, | |
148 | struct hmsg *); | |
149 | void lldpd_handle_shutdown(struct lldpd *, struct hmsg *, | |
150 | struct hmsg *); | |
151 | ||
152 | struct client_handle { | |
153 | enum hmsg_type type; | |
154 | void (*handle)(struct lldpd*, struct hmsg*, struct hmsg*); | |
155 | }; | |
156 | ||
157 | struct client_handle client_handles[] = { | |
158 | { HMSG_NONE, lldpd_handle_none }, | |
159 | { HMSG_GET_INTERFACES, lldpd_handle_get_interfaces }, | |
160 | { HMSG_GET_CHASSIS, lldpd_handle_get_port_related }, | |
161 | { HMSG_GET_PORT, lldpd_handle_get_port_related }, | |
162 | { HMSG_GET_VLANS, lldpd_handle_get_port_related }, | |
163 | { HMSG_SHUTDOWN, lldpd_handle_shutdown }, | |
164 | { 0, NULL } }; | |
165 | 154 | |
166 | 155 | char **saved_argv; |
167 | 156 | |
170 | 159 | { |
171 | 160 | extern const char *__progname; |
172 | 161 | #ifndef USE_SNMP |
173 | fprintf(stderr, "usage: %s [-d] [-c] [-s] [-e] [-p|-P] [-m ip]\n", __progname); | |
162 | fprintf(stderr, "usage: %s [-dvcse] [-p|-P] [-m ip]\n", __progname); | |
174 | 163 | #else /* USE_SNMP */ |
175 | fprintf(stderr, "usage: %s [-d] [-c] [-s] [-e] [-p|-P] [-m ip] [-x]\n", __progname); | |
164 | fprintf(stderr, "usage: %s [-dvcsex] [-p|-P] [-m ip]\n", __progname); | |
176 | 165 | #endif /* USE_SNMP */ |
177 | 166 | exit(1); |
178 | 167 | } |
179 | 168 | |
180 | int | |
181 | lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware) | |
182 | { | |
183 | struct sockaddr_ll sa; | |
169 | void | |
170 | lldpd_iface_init_mtu(struct lldpd *global, struct lldpd_hardware *hardware) | |
171 | { | |
184 | 172 | struct ifreq ifr; |
185 | int master; /* Bond device */ | |
186 | char if_bond[IFNAMSIZ]; | |
187 | int un = 1; | |
188 | short int filter; | |
189 | 173 | |
190 | 174 | /* get MTU */ |
191 | 175 | memset(&ifr, 0, sizeof(ifr)); |
195 | 179 | hardware->h_mtu = 1500; |
196 | 180 | } else |
197 | 181 | hardware->h_mtu = ifr.ifr_mtu; |
198 | ||
199 | /* Open listening socket to receive/send frames */ | |
200 | if ((hardware->h_raw = socket(PF_PACKET, SOCK_RAW, | |
201 | htons(ETH_P_ALL))) < 0) | |
202 | return errno; | |
203 | memset(&sa, 0, sizeof(sa)); | |
204 | sa.sll_family = AF_PACKET; | |
205 | sa.sll_protocol = 0; | |
206 | sa.sll_ifindex = if_nametoindex(hardware->h_ifname); | |
207 | if (bind(hardware->h_raw, (struct sockaddr*)&sa, sizeof(sa)) < 0) | |
208 | return errno; | |
182 | } | |
183 | ||
184 | int | |
185 | lldpd_iface_init_vlan(struct lldpd *global, struct lldpd_vif *vif) | |
186 | { | |
187 | int status; | |
188 | short int filter; | |
189 | ||
190 | lldpd_iface_init_mtu(global, (struct lldpd_hardware*)vif); | |
191 | status = priv_iface_init((struct lldpd_hardware*)vif, -1); | |
192 | if (status != 0) | |
193 | return status; | |
194 | ||
195 | if (global->g_multi) | |
196 | filter = LLDPD_MODE_ANY; | |
197 | else | |
198 | filter = LLDPD_MODE_LLDP; | |
199 | ||
200 | if (lldpd_iface_switchto(global, filter, | |
201 | (struct lldpd_hardware*)vif) == -1) { | |
202 | LLOG_WARNX("unable to apply filter"); | |
203 | return ENETDOWN; | |
204 | } | |
205 | ||
206 | lldpd_iface_multicast(global, vif->vif_ifname, 0); | |
207 | ||
208 | LLOG_DEBUG("vlan interface %s initialized (fd=%d)", vif->vif_ifname, | |
209 | vif->vif_raw); | |
210 | return 0; | |
211 | } | |
212 | ||
213 | int | |
214 | lldpd_iface_init(struct lldpd *global, struct lldpd_hardware *hardware) | |
215 | { | |
216 | int master; /* Bond device */ | |
217 | char if_bond[IFNAMSIZ]; | |
218 | int status; | |
219 | short int filter; | |
220 | ||
221 | lldpd_iface_init_mtu(global, hardware); | |
222 | status = priv_iface_init(hardware, -1); | |
223 | if (status != 0) | |
224 | return status; | |
209 | 225 | |
210 | 226 | if ((master = iface_is_enslaved(global, hardware->h_ifname)) != -1) { |
211 | 227 | /* With bonding device, we need to listen on the bond ! */ |
217 | 233 | hardware->h_raw_real = hardware->h_raw; |
218 | 234 | hardware->h_master = master; |
219 | 235 | hardware->h_raw = -1; |
220 | if ((hardware->h_raw = socket(PF_PACKET, SOCK_RAW, | |
221 | htons(ETH_P_ALL))) < 0) | |
222 | return errno; | |
223 | memset(&sa, 0, sizeof(sa)); | |
224 | sa.sll_family = AF_PACKET; | |
225 | sa.sll_protocol = 0; | |
226 | sa.sll_ifindex = master; | |
227 | if (bind(hardware->h_raw, (struct sockaddr*)&sa, | |
228 | sizeof(sa)) < 0) | |
229 | return errno; | |
230 | /* With bonding, we need to listen to bond device. We use | |
231 | * setsockopt() PACKET_ORIGDEV to get physical device instead of | |
232 | * bond device */ | |
233 | if (setsockopt(hardware->h_raw, SOL_PACKET, | |
234 | PACKET_ORIGDEV, &un, sizeof(un)) == -1) { | |
235 | LLOG_WARN("unable to setsockopt for master bonding device of %s. " | |
236 | "You will get inaccurate results", | |
237 | hardware->h_ifname); | |
238 | } | |
236 | status = priv_iface_init(hardware, master); | |
237 | if (status != 0) { | |
238 | close(hardware->h_raw_real); | |
239 | if (hardware->h_raw != -1) | |
240 | close(hardware->h_raw); | |
241 | return status; | |
242 | } | |
239 | 243 | } |
240 | 244 | |
241 | 245 | if (global->g_multi) |
259 | 263 | void |
260 | 264 | lldpd_iface_multicast(struct lldpd *global, const char *name, int remove) |
261 | 265 | { |
262 | struct ifreq ifr; | |
263 | int i; | |
266 | int i, rc; | |
264 | 267 | |
265 | 268 | for (i=0; global->g_protocols[i].mode != 0; i++) { |
266 | 269 | if (!global->g_protocols[i].enabled) continue; |
267 | memset(&ifr, 0, sizeof(ifr)); | |
268 | strlcpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); | |
269 | memcpy(ifr.ifr_hwaddr.sa_data, | |
270 | global->g_protocols[i].mac, ETH_ALEN); | |
271 | if (ioctl(global->g_sock, (remove)?SIOCDELMULTI:SIOCADDMULTI, | |
272 | &ifr) < 0) { | |
273 | if (errno == ENOENT) | |
274 | return; | |
275 | LLOG_INFO("unable to %s %s address to multicast filter for %s", | |
276 | (remove)?"delete":"add", | |
277 | global->g_protocols[i].name, | |
278 | name); | |
270 | if ((rc = priv_iface_multicast(name, | |
271 | global->g_protocols[i].mac, !remove)) != 0) { | |
272 | errno = rc; | |
273 | if (errno != ENOENT) | |
274 | LLOG_INFO("unable to %s %s address to multicast filter for %s", | |
275 | (remove)?"delete":"add", | |
276 | global->g_protocols[i].name, | |
277 | name); | |
279 | 278 | } |
280 | 279 | } |
281 | 280 | } |
391 | 390 | lldpd_cleanup(struct lldpd *cfg) |
392 | 391 | { |
393 | 392 | struct lldpd_hardware *hardware, *hardware_next; |
393 | struct lldpd_vif *vif, *vif_next; | |
394 | 394 | |
395 | 395 | for (hardware = TAILQ_FIRST(&cfg->g_hardware); hardware != NULL; |
396 | 396 | hardware = hardware_next) { |
411 | 411 | } |
412 | 412 | } |
413 | 413 | } |
414 | for (vif = TAILQ_FIRST(&cfg->g_vif); vif != NULL; | |
415 | vif = vif_next) { | |
416 | vif_next = TAILQ_NEXT(vif, vif_entries); | |
417 | if (vif->vif_flags == 0) { | |
418 | TAILQ_REMOVE(&cfg->g_vif, vif, vif_entries); | |
419 | lldpd_iface_close(cfg, (struct lldpd_hardware*)vif); | |
420 | free(vif); | |
421 | } | |
422 | } | |
423 | } | |
424 | ||
425 | struct lldpd_vif * | |
426 | lldpd_port_add_vlan(struct lldpd *cfg, struct ifaddrs *ifa) | |
427 | { | |
428 | struct lldpd_vif *vif; | |
429 | struct lldpd_hardware *hardware; | |
430 | struct vlan_ioctl_args ifv; | |
431 | ||
432 | TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) { | |
433 | if (strcmp(vif->vif_ifname, ifa->ifa_name) == 0) | |
434 | break; | |
435 | } | |
436 | ||
437 | if (vif == NULL) { | |
438 | if ((vif = (struct lldpd_vif *) | |
439 | calloc(1, sizeof(struct lldpd_vif))) == NULL) | |
440 | return NULL; | |
441 | vif->vif_raw = -1; | |
442 | vif->vif_raw_real = -1; | |
443 | } | |
444 | strlcpy(vif->vif_ifname, ifa->ifa_name, sizeof(vif->vif_ifname)); | |
445 | vif->vif_flags = ifa->ifa_flags; | |
446 | ||
447 | if (vif->vif_raw == -1) { | |
448 | ||
449 | if (lldpd_iface_init_vlan(cfg, vif) != 0) { | |
450 | free(vif); | |
451 | return NULL; | |
452 | } | |
453 | ||
454 | /* Find the real interface */ | |
455 | vif->vif_real = NULL; | |
456 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
457 | memset(&ifv, 0, sizeof(ifv)); | |
458 | ifv.cmd = GET_VLAN_REALDEV_NAME_CMD; | |
459 | strlcpy(ifv.device1, ifa->ifa_name, sizeof(ifv.device1)); | |
460 | if ((ioctl(cfg->g_sock, SIOCGIFVLAN, &ifv) >= 0) && | |
461 | (strncmp(hardware->h_ifname, | |
462 | ifv.u.device2, | |
463 | sizeof(ifv.u.device2)) == 0)) | |
464 | vif->vif_real = hardware; | |
465 | } | |
466 | if (vif->vif_real == NULL) { | |
467 | LLOG_WARNX("unable to find real interface for %s", | |
468 | ifa->ifa_name); | |
469 | free(vif); | |
470 | return NULL; | |
471 | } | |
472 | ||
473 | TAILQ_INSERT_TAIL(&cfg->g_vif, vif, vif_entries); | |
474 | } | |
475 | ||
476 | return vif; | |
414 | 477 | } |
415 | 478 | |
416 | 479 | struct lldpd_hardware * |
420 | 483 | struct lldpd_hardware *hardware; |
421 | 484 | struct lldpd_port *port; |
422 | 485 | struct lldpd_vlan *vlan; |
423 | struct ifreq ifr; | |
424 | 486 | struct vlan_ioctl_args ifv; |
425 | 487 | struct ethtool_cmd ethc; |
426 | 488 | u_int8_t *lladdr; |
490 | 552 | if ((vlan = (struct lldpd_vlan *) |
491 | 553 | calloc(1, sizeof(struct lldpd_vlan))) == NULL) |
492 | 554 | continue; |
493 | if (asprintf(&vlan->v_name, "%s", oifa->ifa_name) == -1) { | |
555 | if ((vlan->v_name = strdup(oifa->ifa_name)) == NULL) { | |
494 | 556 | free(vlan); |
495 | 557 | continue; |
496 | 558 | } |
510 | 572 | freeifaddrs(oifap); |
511 | 573 | |
512 | 574 | /* MAC/PHY */ |
513 | memset(&ifr, 0, sizeof(ifr)); | |
514 | memset(ðc, 0, sizeof(ethc)); | |
515 | strlcpy(ifr.ifr_name, hardware->h_ifname, sizeof(ifr.ifr_name)); | |
516 | ifr.ifr_data = (caddr_t)ðc; | |
517 | ethc.cmd = ETHTOOL_GSET; | |
518 | if (ioctl(cfg->g_sock, SIOCETHTOOL, &ifr) == 0) { | |
575 | if (priv_ethtool(hardware->h_ifname, ðc) == 0) { | |
519 | 576 | int j; |
520 | 577 | int advertised_ethtool_to_rfc3636[][2] = { |
521 | 578 | {ADVERTISED_10baseT_Half, LLDP_DOT3_LINK_AUTONEG_10BASE_T}, |
568 | 625 | break; |
569 | 626 | } |
570 | 627 | if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI; |
571 | } | |
628 | } else | |
629 | LLOG_DEBUG("unable to get eth info for %s", hardware->h_ifname); | |
572 | 630 | |
573 | 631 | if (!INTERFACE_OPENED(hardware)) { |
574 | 632 | |
575 | 633 | if (lldpd_iface_init(cfg, hardware) != 0) { |
634 | LLOG_WARN("unable to initialize %s", hardware->h_ifname); | |
576 | 635 | lldpd_vlan_cleanup(&hardware->h_lport); |
577 | free(hardware->h_lladdr); | |
578 | 636 | free(hardware->h_proto_macs); |
579 | 637 | free(hardware); |
580 | 638 | return (NULL); |
617 | 675 | struct lldpd_port *port; |
618 | 676 | struct lldpd_hardware *ohardware, *firstnull = NULL, *older = NULL; |
619 | 677 | int guess = LLDPD_MODE_LLDP; |
678 | ||
679 | /* Discard VLAN frames */ | |
680 | if ((s >= sizeof(struct ieee8023)) && | |
681 | (((struct ieee8023*)frame)->size == htons(ETHERTYPE_VLAN))) | |
682 | return; | |
620 | 683 | |
621 | 684 | if ((hardware->h_rlastframe != NULL) && |
622 | 685 | (hardware->h_rlastframe->size == s) && |
850 | 913 | } |
851 | 914 | |
852 | 915 | void |
853 | lldpd_handle_client(struct lldpd *cfg, struct lldpd_client *client, | |
854 | char *buffer, int n) | |
855 | { | |
856 | struct hmsg *h; /* Reception */ | |
857 | struct hmsg *t; /* Sending */ | |
858 | struct client_handle *ch; | |
859 | ||
860 | if (n < sizeof(struct hmsg_hdr)) { | |
861 | LLOG_WARNX("too short message request received"); | |
862 | return; | |
863 | } | |
864 | h = (struct hmsg *)buffer; | |
865 | n -= sizeof(struct hmsg_hdr); | |
866 | if (n != h->hdr.len) { | |
867 | LLOG_WARNX("incorrect message size received from %d", | |
868 | h->hdr.pid); | |
869 | return; | |
870 | } | |
871 | ||
872 | if ((t = (struct hmsg*)calloc(1, MAX_HMSGSIZE)) == NULL) { | |
873 | LLOG_WARNX("unable to allocate memory to answer to %d", | |
874 | h->hdr.pid); | |
875 | return; | |
876 | } | |
877 | ctl_msg_init(t, h->hdr.type); | |
878 | for (ch = client_handles; ch->handle != NULL; ch++) { | |
879 | if (ch->type == h->hdr.type) { | |
880 | ch->handle(cfg, h, t); | |
881 | if (t->hdr.len == -1) { | |
882 | t->hdr.len = 0; | |
883 | t->hdr.type = HMSG_NONE; | |
884 | } | |
885 | if (ctl_msg_send(client->fd, t) == -1) | |
886 | LLOG_WARN("unable to send answer to client %d", | |
887 | h->hdr.pid); | |
888 | free(t); | |
889 | return; | |
890 | } | |
891 | } | |
892 | ||
893 | LLOG_WARNX("unknown message request (%d) received from %d", | |
894 | h->hdr.type, h->hdr.pid); | |
895 | free(t); | |
896 | return; | |
897 | } | |
898 | ||
899 | void | |
900 | lldpd_handle_shutdown(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
901 | { | |
902 | LLOG_INFO("received shutdown request from client %d", | |
903 | r->hdr.pid); | |
904 | exit(0); | |
905 | } | |
906 | ||
907 | void | |
908 | lldpd_handle_none(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
909 | { | |
910 | LLOG_INFO("received noop request from client %d", | |
911 | r->hdr.pid); | |
912 | s->hdr.len = -1; | |
913 | } | |
914 | ||
915 | void | |
916 | lldpd_handle_get_interfaces(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
917 | { | |
918 | struct lldpd_interface *iff, *iff_next; | |
916 | lldpd_recv_all(struct lldpd *cfg) | |
917 | { | |
919 | 918 | struct lldpd_hardware *hardware; |
920 | void *p; | |
921 | ||
922 | /* Build the list of interfaces */ | |
923 | TAILQ_HEAD(, lldpd_interface) ifs; | |
924 | TAILQ_INIT(&ifs); | |
925 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
926 | if ((iff = (struct lldpd_interface*)malloc(sizeof( | |
927 | struct lldpd_interface))) == NULL) | |
928 | fatal(NULL); | |
929 | iff->name = hardware->h_ifname; | |
930 | TAILQ_INSERT_TAIL(&ifs, iff, next); | |
931 | } | |
932 | ||
933 | p = &s->data; | |
934 | if (ctl_msg_pack_list(STRUCT_LLDPD_INTERFACE, &ifs, | |
935 | sizeof(struct lldpd_interface), s, &p) == -1) { | |
936 | LLOG_WARNX("unable to pack list of interfaces"); | |
937 | s->hdr.len = -1; | |
938 | } | |
939 | ||
940 | /* Free the temporary list */ | |
941 | for (iff = TAILQ_FIRST(&ifs); | |
942 | iff != NULL; | |
943 | iff = iff_next) { | |
944 | iff_next = TAILQ_NEXT(iff, next); | |
945 | TAILQ_REMOVE(&ifs, iff, next); | |
946 | free(iff); | |
947 | } | |
948 | } | |
949 | ||
950 | void | |
951 | lldpd_handle_get_port_related(struct lldpd *cfg, struct hmsg *r, struct hmsg *s) | |
952 | { | |
953 | char *ifname; | |
954 | struct lldpd_hardware *hardware; | |
955 | void *p; | |
956 | ||
957 | ifname = (char*)(&r->data); | |
958 | if (ifname[r->hdr.len - 1] != 0) { | |
959 | LLOG_WARNX("bad message format for get port related message"); | |
960 | s->hdr.len = -1; | |
961 | return; | |
962 | } | |
963 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { | |
964 | if (strncmp(ifname, hardware->h_ifname, IFNAMSIZ) == 0) { | |
965 | if ((hardware->h_rport == NULL) || | |
966 | (hardware->h_rchassis == NULL)) { | |
967 | s->hdr.len = 0; | |
968 | s->hdr.type = HMSG_NONE; | |
969 | return; | |
970 | } | |
971 | p = &s->data; | |
972 | switch (r->hdr.type) { | |
973 | case HMSG_GET_VLANS: | |
974 | if (ctl_msg_pack_list(STRUCT_LLDPD_VLAN, | |
975 | &hardware->h_rport->p_vlans, | |
976 | sizeof(struct lldpd_vlan), s, &p) == -1) { | |
977 | LLOG_WARNX("unable to send vlans information for " | |
978 | "interface %s for %d", ifname, r->hdr.pid); | |
979 | s->hdr.len = -1; | |
980 | return; | |
981 | } | |
982 | break; | |
983 | case HMSG_GET_PORT: | |
984 | if (ctl_msg_pack_structure(STRUCT_LLDPD_PORT, | |
985 | hardware->h_rport, | |
986 | sizeof(struct lldpd_port), s, &p) == -1) { | |
987 | LLOG_WARNX("unable to send port information for " | |
988 | "interface %s for %d", ifname, r->hdr.pid); | |
989 | s->hdr.len = -1; | |
990 | return; | |
991 | } | |
992 | break; | |
993 | case HMSG_GET_CHASSIS: | |
994 | if (ctl_msg_pack_structure(STRUCT_LLDPD_CHASSIS, | |
995 | hardware->h_rchassis, | |
996 | sizeof(struct lldpd_chassis), s, &p) == -1) { | |
997 | LLOG_WARNX("unable to send chassis information for " | |
998 | "interface %s for %d", ifname, r->hdr.pid); | |
999 | s->hdr.len = -1; | |
1000 | return; | |
1001 | } | |
1002 | break; | |
1003 | default: | |
1004 | LLOG_WARNX("don't know what to do"); | |
1005 | s->hdr.len = -1; | |
1006 | return; | |
1007 | } | |
1008 | return; | |
1009 | } | |
1010 | } | |
1011 | LLOG_WARNX("requested interface %s by %d was not found", | |
1012 | ifname, r->hdr.pid); | |
1013 | s->hdr.len = -1; | |
1014 | return; | |
1015 | } | |
1016 | ||
1017 | void | |
1018 | lldpd_recv_all(struct lldpd *cfg) | |
1019 | { | |
1020 | struct lldpd_hardware *hardware; | |
919 | struct lldpd_vif *vif; | |
1021 | 920 | struct lldpd_client *client, *client_next; |
1022 | 921 | fd_set rfds; |
1023 | 922 | struct timeval tv; |
1057 | 956 | nfds = hardware->h_raw_real; |
1058 | 957 | } |
1059 | 958 | } |
959 | TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) { | |
960 | if ((vif->vif_flags & IFF_UP) == 0) | |
961 | continue; | |
962 | FD_SET(vif->vif_raw, &rfds); | |
963 | if (nfds < vif->vif_raw) | |
964 | nfds = vif->vif_raw; | |
965 | } | |
1060 | 966 | TAILQ_FOREACH(client, &cfg->g_clients, next) { |
1061 | 967 | FD_SET(client->fd, &rfds); |
1062 | 968 | if (nfds < client->fd) |
1090 | 996 | snmp_timeout(); |
1091 | 997 | } |
1092 | 998 | #endif /* USE_SNMP */ |
999 | TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) { | |
1000 | if (!FD_ISSET(vif->vif_raw, &rfds)) | |
1001 | continue; | |
1002 | if ((buffer = (char *)malloc( | |
1003 | vif->vif_mtu)) == NULL) { | |
1004 | LLOG_WARN("failed to alloc reception buffer"); | |
1005 | continue; | |
1006 | } | |
1007 | fromlen = sizeof(from); | |
1008 | if ((n = recvfrom(vif->vif_raw, | |
1009 | buffer, | |
1010 | vif->vif_mtu, 0, | |
1011 | (struct sockaddr *)&from, | |
1012 | &fromlen)) == -1) { | |
1013 | LLOG_WARN("error while receiving frame on vlan %s", | |
1014 | vif->vif_ifname); | |
1015 | vif->vif_real->h_rx_discarded_cnt++; | |
1016 | free(buffer); | |
1017 | continue; | |
1018 | } | |
1019 | if (from.sll_pkttype == PACKET_OUTGOING) { | |
1020 | free(buffer); | |
1021 | continue; | |
1022 | } | |
1023 | if (!((cfg->g_multi) && | |
1024 | (vif->vif_real->h_mode != LLDPD_MODE_ANY) && | |
1025 | (lldpd_guess_type(cfg, buffer, n) != | |
1026 | vif->vif_real->h_mode))) { | |
1027 | vif->vif_real->h_rx_cnt++; | |
1028 | lldpd_decode(cfg, buffer, n, vif->vif_real, 0); | |
1029 | } | |
1030 | ||
1031 | free(buffer); | |
1032 | } | |
1093 | 1033 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) { |
1094 | 1034 | /* We could have received something on _real_ |
1095 | 1035 | * interface. However, even in this case, this could be |
1165 | 1105 | continue; |
1166 | 1106 | } |
1167 | 1107 | if (n > 0) |
1168 | lldpd_handle_client(cfg, client, buffer, n); | |
1108 | client_handle_client(cfg, client, buffer, n); | |
1169 | 1109 | else |
1170 | 1110 | ctl_close(cfg, client->fd); /* Will use TAILQ_REMOVE ! */ |
1171 | 1111 | free(buffer); |
1208 | 1148 | struct ifaddrs *ifap, *ifa; |
1209 | 1149 | struct sockaddr_ll *sdl; |
1210 | 1150 | struct lldpd_hardware *hardware; |
1151 | struct lldpd_vif *vif; | |
1211 | 1152 | int f; |
1212 | 1153 | char status; |
1213 | 1154 | struct utsname *un; |
1214 | struct hostent *hp; | |
1155 | char *hp; | |
1215 | 1156 | |
1216 | 1157 | /* Set system name and description */ |
1217 | 1158 | if ((un = (struct utsname*)malloc(sizeof(struct utsname))) == NULL) |
1218 | 1159 | fatal(NULL); |
1219 | 1160 | if (uname(un) != 0) |
1220 | 1161 | fatal("failed to get system information"); |
1221 | if ((hp = gethostbyname(un->nodename)) == NULL) | |
1162 | if ((hp = priv_gethostbyname()) == NULL) | |
1222 | 1163 | fatal("failed to get system name"); |
1223 | 1164 | free(cfg->g_lchassis.c_name); |
1224 | 1165 | free(cfg->g_lchassis.c_descr); |
1225 | if (asprintf(&cfg->g_lchassis.c_name, "%s", | |
1226 | hp->h_name) == -1) | |
1227 | fatal("failed to set system name"); | |
1166 | if ((cfg->g_lchassis.c_name = strdup(hp)) == NULL) | |
1167 | fatal(NULL); | |
1228 | 1168 | if (asprintf(&cfg->g_lchassis.c_descr, "%s %s %s %s", |
1229 | 1169 | un->sysname, un->release, un->version, un->machine) == -1) |
1230 | 1170 | fatal("failed to set system description"); |
1232 | 1172 | |
1233 | 1173 | /* Check forwarding */ |
1234 | 1174 | cfg->g_lchassis.c_cap_enabled = 0; |
1235 | if ((f = open("/proc/sys/net/ipv4/ip_forward", 0)) >= 0) { | |
1236 | if ((read(f, &status, 1) == 1) && (status == '1')) | |
1175 | if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) { | |
1176 | if ((read(f, &status, 1) == 1) && (status == '1')) { | |
1237 | 1177 | cfg->g_lchassis.c_cap_enabled = LLDP_CAP_ROUTER; |
1178 | } | |
1238 | 1179 | close(f); |
1239 | 1180 | } |
1240 | 1181 | |
1241 | 1182 | TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) |
1242 | 1183 | hardware->h_flags = 0; |
1184 | TAILQ_FOREACH(vif, &cfg->g_vif, vif_entries) | |
1185 | vif->vif_flags = 0; | |
1243 | 1186 | |
1244 | 1187 | if (getifaddrs(&ifap) != 0) |
1245 | 1188 | fatal("lldpd_loop: failed to get interface list"); |
1273 | 1216 | } |
1274 | 1217 | } |
1275 | 1218 | |
1219 | if (ifa->ifa_addr == NULL || | |
1220 | ifa->ifa_addr->sa_family != PF_PACKET) | |
1221 | continue; | |
1222 | ||
1223 | sdl = (struct sockaddr_ll *)ifa->ifa_addr; | |
1224 | if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen) | |
1225 | continue; | |
1226 | ||
1276 | 1227 | if (iface_is_bridge(cfg, ifa->ifa_name)) { |
1277 | 1228 | cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_BRIDGE; |
1278 | 1229 | continue; |
1282 | 1233 | (iface_is_bond(cfg, ifa->ifa_name))) |
1283 | 1234 | continue; |
1284 | 1235 | |
1285 | if (ifa->ifa_addr == NULL || | |
1286 | ifa->ifa_addr->sa_family != PF_PACKET) | |
1287 | continue; | |
1288 | ||
1289 | 1236 | if (!(ifa->ifa_flags & IFF_MULTICAST)) |
1290 | 1237 | continue; |
1291 | 1238 | |
1292 | sdl = (struct sockaddr_ll *)ifa->ifa_addr; | |
1293 | if (sdl->sll_hatype != ARPHRD_ETHER || !sdl->sll_halen) | |
1294 | continue; | |
1295 | ||
1296 | 1239 | if (iface_is_wireless(cfg, ifa->ifa_name)) |
1297 | 1240 | cfg->g_lchassis.c_cap_enabled |= LLDP_CAP_WLAN; |
1298 | ||
1299 | 1241 | |
1300 | 1242 | if (lldpd_port_add(cfg, ifa) == NULL) |
1301 | 1243 | LLOG_WARNX("failed to allocate port %s, skip it", |
1302 | 1244 | ifa->ifa_name); |
1303 | 1245 | } |
1304 | 1246 | |
1247 | /* Handle VLAN */ | |
1248 | if (cfg->g_listen_vlans) { | |
1249 | for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) { | |
1250 | if ((iface_is_vlan(cfg, ifa->ifa_name)) && | |
1251 | (lldpd_port_add_vlan(cfg, ifa) == NULL)) { | |
1252 | LLOG_WARNX("unable to allocate vlan %s, skip it", | |
1253 | ifa->ifa_name); | |
1254 | } | |
1255 | } | |
1256 | } | |
1257 | ||
1305 | 1258 | freeifaddrs(ifap); |
1306 | 1259 | |
1307 | 1260 | lldpd_cleanup(cfg); |
1311 | 1264 | } |
1312 | 1265 | |
1313 | 1266 | void |
1314 | lldpd_hangup(int sig) | |
1315 | { | |
1316 | /* Re-execute */ | |
1317 | LLOG_INFO("sighup received, reloading"); | |
1318 | lldpd_exit(); | |
1319 | execv(saved_argv[0], saved_argv); | |
1320 | } | |
1321 | ||
1322 | void | |
1323 | 1267 | lldpd_shutdown(int sig) |
1324 | 1268 | { |
1325 | 1269 | LLOG_INFO("signal received, exiting"); |
1333 | 1277 | lldpd_exit() |
1334 | 1278 | { |
1335 | 1279 | struct lldpd_hardware *hardware; |
1336 | ctl_cleanup(gcfg->g_ctl, LLDPD_CTL_SOCKET); | |
1280 | struct lldpd_vif *vif; | |
1281 | close(gcfg->g_ctl); | |
1282 | priv_ctl_cleanup(); | |
1337 | 1283 | TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) { |
1338 | 1284 | if (INTERFACE_OPENED(hardware)) |
1339 | 1285 | lldpd_iface_close(gcfg, hardware); |
1286 | } | |
1287 | TAILQ_FOREACH(vif, &gcfg->g_vif, vif_entries) { | |
1288 | if (vif->vif_raw != -1) | |
1289 | lldpd_iface_close(gcfg, (struct lldpd_hardware*)vif); | |
1340 | 1290 | } |
1341 | 1291 | #ifdef USE_SNMP |
1342 | 1292 | if (gcfg->g_snmp) |
1350 | 1300 | struct lldpd *cfg; |
1351 | 1301 | int ch, snmp = 0, debug = 0; |
1352 | 1302 | char *mgmtp = NULL; |
1353 | char *popt, opts[] = "dxm:p:@ "; | |
1354 | int probe = 0, i, found; | |
1303 | char *popt, opts[] = "vdxm:p:@ "; | |
1304 | int probe = 0, i, found, vlan = 0; | |
1355 | 1305 | |
1356 | 1306 | saved_argv = argv; |
1357 | 1307 | |
1366 | 1316 | *popt = '\0'; |
1367 | 1317 | while ((ch = getopt(argc, argv, opts)) != -1) { |
1368 | 1318 | switch (ch) { |
1319 | case 'v': | |
1320 | vlan = 1; | |
1321 | break; | |
1369 | 1322 | case 'd': |
1370 | 1323 | debug++; |
1371 | 1324 | break; |
1394 | 1347 | |
1395 | 1348 | log_init(debug); |
1396 | 1349 | |
1350 | if (!debug) { | |
1351 | int pid; | |
1352 | char *spid; | |
1353 | if (daemon(0, 0) != 0) | |
1354 | fatal("failed to detach daemon"); | |
1355 | if ((pid = open(LLDPD_PID_FILE, | |
1356 | O_TRUNC | O_CREAT | O_WRONLY)) == -1) | |
1357 | fatal("unable to open pid file " LLDPD_PID_FILE); | |
1358 | if (asprintf(&spid, "%d\n", getpid()) == -1) | |
1359 | fatal("unable to create pid file " LLDPD_PID_FILE); | |
1360 | if (write(pid, spid, strlen(spid)) == -1) | |
1361 | fatal("unable to write pid file " LLDPD_PID_FILE); | |
1362 | free(spid); | |
1363 | close(pid); | |
1364 | } | |
1365 | ||
1366 | priv_init(PRIVSEP_CHROOT); | |
1367 | ||
1397 | 1368 | if (probe == 0) probe = LLDPD_TTL; |
1398 | 1369 | |
1399 | 1370 | if ((cfg = (struct lldpd *) |
1400 | 1371 | calloc(1, sizeof(struct lldpd))) == NULL) |
1401 | 1372 | fatal(NULL); |
1402 | 1373 | |
1403 | if (mgmtp != NULL) | |
1404 | cfg->g_mgmt_pattern = mgmtp; | |
1374 | cfg->g_mgmt_pattern = mgmtp; | |
1375 | cfg->g_listen_vlans = vlan; | |
1405 | 1376 | |
1406 | 1377 | /* Get ioctl socket */ |
1407 | 1378 | if ((cfg->g_sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) |
1426 | 1397 | cfg->g_multi--; |
1427 | 1398 | |
1428 | 1399 | TAILQ_INIT(&cfg->g_hardware); |
1400 | TAILQ_INIT(&cfg->g_vif); | |
1429 | 1401 | |
1430 | 1402 | #ifdef USE_SNMP |
1431 | 1403 | if (snmp) { |
1435 | 1407 | #endif /* USE_SNMP */ |
1436 | 1408 | |
1437 | 1409 | /* Create socket */ |
1438 | if ((cfg->g_ctl = ctl_create(cfg, LLDPD_CTL_SOCKET)) == -1) | |
1439 | fatal("unable to create control socket " LLDPD_CTL_SOCKET); | |
1440 | ||
1441 | if (!debug && daemon(0, 0) != 0) { | |
1442 | ctl_cleanup(cfg->g_ctl, LLDPD_CTL_SOCKET); | |
1443 | fatal("failed to detach daemon"); | |
1444 | } | |
1410 | if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1) | |
1411 | fatalx("unable to create control socket " LLDPD_CTL_SOCKET); | |
1412 | TAILQ_INIT(&cfg->g_clients); | |
1413 | ||
1445 | 1414 | gcfg = cfg; |
1446 | 1415 | if (atexit(lldpd_exit) != 0) { |
1447 | ctl_cleanup(cfg->g_ctl, LLDPD_CTL_SOCKET); | |
1416 | close(cfg->g_ctl); | |
1417 | priv_ctl_cleanup(); | |
1448 | 1418 | fatal("unable to set exit function"); |
1449 | 1419 | } |
1450 | if (!debug) { | |
1451 | int pid; | |
1452 | char *spid; | |
1453 | if ((pid = open(LLDPD_PID_FILE, | |
1454 | O_TRUNC | O_CREAT | O_WRONLY)) == -1) | |
1455 | fatal("unable to open pid file " LLDPD_PID_FILE); | |
1456 | if (asprintf(&spid, "%d\n", getpid()) == -1) | |
1457 | fatal("unable to create pid file " LLDPD_PID_FILE); | |
1458 | if (write(pid, spid, strlen(spid)) == -1) | |
1459 | fatal("unable to write pid file " LLDPD_PID_FILE); | |
1460 | free(spid); | |
1461 | close(pid); | |
1462 | } | |
1463 | 1420 | |
1464 | 1421 | /* Signal handling */ |
1465 | signal(SIGHUP, lldpd_hangup); | |
1422 | signal(SIGHUP, lldpd_shutdown); | |
1466 | 1423 | signal(SIGINT, lldpd_shutdown); |
1467 | 1424 | signal(SIGTERM, lldpd_shutdown); |
1468 | 1425 |
32 | 32 | #endif |
33 | 33 | #include <net/ethernet.h> |
34 | 34 | #include <netinet/in.h> |
35 | #include <linux/ethtool.h> | |
35 | 36 | |
36 | 37 | #include "compat.h" |
37 | 38 | #include "lldp.h" |
110 | 111 | #define LLDPD_MODE_CDPV2 3 |
111 | 112 | #define LLDPD_MODE_SONMP 4 |
112 | 113 | #define LLDPD_MODE_EDP 5 |
114 | #define LLDPD_MODE_FDP 6 | |
113 | 115 | int h_mode; |
114 | 116 | |
115 | 117 | int h_flags; |
121 | 123 | u_int64_t h_rx_cnt; |
122 | 124 | u_int64_t h_rx_discarded_cnt; |
123 | 125 | u_int64_t h_rx_ageout_cnt; |
126 | u_int64_t h_rx_unrecognized_cnt; | |
124 | 127 | |
125 | 128 | u_int8_t *h_proto_macs; |
126 | 129 | time_t h_start_probe; |
135 | 138 | struct lldpd_frame *h_rlastframe; |
136 | 139 | struct lldpd_port *h_rport; |
137 | 140 | struct lldpd_chassis *h_rchassis; |
141 | }; | |
142 | ||
143 | /* lldpd_vif can be casted to lldpd_hardware on some cases */ | |
144 | struct lldpd_vif { | |
145 | TAILQ_ENTRY(lldpd_vif) vif_entries; | |
146 | int vif_raw; | |
147 | int vif_raw_real; /* Not used */ | |
148 | int vif_master; /* Not used */ | |
149 | int vif_mode; /* Not used */ | |
150 | int vif_flags; | |
151 | int vif_mtu; | |
152 | char vif_ifname[IFNAMSIZ]; | |
153 | ||
154 | /* No more compatibility with struct lldpd_hardware from here */ | |
155 | struct lldpd_hardware *vif_real; | |
138 | 156 | }; |
139 | 157 | |
140 | 158 | struct lldpd_interface { |
173 | 191 | struct protocol *g_protocols; |
174 | 192 | int g_multi; /* Set to 1 if multiple protocols */ |
175 | 193 | int g_probe_time; |
194 | int g_listen_vlans; | |
176 | 195 | |
177 | 196 | time_t g_lastsent; |
178 | 197 | int g_lastrid; |
189 | 208 | struct lldpd_chassis g_lchassis; |
190 | 209 | |
191 | 210 | TAILQ_HEAD(, lldpd_hardware) g_hardware; |
211 | TAILQ_HEAD(, lldpd_vif) g_vif; | |
192 | 212 | }; |
193 | 213 | |
194 | 214 | enum hmsg_type { |
228 | 248 | /* cdp.c */ |
229 | 249 | int cdpv1_send(PROTO_SEND_SIG); |
230 | 250 | int cdpv2_send(PROTO_SEND_SIG); |
251 | int fdp_send(PROTO_SEND_SIG); | |
231 | 252 | int cdp_decode(PROTO_DECODE_SIG); |
232 | 253 | int cdpv1_guess(PROTO_GUESS_SIG); |
233 | 254 | int cdpv2_guess(PROTO_GUESS_SIG); |
241 | 262 | int edp_decode(PROTO_DECODE_SIG); |
242 | 263 | |
243 | 264 | /* ctl.c */ |
244 | int ctl_create(struct lldpd *, char *); | |
265 | int ctl_create(char *); | |
245 | 266 | int ctl_connect(char *); |
246 | void ctl_cleanup(int, char *); | |
267 | void ctl_cleanup(char *); | |
247 | 268 | int ctl_accept(struct lldpd *, int); |
248 | 269 | int ctl_close(struct lldpd *, int); |
249 | 270 | void ctl_msg_init(struct hmsg *, enum hmsg_type); |
281 | 302 | void agent_shutdown(); |
282 | 303 | void agent_init(struct lldpd *, int); |
283 | 304 | |
305 | /* agent_priv.c */ | |
306 | void agent_priv_register_domain(); | |
307 | ||
284 | 308 | /* strlcpy.c */ |
285 | 309 | size_t strlcpy(char *, const char *, size_t); |
286 | 310 | |
288 | 312 | void iov_dump(struct lldpd_frame **, struct iovec *, int); |
289 | 313 | u_int16_t iov_checksum(struct iovec *, int, int); |
290 | 314 | |
315 | /* client.c */ | |
316 | struct client_handle { | |
317 | enum hmsg_type type; | |
318 | void (*handle)(struct lldpd*, struct hmsg*, struct hmsg*); | |
319 | }; | |
320 | ||
321 | void client_handle_client(struct lldpd *, struct lldpd_client *, | |
322 | char *, int); | |
323 | void client_handle_none(struct lldpd *, struct hmsg *, | |
324 | struct hmsg *); | |
325 | void client_handle_get_interfaces(struct lldpd *, struct hmsg *, | |
326 | struct hmsg *); | |
327 | void client_handle_get_port_related(struct lldpd *, struct hmsg *, | |
328 | struct hmsg *); | |
329 | void client_handle_shutdown(struct lldpd *, struct hmsg *, | |
330 | struct hmsg *); | |
331 | ||
332 | /* priv.c */ | |
333 | void priv_init(char*); | |
334 | int priv_ctl_create(); | |
335 | void priv_ctl_cleanup(); | |
336 | char *priv_gethostbyname(); | |
337 | int priv_open(char*); | |
338 | int priv_ethtool(char*, struct ethtool_cmd*); | |
339 | int priv_iface_init(struct lldpd_hardware *, int); | |
340 | int priv_iface_multicast(char *, u_int8_t *, int); | |
341 | int priv_snmp_socket(struct sockaddr_un *); | |
342 | ||
343 | /* privsep_fdpass.c */ | |
344 | int receive_fd(int); | |
345 | void send_fd(int, int); | |
346 | ||
291 | 347 | #endif /* _LLDPD_H */ |
0 | /* | |
1 | * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx> | |
2 | * | |
3 | * Permission to use, copy, modify, and distribute this software for any | |
4 | * purpose with or without fee is hereby granted, provided that the above | |
5 | * copyright notice and this permission notice appear in all copies. | |
6 | * | |
7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
10 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
12 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
13 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
14 | */ | |
15 | ||
16 | /* This file contains code for privilege separation. When an error arises in | |
17 | * monitor (which is running as root), it just stops instead of trying to | |
18 | * recover. This module also contains proxies to privileged operations. In this | |
19 | * case, error can be non fatal. */ | |
20 | ||
21 | #include "lldpd.h" | |
22 | ||
23 | #include <stdio.h> | |
24 | #include <unistd.h> | |
25 | #include <errno.h> | |
26 | #include <sys/types.h> | |
27 | #include <sys/wait.h> | |
28 | #include <sys/stat.h> | |
29 | #include <sys/types.h> | |
30 | #include <sys/socket.h> | |
31 | #include <sys/un.h> | |
32 | #include <regex.h> | |
33 | #include <fcntl.h> | |
34 | #include <pwd.h> | |
35 | #include <grp.h> | |
36 | #include <sys/utsname.h> | |
37 | #include <sys/ioctl.h> | |
38 | #include <netdb.h> | |
39 | #include <linux/sockios.h> | |
40 | #include <linux/if_packet.h> | |
41 | ||
42 | enum { | |
43 | PRIV_PING, | |
44 | PRIV_CREATE_CTL_SOCKET, | |
45 | PRIV_DELETE_CTL_SOCKET, | |
46 | PRIV_GET_HOSTNAME, | |
47 | PRIV_OPEN, | |
48 | PRIV_ETHTOOL, | |
49 | PRIV_IFACE_INIT, | |
50 | PRIV_IFACE_MULTICAST, | |
51 | PRIV_SNMP_SOCKET, | |
52 | }; | |
53 | ||
54 | static int may_read(int, void *, size_t); | |
55 | static void must_read(int, void *, size_t); | |
56 | static void must_write(int, void *, size_t); | |
57 | ||
58 | int remote; /* Other side */ | |
59 | int monitored = -1; /* Child */ | |
60 | int sock = -1; | |
61 | ||
62 | /* UID/GID of unprivileged user */ | |
63 | gid_t gid = 0; | |
64 | uid_t uid = 0; | |
65 | ||
66 | /* Proxies */ | |
67 | ||
68 | void | |
69 | priv_ping() | |
70 | { | |
71 | int cmd, rc; | |
72 | cmd = PRIV_PING; | |
73 | must_write(remote, &cmd, sizeof(int)); | |
74 | must_read(remote, &rc, sizeof(int)); | |
75 | LLOG_DEBUG("monitor ready"); | |
76 | } | |
77 | ||
78 | /* Proxy for ctl_create, no argument since this is the monitor that decides the | |
79 | * location of the socket */ | |
80 | int | |
81 | priv_ctl_create() | |
82 | { | |
83 | int cmd, rc; | |
84 | cmd = PRIV_CREATE_CTL_SOCKET; | |
85 | must_write(remote, &cmd, sizeof(int)); | |
86 | must_read(remote, &rc, sizeof(int)); | |
87 | if (rc == -1) | |
88 | return -1; | |
89 | return receive_fd(remote); | |
90 | } | |
91 | ||
92 | /* Proxy for ctl_cleanup */ | |
93 | void | |
94 | priv_ctl_cleanup() | |
95 | { | |
96 | int cmd, rc; | |
97 | cmd = PRIV_DELETE_CTL_SOCKET; | |
98 | must_write(remote, &cmd, sizeof(int)); | |
99 | must_read(remote, &rc, sizeof(int)); | |
100 | } | |
101 | ||
102 | /* Proxy for gethostbyname */ | |
103 | char * | |
104 | priv_gethostbyname() | |
105 | { | |
106 | int cmd, rc; | |
107 | static char *buf = NULL; | |
108 | cmd = PRIV_GET_HOSTNAME; | |
109 | must_write(remote, &cmd, sizeof(int)); | |
110 | must_read(remote, &rc, sizeof(int)); | |
111 | if ((buf = (char*)realloc(buf, rc+1)) == NULL) | |
112 | fatal(NULL); | |
113 | must_read(remote, buf, rc+1); | |
114 | return buf; | |
115 | } | |
116 | ||
117 | /* Proxy for open */ | |
118 | int | |
119 | priv_open(char *file) | |
120 | { | |
121 | int cmd, len, rc; | |
122 | cmd = PRIV_OPEN; | |
123 | must_write(remote, &cmd, sizeof(int)); | |
124 | len = strlen(file); | |
125 | must_write(remote, &len, sizeof(int)); | |
126 | must_write(remote, file, len + 1); | |
127 | must_read(remote, &rc, sizeof(int)); | |
128 | if (rc == -1) | |
129 | return rc; | |
130 | return receive_fd(remote); | |
131 | } | |
132 | ||
133 | /* Proxy for ethtool ioctl */ | |
134 | int | |
135 | priv_ethtool(char *ifname, struct ethtool_cmd *ethc) | |
136 | { | |
137 | int cmd, rc, len; | |
138 | cmd = PRIV_ETHTOOL; | |
139 | must_write(remote, &cmd, sizeof(int)); | |
140 | len = strlen(ifname); | |
141 | must_write(remote, &len, sizeof(int)); | |
142 | must_write(remote, ifname, len + 1); | |
143 | must_read(remote, &rc, sizeof(int)); | |
144 | if (rc != 0) | |
145 | return rc; | |
146 | must_read(remote, ethc, sizeof(struct ethtool_cmd)); | |
147 | return rc; | |
148 | } | |
149 | ||
150 | int | |
151 | priv_iface_init(struct lldpd_hardware *hardware, int master) | |
152 | { | |
153 | int cmd, rc; | |
154 | cmd = PRIV_IFACE_INIT; | |
155 | must_write(remote, &cmd, sizeof(int)); | |
156 | must_write(remote, &master, sizeof(int)); | |
157 | must_write(remote, hardware->h_ifname, IFNAMSIZ); | |
158 | must_read(remote, &rc, sizeof(int)); | |
159 | if (rc != 0) | |
160 | return rc; /* It's errno */ | |
161 | hardware->h_raw = receive_fd(remote); | |
162 | return 0; | |
163 | } | |
164 | ||
165 | int | |
166 | priv_iface_multicast(char *name, u_int8_t *mac, int add) | |
167 | { | |
168 | int cmd, rc; | |
169 | cmd = PRIV_IFACE_MULTICAST; | |
170 | must_write(remote, &cmd, sizeof(int)); | |
171 | must_write(remote, name, IFNAMSIZ); | |
172 | must_write(remote, mac, ETH_ALEN); | |
173 | must_write(remote, &add, sizeof(int)); | |
174 | must_read(remote, &rc, sizeof(int)); | |
175 | return rc; | |
176 | } | |
177 | ||
178 | int | |
179 | priv_snmp_socket(struct sockaddr_un *addr) | |
180 | { | |
181 | int cmd, rc; | |
182 | cmd = PRIV_SNMP_SOCKET; | |
183 | must_write(remote, &cmd, sizeof(int)); | |
184 | must_write(remote, addr, sizeof(struct sockaddr_un)); | |
185 | must_read(remote, &rc, sizeof(int)); | |
186 | if (rc < 0) | |
187 | return rc; | |
188 | return receive_fd(remote); | |
189 | } | |
190 | ||
191 | void | |
192 | asroot_ping() | |
193 | { | |
194 | int rc = 1; | |
195 | must_write(remote, &rc, sizeof(int)); | |
196 | } | |
197 | ||
198 | void | |
199 | asroot_ctl_create() | |
200 | { | |
201 | int rc; | |
202 | if ((rc = ctl_create(LLDPD_CTL_SOCKET)) == -1) { | |
203 | LLOG_WARN("[priv]: unable to create control socket"); | |
204 | must_write(remote, &rc, sizeof(int)); | |
205 | return; | |
206 | } | |
207 | if (chown(LLDPD_CTL_SOCKET, uid, gid) == -1) | |
208 | LLOG_WARN("[priv]: unable to chown control socket"); | |
209 | if (chmod(LLDPD_CTL_SOCKET, | |
210 | S_IRUSR | S_IWUSR | S_IXUSR | | |
211 | S_IRGRP | S_IWGRP | S_IXGRP) == -1) | |
212 | LLOG_WARN("[priv]: unable to chmod control socket"); | |
213 | must_write(remote, &rc, sizeof(int)); | |
214 | send_fd(remote, rc); | |
215 | close(rc); | |
216 | } | |
217 | ||
218 | void | |
219 | asroot_ctl_cleanup() | |
220 | { | |
221 | int rc = 0; | |
222 | ctl_cleanup(LLDPD_CTL_SOCKET); | |
223 | ||
224 | /* Ack */ | |
225 | must_write(remote, &rc, sizeof(int)); | |
226 | } | |
227 | ||
228 | void | |
229 | asroot_gethostbyname() | |
230 | { | |
231 | struct utsname un; | |
232 | struct hostent *hp; | |
233 | int len; | |
234 | if (uname(&un) != 0) | |
235 | fatal("[priv]: failed to get system information"); | |
236 | if ((hp = gethostbyname(un.nodename)) == NULL) | |
237 | fatal("[priv]: failed to get system name"); | |
238 | len = strlen(hp->h_name); | |
239 | must_write(remote, &len, sizeof(int)); | |
240 | must_write(remote, hp->h_name, strlen(hp->h_name) + 1); | |
241 | } | |
242 | ||
243 | void | |
244 | asroot_open() | |
245 | { | |
246 | const char* authorized[] = { | |
247 | "/proc/sys/net/ipv4/ip_forward", | |
248 | "/sys/class/net/[^.][^/]*/brforward", | |
249 | "/sys/class/net/[^.][^/]*/brport", | |
250 | NULL | |
251 | }; | |
252 | char **f; | |
253 | char *file; | |
254 | int fd, len, rc; | |
255 | regex_t preg; | |
256 | ||
257 | must_read(remote, &len, sizeof(len)); | |
258 | if ((file = (char *)malloc(len + 1)) == NULL) | |
259 | fatal(NULL); | |
260 | must_read(remote, file, len); | |
261 | file[len] = '\0'; | |
262 | ||
263 | for (f=authorized; *f != NULL; f++) { | |
264 | if (regcomp(&preg, *f, REG_NOSUB) != 0) | |
265 | /* Should not happen */ | |
266 | fatal("unable to compile a regex"); | |
267 | if (regexec(&preg, file, 0, NULL, 0) == 0) { | |
268 | regfree(&preg); | |
269 | break; | |
270 | } | |
271 | regfree(&preg); | |
272 | } | |
273 | if (*f == NULL) { | |
274 | LLOG_WARNX("[priv]: not authorized to open %s", file); | |
275 | rc = -1; | |
276 | must_write(remote, &rc, sizeof(int)); | |
277 | free(file); | |
278 | return; | |
279 | } | |
280 | if ((fd = open(file, 0)) == -1) { | |
281 | rc = -1; | |
282 | must_write(remote, &rc, sizeof(int)); | |
283 | free(file); | |
284 | return; | |
285 | } | |
286 | free(file); | |
287 | must_write(remote, &fd, sizeof(int)); | |
288 | send_fd(remote, fd); | |
289 | close(fd); | |
290 | } | |
291 | ||
292 | void | |
293 | asroot_ethtool() | |
294 | { | |
295 | struct ifreq ifr; | |
296 | struct ethtool_cmd ethc; | |
297 | int len, rc; | |
298 | char *ifname; | |
299 | ||
300 | memset(&ifr, 0, sizeof(ifr)); | |
301 | memset(ðc, 0, sizeof(ethc)); | |
302 | must_read(remote, &len, sizeof(int)); | |
303 | if ((ifname = (char*)malloc(len + 1)) == NULL) | |
304 | fatal(NULL); | |
305 | must_read(remote, ifname, len); | |
306 | ifname[len] = '\0'; | |
307 | strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); | |
308 | free(ifname); | |
309 | ifr.ifr_data = (caddr_t)ðc; | |
310 | ethc.cmd = ETHTOOL_GSET; | |
311 | if ((rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) { | |
312 | LLOG_DEBUG("[priv]: unable to ioctl ETHTOOL for %s", | |
313 | ifr.ifr_name); | |
314 | must_write(remote, &rc, sizeof(int)); | |
315 | close(sock); | |
316 | return; | |
317 | } | |
318 | must_write(remote, &rc, sizeof(int)); | |
319 | must_write(remote, ðc, sizeof(struct ethtool_cmd)); | |
320 | } | |
321 | ||
322 | void | |
323 | asroot_iface_init() | |
324 | { | |
325 | struct sockaddr_ll sa; | |
326 | int un = 1; | |
327 | int s, master; | |
328 | char ifname[IFNAMSIZ]; | |
329 | ||
330 | must_read(remote, &master, sizeof(int)); | |
331 | must_read(remote, ifname, IFNAMSIZ); | |
332 | ifname[IFNAMSIZ-1] = '\0'; | |
333 | ||
334 | /* Open listening socket to receive/send frames */ | |
335 | if ((s = socket(PF_PACKET, SOCK_RAW, | |
336 | htons(ETH_P_ALL))) < 0) { | |
337 | must_write(remote, &errno, sizeof(errno)); | |
338 | return; | |
339 | } | |
340 | memset(&sa, 0, sizeof(sa)); | |
341 | sa.sll_family = AF_PACKET; | |
342 | sa.sll_protocol = 0; | |
343 | if (master == -1) | |
344 | sa.sll_ifindex = if_nametoindex(ifname); | |
345 | else | |
346 | sa.sll_ifindex = master; | |
347 | if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) { | |
348 | must_write(remote, &errno, sizeof(errno)); | |
349 | close(s); | |
350 | return; | |
351 | } | |
352 | ||
353 | if (master != -1) { | |
354 | /* With bonding, we need to listen to bond device. We use | |
355 | * setsockopt() PACKET_ORIGDEV to get physical device instead of | |
356 | * bond device */ | |
357 | if (setsockopt(s, SOL_PACKET, | |
358 | PACKET_ORIGDEV, &un, sizeof(un)) == -1) { | |
359 | LLOG_WARN("[priv]: unable to setsockopt for master bonding device of %s. " | |
360 | "You will get inaccurate results", | |
361 | ifname); | |
362 | } | |
363 | } | |
364 | errno = 0; | |
365 | must_write(remote, &errno, sizeof(errno)); | |
366 | send_fd(remote, s); | |
367 | close(s); | |
368 | } | |
369 | ||
370 | void | |
371 | asroot_iface_multicast() | |
372 | { | |
373 | int add, rc = 0; | |
374 | struct ifreq ifr; | |
375 | memset(&ifr, 0, sizeof(ifr)); | |
376 | must_read(remote, ifr.ifr_name, IFNAMSIZ); | |
377 | ifr.ifr_name[IFNAMSIZ-1] = '\0'; | |
378 | must_read(remote, ifr.ifr_hwaddr.sa_data, ETH_ALEN); | |
379 | must_read(remote, &add, sizeof(int)); | |
380 | ||
381 | if (ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI, | |
382 | &ifr) < 0) { | |
383 | must_write(remote, &errno, sizeof(errno)); | |
384 | return; | |
385 | } | |
386 | ||
387 | must_write(remote, &rc, sizeof(rc)); | |
388 | } | |
389 | ||
390 | static void | |
391 | asroot_snmp_socket() | |
392 | { | |
393 | int sock, rc; | |
394 | static struct sockaddr_un *addr = NULL; | |
395 | struct sockaddr_un bogus; | |
396 | ||
397 | if (!addr) { | |
398 | addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un)); | |
399 | must_read(remote, addr, sizeof(struct sockaddr_un)); | |
400 | } else | |
401 | /* We have already been asked to connect to a socket. We will | |
402 | * connect to the same socket. */ | |
403 | must_read(remote, &bogus, sizeof(struct sockaddr_un)); | |
404 | if (addr->sun_family != AF_UNIX) | |
405 | fatal("someone is trying to trick me"); | |
406 | addr->sun_path[sizeof(addr->sun_path)-1] = '\0'; | |
407 | ||
408 | if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { | |
409 | LLOG_WARN("[priv]: cannot open socket"); | |
410 | must_write(remote, &sock, sizeof(int)); | |
411 | return; | |
412 | } | |
413 | if ((rc = connect(sock, (struct sockaddr *) addr, | |
414 | sizeof(struct sockaddr_un))) != 0) { | |
415 | LLOG_INFO("[priv]: cannot connect to %s", addr->sun_path); | |
416 | close(sock); | |
417 | rc = -1; | |
418 | must_write(remote, &rc, sizeof(int)); | |
419 | return; | |
420 | } | |
421 | must_write(remote, &rc, sizeof(int)); | |
422 | send_fd(remote, sock); | |
423 | close(sock); | |
424 | } | |
425 | ||
426 | struct dispatch_actions { | |
427 | int msg; | |
428 | void(*function)(void); | |
429 | }; | |
430 | ||
431 | struct dispatch_actions actions[] = { | |
432 | {PRIV_PING, asroot_ping}, | |
433 | {PRIV_CREATE_CTL_SOCKET, asroot_ctl_create}, | |
434 | {PRIV_DELETE_CTL_SOCKET, asroot_ctl_cleanup}, | |
435 | {PRIV_GET_HOSTNAME, asroot_gethostbyname}, | |
436 | {PRIV_OPEN, asroot_open}, | |
437 | {PRIV_ETHTOOL, asroot_ethtool}, | |
438 | {PRIV_IFACE_INIT, asroot_iface_init}, | |
439 | {PRIV_IFACE_MULTICAST, asroot_iface_multicast}, | |
440 | {PRIV_SNMP_SOCKET, asroot_snmp_socket}, | |
441 | {-1, NULL} | |
442 | }; | |
443 | ||
444 | /* Main loop, run as root */ | |
445 | void | |
446 | priv_loop() | |
447 | { | |
448 | int cmd; | |
449 | struct dispatch_actions *a; | |
450 | ||
451 | while (!may_read(remote, &cmd, sizeof(int))) { | |
452 | for (a = actions; a->function != NULL; a++) { | |
453 | if (cmd == a->msg) { | |
454 | a->function(); | |
455 | break; | |
456 | } | |
457 | } | |
458 | if (a->function == NULL) | |
459 | fatal("[priv]: bogus message received"); | |
460 | } | |
461 | /* Should never be there */ | |
462 | } | |
463 | ||
464 | void | |
465 | priv_exit() | |
466 | { | |
467 | int status; | |
468 | int rc; | |
469 | if ((rc = waitpid(monitored, &status, WNOHANG)) == 0) { | |
470 | LLOG_DEBUG("[priv]: killing child"); | |
471 | kill(monitored, SIGTERM); | |
472 | } | |
473 | if ((rc = waitpid(monitored, &status, WNOHANG)) == -1) | |
474 | _exit(0); | |
475 | LLOG_DEBUG("[priv]: waiting for child %d to terminate", monitored); | |
476 | } | |
477 | ||
478 | /* If priv parent gets a TERM or HUP, pass it through to child instead */ | |
479 | static void | |
480 | sig_pass_to_chld(int sig) | |
481 | { | |
482 | int oerrno = errno; | |
483 | if (monitored != -1) | |
484 | kill(monitored, sig); | |
485 | errno = oerrno; | |
486 | } | |
487 | ||
488 | /* if parent gets a SIGCHLD, it will exit */ | |
489 | static void | |
490 | sig_chld(int sig) | |
491 | { | |
492 | LLOG_DEBUG("[priv]: received signal %d, exiting", sig); | |
493 | priv_exit(); | |
494 | } | |
495 | ||
496 | /* Initialization */ | |
497 | void | |
498 | priv_init(char *chrootdir) | |
499 | { | |
500 | int pair[2]; | |
501 | struct passwd *user; | |
502 | struct group *group; | |
503 | gid_t gidset[1]; | |
504 | ||
505 | /* Create socket pair */ | |
506 | if (socketpair(AF_LOCAL, SOCK_DGRAM, PF_UNSPEC, pair) < 0) | |
507 | fatal("[priv]: unable to create socket pair for privilege separation"); | |
508 | ||
509 | /* Get users */ | |
510 | if ((user = getpwnam(PRIVSEP_USER)) == NULL) | |
511 | fatal("[priv]: no " PRIVSEP_USER " user for privilege separation"); | |
512 | uid = user->pw_uid; | |
513 | if ((group = getgrnam(PRIVSEP_GROUP)) == NULL) | |
514 | fatal("[priv]: no " PRIVSEP_GROUP " group for privilege separation"); | |
515 | gid = group->gr_gid; | |
516 | ||
517 | /* Spawn off monitor */ | |
518 | if ((monitored = fork()) < 0) | |
519 | fatal("[priv]: unable to fork monitor"); | |
520 | switch (monitored) { | |
521 | case 0: | |
522 | /* We are in the children, drop privileges */ | |
523 | if (chroot(chrootdir) == -1) | |
524 | fatal("[priv]: unable to chroot"); | |
525 | if (chdir("/") != 0) | |
526 | fatal("[priv]: unable to chdir"); | |
527 | gidset[0] = gid; | |
528 | if (setresgid(gid, gid, gid) == -1) | |
529 | fatal("[priv]: setresgid() failed"); | |
530 | if (setgroups(1, gidset) == -1) | |
531 | fatal("[priv]: setgroups() failed"); | |
532 | if (setresuid(uid, uid, uid) == -1) | |
533 | fatal("[priv]: setresuid() failed"); | |
534 | remote = pair[0]; | |
535 | close(pair[1]); | |
536 | priv_ping(); | |
537 | break; | |
538 | default: | |
539 | /* We are in the monitor */ | |
540 | remote = pair[1]; | |
541 | close(pair[0]); | |
542 | if (atexit(priv_exit) != 0) | |
543 | fatal("[priv]: unable to set exit function"); | |
544 | if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { | |
545 | fatal("[priv]: unable to get a socket"); | |
546 | } | |
547 | ||
548 | signal(SIGALRM, sig_pass_to_chld); | |
549 | signal(SIGTERM, sig_pass_to_chld); | |
550 | signal(SIGHUP, sig_pass_to_chld); | |
551 | signal(SIGINT, sig_pass_to_chld); | |
552 | signal(SIGQUIT, sig_pass_to_chld); | |
553 | signal(SIGCHLD, sig_chld); | |
554 | priv_loop(); | |
555 | exit(0); | |
556 | } | |
557 | } | |
558 | ||
559 | /* Stolen from sbin/pflogd/privsep.c from OpenBSD */ | |
560 | /* | |
561 | * Copyright (c) 2003 Can Erkin Acar | |
562 | * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org> | |
563 | * | |
564 | * Permission to use, copy, modify, and distribute this software for any | |
565 | * purpose with or without fee is hereby granted, provided that the above | |
566 | * copyright notice and this permission notice appear in all copies. | |
567 | * | |
568 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
569 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
570 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
571 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
572 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
573 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
574 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
575 | */ | |
576 | ||
577 | /* Read all data or return 1 for error. */ | |
578 | static int | |
579 | may_read(int fd, void *buf, size_t n) | |
580 | { | |
581 | char *s = buf; | |
582 | ssize_t res, pos = 0; | |
583 | ||
584 | while (n > pos) { | |
585 | res = read(fd, s + pos, n - pos); | |
586 | switch (res) { | |
587 | case -1: | |
588 | if (errno == EINTR || errno == EAGAIN) | |
589 | continue; | |
590 | case 0: | |
591 | return (1); | |
592 | default: | |
593 | pos += res; | |
594 | } | |
595 | } | |
596 | return (0); | |
597 | } | |
598 | ||
599 | /* Read data with the assertion that it all must come through, or | |
600 | * else abort the process. Based on atomicio() from openssh. */ | |
601 | static void | |
602 | must_read(int fd, void *buf, size_t n) | |
603 | { | |
604 | char *s = buf; | |
605 | ssize_t res, pos = 0; | |
606 | ||
607 | while (n > pos) { | |
608 | res = read(fd, s + pos, n - pos); | |
609 | switch (res) { | |
610 | case -1: | |
611 | if (errno == EINTR || errno == EAGAIN) | |
612 | continue; | |
613 | case 0: | |
614 | _exit(0); | |
615 | default: | |
616 | pos += res; | |
617 | } | |
618 | } | |
619 | } | |
620 | ||
621 | /* Write data with the assertion that it all has to be written, or | |
622 | * else abort the process. Based on atomicio() from openssh. */ | |
623 | static void | |
624 | must_write(int fd, void *buf, size_t n) | |
625 | { | |
626 | char *s = buf; | |
627 | ssize_t res, pos = 0; | |
628 | ||
629 | while (n > pos) { | |
630 | res = write(fd, s + pos, n - pos); | |
631 | switch (res) { | |
632 | case -1: | |
633 | if (errno == EINTR || errno == EAGAIN) | |
634 | continue; | |
635 | case 0: | |
636 | _exit(0); | |
637 | default: | |
638 | pos += res; | |
639 | } | |
640 | } | |
641 | } |
0 | /* | |
1 | * Copyright 2001 Niels Provos <provos@citi.umich.edu> | |
2 | * All rights reserved. | |
3 | * | |
4 | * Copyright (c) 2002 Matthieu Herrb | |
5 | * All rights reserved. | |
6 | * | |
7 | * Redistribution and use in source and binary forms, with or without | |
8 | * modification, are permitted provided that the following conditions | |
9 | * are met: | |
10 | * | |
11 | * - Redistributions of source code must retain the above copyright | |
12 | * notice, this list of conditions and the following disclaimer. | |
13 | * - Redistributions in binary form must reproduce the above | |
14 | * copyright notice, this list of conditions and the following | |
15 | * disclaimer in the documentation and/or other materials provided | |
16 | * with the distribution. | |
17 | * | |
18 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
19 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
20 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
21 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
22 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
23 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
24 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
25 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
26 | * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
28 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
29 | * POSSIBILITY OF SUCH DAMAGE. | |
30 | */ | |
31 | ||
32 | #include "lldpd.h" | |
33 | ||
34 | #include <sys/param.h> | |
35 | #include <sys/uio.h> | |
36 | #include <sys/types.h> | |
37 | #include <sys/socket.h> | |
38 | #include <sys/stat.h> | |
39 | #include <err.h> | |
40 | #include <errno.h> | |
41 | #include <fcntl.h> | |
42 | #include <signal.h> | |
43 | #include <stdio.h> | |
44 | #include <stdlib.h> | |
45 | #include <string.h> | |
46 | #include <unistd.h> | |
47 | ||
48 | void | |
49 | send_fd(int sock, int fd) | |
50 | { | |
51 | struct msghdr msg; | |
52 | union { | |
53 | struct cmsghdr hdr; | |
54 | char buf[CMSG_SPACE(sizeof(int))]; | |
55 | } cmsgbuf; | |
56 | struct cmsghdr *cmsg; | |
57 | struct iovec vec; | |
58 | int result = 0; | |
59 | ssize_t n; | |
60 | ||
61 | memset(&msg, 0, sizeof(msg)); | |
62 | ||
63 | if (fd >= 0) { | |
64 | msg.msg_control = (caddr_t)&cmsgbuf.buf; | |
65 | msg.msg_controllen = sizeof(cmsgbuf.buf); | |
66 | cmsg = CMSG_FIRSTHDR(&msg); | |
67 | cmsg->cmsg_len = CMSG_LEN(sizeof(int)); | |
68 | cmsg->cmsg_level = SOL_SOCKET; | |
69 | cmsg->cmsg_type = SCM_RIGHTS; | |
70 | *(int *)CMSG_DATA(cmsg) = fd; | |
71 | } else { | |
72 | result = errno; | |
73 | } | |
74 | ||
75 | vec.iov_base = &result; | |
76 | vec.iov_len = sizeof(int); | |
77 | msg.msg_iov = &vec; | |
78 | msg.msg_iovlen = 1; | |
79 | ||
80 | if ((n = sendmsg(sock, &msg, 0)) == -1) | |
81 | LLOG_WARN("sendmsg(%d)", sock); | |
82 | if (n != sizeof(int)) | |
83 | LLOG_WARNX("sendmsg: expected sent 1 got %ld", | |
84 | (long)n); | |
85 | } | |
86 | ||
87 | int | |
88 | receive_fd(int sock) | |
89 | { | |
90 | struct msghdr msg; | |
91 | union { | |
92 | struct cmsghdr hdr; | |
93 | char buf[CMSG_SPACE(sizeof(int))]; | |
94 | } cmsgbuf; | |
95 | struct cmsghdr *cmsg; | |
96 | struct iovec vec; | |
97 | ssize_t n; | |
98 | int result; | |
99 | int fd; | |
100 | ||
101 | memset(&msg, 0, sizeof(msg)); | |
102 | vec.iov_base = &result; | |
103 | vec.iov_len = sizeof(int); | |
104 | msg.msg_iov = &vec; | |
105 | msg.msg_iovlen = 1; | |
106 | msg.msg_control = &cmsgbuf.buf; | |
107 | msg.msg_controllen = sizeof(cmsgbuf.buf); | |
108 | ||
109 | if ((n = recvmsg(sock, &msg, 0)) == -1) | |
110 | LLOG_WARN("recvmsg"); | |
111 | if (n != sizeof(int)) | |
112 | LLOG_WARNX("recvmsg: expected received 1 got %ld", | |
113 | (long)n); | |
114 | if (result == 0) { | |
115 | cmsg = CMSG_FIRSTHDR(&msg); | |
116 | if (cmsg == NULL) { | |
117 | LLOG_WARNX("no message header"); | |
118 | return -1; | |
119 | } | |
120 | if (cmsg->cmsg_type != SCM_RIGHTS) | |
121 | LLOG_WARNX("expected type %d got %d", | |
122 | SCM_RIGHTS, cmsg->cmsg_type); | |
123 | fd = (*(int *)CMSG_DATA(cmsg)); | |
124 | return fd; | |
125 | } else { | |
126 | errno = result; | |
127 | return -1; | |
128 | } | |
129 | } |