diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/prefix.h | 52 | ||||
-rw-r--r-- | lib/qafi_safi.h | 5 | ||||
-rw-r--r-- | lib/qfstring.c | 234 | ||||
-rw-r--r-- | lib/qfstring.h | 11 | ||||
-rw-r--r-- | lib/smux.c | 9 | ||||
-rw-r--r-- | lib/sockopt.c | 1145 | ||||
-rw-r--r-- | lib/sockopt.h | 41 | ||||
-rw-r--r-- | lib/sockunion.c | 403 | ||||
-rw-r--r-- | lib/sockunion.h | 47 | ||||
-rw-r--r-- | lib/thread.c | 8 | ||||
-rw-r--r-- | lib/vty_io.c | 1 | ||||
-rw-r--r-- | lib/vty_io_term.c | 38 |
12 files changed, 1349 insertions, 645 deletions
diff --git a/lib/prefix.h b/lib/prefix.h index bc44244c..7944fd69 100644 --- a/lib/prefix.h +++ b/lib/prefix.h @@ -40,8 +40,8 @@ typedef const union sockunion* const_sockunion ; /* IPv4 and IPv6 unified prefix structure. */ struct prefix { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; union { u_char prefix; @@ -61,36 +61,66 @@ struct prefix /* IPv4 prefix structure. */ struct prefix_ipv4 { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; struct in_addr prefix __attribute__ ((aligned (8))); }; +CONFIRM(offsetof(struct prefix_ipv4, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_ipv4, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_ipv4, prefix) + == offsetof(struct prefix, u.prefix4)) ; +CONFIRM(sizeof(struct prefix_ipv4) <= sizeof(struct prefix)) ; /* IPv6 prefix structure. */ #ifdef HAVE_IPV6 struct prefix_ipv6 { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; struct in6_addr prefix __attribute__ ((aligned (8))); }; +CONFIRM(offsetof(struct prefix_ipv6, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_ipv6, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_ipv6, prefix) + == offsetof(struct prefix, u.prefix6)) ; +CONFIRM(sizeof(struct prefix_ipv6) <= sizeof(struct prefix)) ; #endif /* HAVE_IPV6 */ struct prefix_ls { - u_char family; - u_char prefixlen; + sa_family_t family; + u_char prefixlen; struct in_addr id __attribute__ ((aligned (8))); struct in_addr adv_router; }; +CONFIRM(offsetof(struct prefix_ls, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_ls, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_ls, id) + == offsetof(struct prefix, u.lp.id)) ; +CONFIRM(offsetof(struct prefix_ls, adv_router) + == offsetof(struct prefix, u.lp.adv_router)) ; +CONFIRM(sizeof(struct prefix_ls) <= sizeof(struct prefix)) ; /* Prefix for routing distinguisher. */ struct prefix_rd { - u_char family; - u_char prefixlen; - u_char val[8] __attribute__ ((aligned (8))); + sa_family_t family; + u_char prefixlen; + u_char val[8] __attribute__ ((aligned (8))); }; +CONFIRM(offsetof(struct prefix_rd, family) + == offsetof(struct prefix, family)) ; +CONFIRM(offsetof(struct prefix_rd, prefixlen) + == offsetof(struct prefix, prefixlen)) ; +CONFIRM(offsetof(struct prefix_rd, val) + == offsetof(struct prefix, u.val)) ; +CONFIRM(sizeof(struct prefix_rd) <= sizeof(struct prefix)) ; #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 diff --git a/lib/qafi_safi.h b/lib/qafi_safi.h index c93b741e..5559097d 100644 --- a/lib/qafi_safi.h +++ b/lib/qafi_safi.h @@ -139,11 +139,6 @@ typedef struct qAFI_SAFI qAFI_SAFI_t ; typedef struct qAFI_SAFI* qAFI_SAFI ; /*============================================================================== - * POSIX address family type - */ -typedef int pAF_t ; - -/*============================================================================== * Quagga AFI/SAFI values -- original macro definitions */ diff --git a/lib/qfstring.c b/lib/qfstring.c index 9f2e2e9c..eda2aa9a 100644 --- a/lib/qfstring.c +++ b/lib/qfstring.c @@ -20,6 +20,8 @@ */ #include "misc.h" +#include <stdio.h> + #include "qfstring.h" /*============================================================================== @@ -397,7 +399,9 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags, * pf_plus -- requires '+' or '-' * pf_space -- requires space or '-' * pf_zeros -- zero fill to width - * pf_alt -- add '0x' or '0X' if hex (no effect on decimal) + * pf_alt -- add '0x' or '0X' if hex -- depending on pf_uc + * add '0' if octal and not zero. + * no effect otherwise * * pf_precision -- explicit precision (needed if precision == 0) * @@ -430,7 +434,11 @@ qfs_pointer(qf_str qfs, void* p_val, enum pf_flags flags, * * * pf_unsigned or sign == 0 takes precedence over pf_plus and pf_space. * - * For hex output, pf_commas groups digits in 4's, separated by '_'. + * For decimal output, pf_commas groups digits in 3's, separated by ','. + * For hex output, pf_commas groups digits in 4's, separated by '_'. + * For oct output, pf_commas is ignored. + * + * Note that pf_commas is a glibc extension, which does not apply to hex ! * * For hex output if precision is: * @@ -521,6 +529,9 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, if ((flags & pf_precision) || (width <= 0)) flags &= ~pf_zeros ; /* turn off zero fill */ + if (flags & pf_oct) + flags &= ~pf_commas ; /* turn off commas */ + /* Set up any required sign and radix prefix */ if ((flags & pf_unsigned) || (sign == 0)) { @@ -548,15 +559,22 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, sign_len = 0 ; } ; - if ((flags & (pf_hex | pf_alt)) == (pf_hex | pf_alt)) - { - radix_str = (flags & pf_uc) ? "0X" : "0x" ; - radix_len = 2 ; - } - else + radix_str = "" ; + radix_len = 0 ; + + if (flags & pf_alt) { - radix_str = "" ; - radix_len = 0 ; + if (flags & pf_hex) + { + confirm(pf_uc != 0) ; + radix_str = (flags & pf_uc) ? "0X" : "0x" ; + radix_len = 2 ; + } + else if ((flags & pf_oct) && (val != 0)) + { + radix_str = "0" ; + radix_len = 1 ; + } ; } ; /* Turn off zero fill if left justify (width < 0) */ @@ -574,8 +592,14 @@ qfs_number(qf_str qfs, uintmax_t val, int sign, enum pf_flags flags, } ; /* Start with the basic digit conversion. */ - base = (flags & pf_hex) ? 16 : 10 ; + base = 10 ; + if (flags & pf_hex) + base = 16 ; + else if (flags & pf_oct) + base = 8 ; + digits = (flags & pf_uc) ? uc : lc ; + confirm(pf_uc != 0) ; e = p = num + sizeof(num) ; v = val ; @@ -734,12 +758,15 @@ enum pf_phase pfp_flags, pfp_width, pfp_precision, - pfp_num_type, + pfp_int_type, + pfp_float_type, pfp_done, pfp_failed } ; +CONFIRM(pfp_float_type > pfp_int_type) ; + /* Number types for printing */ enum arg_num_type { @@ -750,7 +777,8 @@ enum arg_num_type ant_long_long, /* ll */ ant_intmax_t, /* j */ ant_size_t, /* z */ - ant_ptr_t, /* %p */ + ant_ptr_t, /* void* */ + ant_long_double, /* L for float */ ant_default = ant_int, }; @@ -759,14 +787,16 @@ static enum pf_phase qfs_arg_string(qf_str qfs, const char* src, enum pf_flags flags, int width, int precision) ; static enum pf_phase qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) ; -static enum pf_phase qfs_arg_number(qf_str qfs, va_list* p_va, +static enum pf_phase qfs_arg_integer(qf_str qfs, va_list* p_va, enum pf_flags flags, int width, int precision, enum arg_num_type ant) ; +static enum pf_phase qfs_arg_float(qf_str qfs, va_list* p_va, + const char* start, size_t flen, enum arg_num_type ant) ; /*------------------------------------------------------------------------------ * Formatted print to qf_str -- cf printf() -- appends to the qf_str. * - * This operation is async-signal-safe. Takes into account the offset, and - * adds up any overflow. + * This operation is async-signal-safe -- EXCEPT for floating point values. + * Takes into account the offset, and adds up any overflow. * * Returns: the resulting length of the qf_str. */ @@ -786,8 +816,8 @@ qfs_printf(qf_str qfs, const char* format, ...) /*------------------------------------------------------------------------------ * Formatted print to qf_str -- cf vprintf() -- appends to the qf_str. * - * This operation is async-signal-safe. Takes into account the offset, and - * adds up any overflow + * This operation is async-signal-safe -- EXCEPT for floating point values. + * Takes into account the offset, and adds up any overflow. * * Operates on a copy of the va_list -- so the original is *unchanged*. * @@ -851,6 +881,11 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; break ; + case '#': + flags |= pf_alt ; + phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; + break ; + case ' ': flags |= pf_space ; phase = (phase <= pfp_flags) ? pfp_flags : pfp_failed ; @@ -909,13 +944,13 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case '.': - phase = (phase <= pfp_precision) ? pfp_precision : pfp_failed; + phase = (phase < pfp_precision) ? pfp_precision : pfp_failed ; flags |= pf_precision ; precision = 0 ; break ; case 'l': /* 1 or 2 'l', not 'h', 'j' or 'z' */ - phase = pfp_num_type ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; if (ant == ant_default) ant = ant_long ; else if (ant == ant_long) @@ -925,7 +960,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case 'h': /* 1 or 2 'h', not 'l', 'j' or 'z' */ - phase = pfp_num_type ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; if (ant == ant_default) ant = ant_short ; else if (ant == ant_short) @@ -935,17 +970,22 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case 'j': /* 1 'j', not 'h', 'l' or 'z' */ - phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; ant = ant_intmax_t ; break ; case 'z': /* 1 'z', not 'h', 'l' or 'j' */ - phase = (phase <= pfp_num_type) ? pfp_num_type : pfp_failed ; + phase = (phase <= pfp_int_type) ? pfp_int_type : pfp_failed ; ant = ant_size_t ; break ; + case 'L': /* 1 'L', not for integers ! */ + phase = (phase < pfp_int_type) ? pfp_float_type : pfp_failed ; + ant = ant_long_double ; + break ; + case 's': - if (phase == pfp_num_type) + if (phase == pfp_int_type) phase = pfp_failed ; /* don't do 'l' etc. */ else phase = qfs_arg_string(qfs, va_arg(vac, char*), @@ -953,7 +993,7 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) break ; case 'c': - if (phase == pfp_num_type) + if (phase == pfp_int_type) phase = pfp_failed ; /* don't do 'l' etc. */ else phase = qfs_arg_char(qfs, (char)va_arg(vac, int), @@ -962,33 +1002,53 @@ qfs_vprintf(qf_str qfs, const char *format, va_list va) case 'd': case 'i': - phase = qfs_arg_number(qfs, &vac, flags, width, precision, + phase = qfs_arg_integer(qfs, &vac, flags, width, precision, ant) ; break ; case 'u': - phase = qfs_arg_number(qfs, &vac, flags | pf_unsigned, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_unsigned, width, + precision, ant) ; + break ; + + case 'o': + phase = qfs_arg_integer(qfs, &vac, flags | pf_oct, width, precision, ant) ; break ; case 'x': - phase = qfs_arg_number(qfs, &vac, flags | pf_hex_x, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_hex_x, width, precision, ant) ; break ; case 'X': - phase = qfs_arg_number(qfs, &vac, flags | pf_hex_X, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_hex_X, width, precision, ant) ; break ; case 'p': - if (phase == pfp_num_type) + if (phase == pfp_int_type) phase = pfp_failed ; else - phase = qfs_arg_number(qfs, &vac, flags | pf_void_p, width, + phase = qfs_arg_integer(qfs, &vac, flags | pf_void_p, width, precision, ant_ptr_t) ; break ; + case 'e': + case 'E': + case 'f': + case 'F': + case 'g': + case 'G': + case 'a': + case 'A': + if (phase == pfp_int_type) + phase = pfp_failed ; + else + phase = qfs_arg_float(qfs, &vac, start, format - start, + ant) ; + break ; + default: /* unrecognised format */ phase = pfp_failed ; break ; @@ -1089,14 +1149,16 @@ qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) } ; /*------------------------------------------------------------------------------ - * %d, %i, %u, %x, %X and %p handler + * %d, %i, %u, %o, %x, %X and %p handler * - * Accepts: pf_commas -- format with commas + * Accepts: pf_commas -- format with commas or '_' for hex (non-standard) + * ignored for octal. * pf_minus -- left justify (any width will be -ve) * pf_plus -- requires sign * pf_space -- requires space or '-' * pf_zeros -- zero fill to width * pf_alt -- '0x' or '0X' for hex + * '0' for octal * * pf_precision -- precision specified * @@ -1107,16 +1169,24 @@ qfs_arg_char(qf_str qfs, char ch, enum pf_flags flags, int width, int precision) * * and: all the number argument types. * + * Rejects: ant == ant_long_double -- which is how the parser spots an + * erroneous %Ld for example. + * * This operation is async-signal-safe. Takes into account the offset, and * adds up any overflow */ static enum pf_phase -qfs_arg_number(qf_str qfs, va_list* p_va, enum pf_flags flags, +qfs_arg_integer(qf_str qfs, va_list* p_va, enum pf_flags flags, int width, int precision, enum arg_num_type ant) { uintmax_t u_val ; intmax_t s_val ; + /* Reject if seen an 'L' + */ + if (ant == ant_long_double) + return pfp_failed ; + /* Special for hex with '0... if no explicit precision, set -1 for byte * and -2 for everything else -- see qfs_number(). */ @@ -1208,3 +1278,99 @@ qfs_arg_number(qf_str qfs, va_list* p_va, enum pf_flags flags, return pfp_done ; } ; +/*------------------------------------------------------------------------------ + * %e, %E, %f, %F, %g, %G, %a and %A handler + * + * This uses the standard library sprintf() to do the business, so this is + * NOT async-signal-safe. This means that we get the full precision supported + * by the system ! Attempting to construct async-signal-safe conversion is + * doomed to failure, because any floating point operation may affect flags + * and other state in the processor :-( + * + * This operation is *NOT* async-signal-safe. Takes into account the offset, + * and adds up any overflow + */ + +union float_value +{ + double d ; + long double ld ; +} ; + +static int +qfs_arg_float_snprintf(void* buf, int have, const char* format, + union float_value* p_val, enum arg_num_type ant) +{ + if (ant == ant_default) + return snprintf(buf, have, format, p_val->d) ; + else + return snprintf(buf, have, format, p_val->ld) ; +} ; + +static enum pf_phase +qfs_arg_float(qf_str qfs, va_list* p_va, const char* start, size_t flen, + enum arg_num_type ant) +{ + union float_value val ; + char format[flen + 1] ; + int want ; + + if (ant == ant_default) + val.d = va_arg(*p_va, double) ; + else + val.ld = va_arg(*p_va, long double) ; + + memcpy(format, start, flen) ; + format[flen + 1] = '\0' ; + + if (qfs->offset == 0) + { + /* No offset, so can use the qfs directly. + */ + int have ; + + have = qfs_left(qfs) ; + want = qfs_arg_float_snprintf(qfs->ptr, have + 1, format, &val, ant) ; + + if (want > 0) + { + if (want <= have) + qfs->ptr += want ; + else + { + qfs->ptr = qfs->end ; + qfs->overflow += (want - have) ; + } ; + } ; + } + else + { + /* Because the offset is not zero, need to use an intermediate + * buffer and then copy part after the offset. + * + * First, discover full extent of the formatted value, then if that + * exceeds the offset, construct buffer and copy what we can to the + * qps; otherwise, reduce the offset. + */ + want = qfs_arg_float_snprintf(NULL, 0, format, &val, ant) ; + + if (want > 0) + { + int take ; + + take = qfs->offset + qfs_left(qfs) ; + if (take > want) + take = want ; + + { + char tmp[take + 1] ; + want = qfs_arg_float_snprintf(tmp, take + 1, format, &val, ant) ; + + if (want > 0) + qfs_append_n(qfs, tmp, want) ; + } ; + } ; + } ; + + return (want >= 0) ? pfp_done : pfp_failed ; +} ; diff --git a/lib/qfstring.h b/lib/qfstring.h index 03204c3d..be858899 100644 --- a/lib/qfstring.h +++ b/lib/qfstring.h @@ -26,11 +26,15 @@ #include "vargs.h" /*============================================================================== - * These "qfstrings" address the issues of dealing with *fixed* length + * These "qfstring" address the issues of dealing with *fixed* length * strings, particularly where the string handling must be async-signal-safe. * * Are also used to support snprintf() style printing, but to one or more * fixed length buffers. + * + * All operations that can possibly be async-signal-safe, are. Notable + * exception is anything involving floating point values -- because of the + * state contain in floating point status/option registers ! */ /* When initialised a qf_string is set: @@ -75,8 +79,9 @@ enum pf_flags pf_precision = BIT( 7), /* '.' seen */ /* The following signal how to render the value */ - pf_hex = BIT( 8), /* hex */ - pf_uc = BIT( 9), /* upper-case */ + pf_oct = BIT( 8), /* octal */ + pf_hex = BIT( 9), /* hex */ + pf_uc = BIT(10), /* upper-case */ /* The following signal the type of value */ pf_ptr = BIT(14), /* is a pointer */ @@ -38,6 +38,7 @@ #include <lib/version.h> #include "memory.h" #include "sockunion.h" +#include "sockopt.h" #include "smux.h" #define min(A,B) ((A) < (B) ? (A) : (B)) @@ -215,8 +216,8 @@ smux_socket (void) sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) continue; - sockopt_reuseaddr (sock); - sockopt_reuseport (sock); + setsockopt_reuseaddr (sock); + setsockopt_reuseport (sock); ret = connect (sock, res->ai_addr, res->ai_addrlen); if (ret < 0) { @@ -251,8 +252,8 @@ smux_socket (void) serv.sin_addr.s_addr = htonl (INADDR_LOOPBACK); - sockopt_reuseaddr (sock); - sockopt_reuseport (sock); + setsockopt_reuseaddr (sock); + setsockopt_reuseport (sock); ret = connect (sock, (struct sockaddr *) &serv, sizeof (struct sockaddr_in)); if (ret < 0) diff --git a/lib/sockopt.c b/lib/sockopt.c index aa747429..083cafc2 100644 --- a/lib/sockopt.c +++ b/lib/sockopt.c @@ -1,4 +1,4 @@ -/* setsockopt functions +/* Setting and getting socket options -- utility functions. * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. @@ -20,218 +20,585 @@ */ #include <zebra.h> + #include "log.h" #include "sockopt.h" #include "sockunion.h" #include "pthread_safe.h" -int -setsockopt_so_recvbuf (int sock_fd, int size) +/*------------------------------------------------------------------------------ + * Set socket SO_REUSEADDR option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_reuseaddr (int sock_fd) { int ret; + int on = 1; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ; + ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); if (ret < 0) { int err = errno ; - zlog_err ("socket %d: cannot setsockopt SO_RCVBUF to %d: %s", - sock_fd, size, errtoa(err, 0).str) ; + zlog_warn ("cannot set sockopt SO_REUSEADDR on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; errno = err ; } ; - return ret; + return ret ; } -int -setsockopt_so_sendbuf (const int sock_fd, int size) +/*------------------------------------------------------------------------------ + * Set socket SO_REUSEPORT option -- if it is locally supported. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_reuseport (int sock_fd) { - int ret ; + int ret; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); +#ifdef SO_REUSEPORT + int on = 1; + ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT, &on, sizeof(on)); +#else + ret = 0 ; +#endif if (ret < 0) { int err = errno ; - zlog_err ("socket %d: cannot setsockopt SO_SNDBUF to %d: %s", - sock_fd, size, errtoa(err, 0).str) ; + zlog_warn ("cannot set sockopt SO_REUSEPORT on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; errno = err ; } ; - return ret; + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set socket SO_BROADCAST option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_broadcast (int sock_fd) +{ + int ret; + int on = 1; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)); + if (ret < 0) + { + int err = errno ; + zlog_warn ("cannot set sockopt SO_BROADCAST on socket %d: %s", sock_fd, + errtoa(err, 0).str) ; + errno = err ; + } + return ret ; } -int -getsockopt_so_sendbuf (const int sock_fd) +/*------------------------------------------------------------------------------ + * Set TCP_CORK, if available. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed -- see errno + * + * Logs a LOG_WARNING message if fails. + */ +extern int +setsockopt_cork (int sock_fd, int onoff) { - u_int32_t optval; - socklen_t optlen = sizeof (optval); +#ifdef TCP_CORK + int ret; - int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen); + ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_CORK, &onoff, sizeof(onoff)); if (ret < 0) - { - int err = errno ; - zlog_err ("socket %d: cannot getsockopt SO_SNDBUF: %s", + { + int err = errno ; + zlog_warn ("cannot set sockopt TCP_CORK to %d on socket %d: %s", onoff, sock_fd, errtoa(err, 0).str) ; - errno = err ; - return ret; - } - - return optval; + errno = err ; + } + return ret ; +#else + return 0; +#endif } -static void * -getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) +/*------------------------------------------------------------------------------ + * Set IP_TTL/IPV6_UNICAST_HOPS for socket, if available + * + * The ttl given is the maximum number of hops to allow -- so will generally + * be 1 or 255 or some small number. + * + * NB: This code treats any ttl outside the range 1..MAXTTL as MAXTTL. + * + * Returns: >= 0 => OK -- or not supported + * < 0 => failed, see errno. + * + * Logs a LOG_WARNING message if fails. + * + * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question + * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS. + * + * Here we try first to use the socket family, and if that fails on + * AF_INET6, then if the protocol family is AF_INET, then tries that. + */ +extern int +setsockopt_ttl (int sock_fd, int ttl) { - struct cmsghdr *cmsg; - void *ptr = NULL; + const char* name ; + int af ; + int ret ; - for (cmsg = ZCMSG_FIRSTHDR(msgh); - cmsg != NULL; - cmsg = CMSG_NXTHDR(msgh, cmsg)) - if (cmsg->cmsg_level == level && cmsg->cmsg_type) - return (ptr = CMSG_DATA(cmsg)); + af = sockunion_getsockfamily(sock_fd) ; + if (af < 0) + return af ; - return NULL; -} + if ((ttl < 1) || (ttl > MAXTTL)) + ttl = MAXTTL ; -#ifdef HAVE_IPV6 -/* Set IPv6 packet info to the socket. */ -int -setsockopt_ipv6_pktinfo (int sock_fd, int val) -{ - int ret; + ret = 0 ; + name = NULL ; -#ifdef IPV6_RECVPKTINFO /*2292bis-01*/ - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO, &val, sizeof(val)); - if (ret < 0) + while (1) { - int err = errno ; - zlog_warn ("cannot setsockopt IPV6_RECVPKTINFO: %s", errtoa(err, 0).str); - errno = err ; + switch (af) + { + case AF_INET: +#ifdef IP_TTL + ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); + name = "IP_TTL" ; +#endif /* IP_TTL */ + break ; + +#ifdef HAVE_IPV6 + case AF_INET6: + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, + &ttl, sizeof(ttl)); + name = "IPV6_UNICAST_HOPS" ; + + if (ret < 0) + { + af = sockunion_getprotofamily(sock_fd) ; + if (af == AF_INET) + continue ; + } ; + break ; +#endif + + default: /* ignore unknown family */ + break ; + } ; + + break ; } ; -#else /*RFC2292*/ - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_PKTINFO, &val, sizeof(val)); + if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_PKTINFO: %s", errtoa(err, 0).str); + zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, ttl, + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#endif /* INIA_IPV6 */ - return ret; -} -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_checksum (int sock_fd, int val) + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set IP_MINTTL/IPV6_MINHOPCOUNT (GTSM), if available. + * + * The ttl given is the maximum number of hops to allow -- so will generally + * be 1 -- which is the same as IP_TTL/IPV6_UNICAST_HOPS. + * + * NB: to turn off GTSM, need to set ttl = MAXTTL. This code treats any ttl + * outside the range 1..MAXTTL as MAXTTL. + * + * The underlying mechanics want MAX_TTL - (ttl - 1) -- and may not + * accept a value of zero. + * + * Returns: >= 0 => OK + * < 0 => failed or not supported -- see errno + * EOPNOTSUPP if not supported + * + * Logs a LOG_WARNING message if fails (and is supported). + * + * NB: for AF_INET6 where have: IN6_IS_ADDR_V4MAPPED, there is the question + * of whether to use IPPROTO_IP/IP_TTL or IPPROTO_IPV6/IPV6_UNICAST_HOPS. + * + * Here we try first to use the socket family, and if that fails on + * AF_INET6, then if the protocol family is AF_INET, then tries that. + */ +extern int +setsockopt_minttl (int sock_fd, int ttl) { - int ret; + const char* name ; + int af ; + int minttl ; + int ret ; -#ifdef GNU_LINUX - ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); + af = sockunion_getsockfamily(sock_fd) ; + if (af < 0) + return af ; + + ret = 0 ; + name = NULL ; + + if ((ttl < 1) || (ttl > MAXTTL)) + ttl = MAXTTL ; + + minttl = MAXTTL - (ttl - 1) ; + + while (1) + { + enum + { +#ifdef IP_MINTTL + have_ip_minttl = true, #else - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); -#endif /* GNU_LINUX */ + have_ip_minttl = false, +#endif + ip_minttl = IP_MINTTL + 0 + } ; + +#ifdef HAVE_IPV6 + +# ifdef GNU_LINUX + /* The #include to bring in IPV6_MINHOPCOUNT is buried more or less as + * deep as we can get it, because it also redefines a number of things + * that we do not want redefined. + */ + #include <linux/in6.h> +# endif + + enum + { +# ifdef IPV6_MINHOPCOUNT + have_ipv6_minhopcount = true, +# else + have_ipv6_minhopcount = false, +# endif + ipv6_minhopcount = IPV6_MINHOPCOUNT + 0 + } ; +#endif /* HAVE_IPV6 */ + + switch (af) + { + case AF_INET: + name = "IP_MINTTL" ; + if (have_ip_minttl) + ret = setsockopt (sock_fd, IPPROTO_IP, ip_minttl, + &minttl, sizeof(minttl)); + else + { + ret = -1 ; + errno = EOPNOTSUPP ; + } ; + + break ; + +#ifdef HAVE_IPV6 + case AF_INET6: + name = "IPV6_MINHOPCOUNT" ; + if (have_ipv6_minhopcount) + ret = setsockopt (sock_fd, IPPROTO_IPV6, ipv6_minhopcount, + &minttl, sizeof(minttl)); + else + { + ret = -1 ; + errno = EOPNOTSUPP ; + } ; + + if (ret < 0) + { + af = sockunion_getprotofamily(sock_fd) ; + if (af == AF_INET) + continue ; + } ; + + break ; +#endif /* HAVE_IPV6 */ + + default: /* ignore unknown family */ + break ; + } ; + + break ; + } ; + if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_CHECKSUM: %s", errtoa(err, 0).str); + zlog_warn("cannot set sockopt %s to %d on socket %d: %s", name, minttl, + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; - return ret; -} -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_multicast_hops (int sock_fd, int val) + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set TCP MD5 signature socket option, if available. + * + * A NULL password or an empty password both signify unsetting the MD5 + * signature. + * + * Returns: >= 0 => OK (or not supported, but password NULL or empty) + * < 0 => failed or not supported, see errno. + * + * NB: returns EOPNOTSUPP if TCP MD5 is not supported and password is not NULL + * and is not empty. + * + * Logs a LOG_ERR message if fails (and is supported). + */ +extern int +setsockopt_tcp_signature (int sock_fd, sockunion su, const char *password) { - int ret; + int ret ; - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, - sizeof(val)); + if ((password != NULL) && (*password == '\0')) + password = NULL ; + + ret = 0 ; /* so far, so good */ + +#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) + /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's + * version of the Quagga patch (based on work by Rick Payne, and Bruce + * Simpson) + */ +#define TCP_MD5_AUTH 13 +#define TCP_MD5_AUTH_ADD 1 +#define TCP_MD5_AUTH_DEL 2 + struct tcp_rfc2385_cmd { + u_int8_t command; /* Command - Add/Delete */ + u_int32_t address; /* IPV4 address associated */ + u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ + void *key; /* MD5 Key */ + } cmd; + struct in_addr *addr = &su->sin.sin_addr; + + cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); + cmd.address = addr->s_addr; + cmd.keylen = (password != NULL ? strlen (password) : 0); + cmd.key = password; + + ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ; if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_MULTICAST_HOPS: %s", - errtoa(err, 0).str); + zlog_err("sockopt_tcp_signature: setsockopt(%d): %s", + sock_fd, errtoa(err, 0).str) ; errno = err ; + } + +#elif HAVE_DECL_TCP_MD5SIG + +# ifdef GNU_LINUX + struct tcp_md5sig md5sig ; +# ifdef HAVE_IPV6 + int saf ; + union sockunion sux[1] ; +# endif + + /* Testing reveals that in order to set an MD5 password for an AF_INET6 + * socket, the address passed in must be AF_INET6, even if what we are + * dealing with here is an IN6_IS_ADDR_V4MAPPED socket. + */ +# ifdef HAVE_IPV6 + saf = sockunion_getsockfamily(sock_fd) ; + if (saf < 0) + return saf ; + + if ((saf == AF_INET6) && (sockunion_family(su) == AF_INET)) + { + sockunion_copy (sux, su) ; + sockunion_map_ipv4 (sux) ; + su = sux ; /* substitute v4 mapped address */ } ; - return ret; -} +# endif -/* Set multicast hops val to the socket. */ -int -setsockopt_ipv6_unicast_hops (int sock_fd, int val) -{ - int ret; + /* Set address to AF_UNSPEC and key length and everything else to zero, + * then copy in the address and the key. + */ + memset (&md5sig, 0, sizeof (md5sig)) ; + confirm(AF_UNSPEC == 0) ; - ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); - if (ret < 0) + memcpy (&md5sig.tcpm_addr, &su->sa, sockunion_get_len(su)) ; + + if (password != NULL) { - int err = errno ; - zlog_warn("cannot setsockopt IPV6_UNICAST_HOPS: %s", errtoa(err, 0).str); - errno = err ; + size_t keylen = strlen(password) ; + + if (md5sig.tcpm_keylen <= TCP_MD5SIG_MAXKEYLEN) + { + md5sig.tcpm_keylen = keylen ; + memcpy (md5sig.tcpm_key, password, keylen); + } + else + { + errno = EINVAL ; /* manufactured error */ + ret = -1 ; + } ; } ; - return ret; -} -int -setsockopt_ipv6_hoplimit (int sock_fd, int val) -{ - int ret; +# else + /* + * XXX Need to do PF_KEY operation here to add/remove an SA entry, + * and add/remove an SP entry for this peer's packet flows also. + */ + int md5sig = (password != NULL) ? 1 : 0; + +# endif /* GNU_LINUX */ + + if (ret >= 0) + { + ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, + &md5sig, sizeof(md5sig)) ; + if (ret < 0) + /* ENOENT is harmless. It is returned when we clear a password where + * one was not previously set. + */ + if ((errno == ENOENT) && (password == NULL)) + ret = 0 ; + } ; + +#else + + /* TCP MD5 is not supported */ + + if (password != NULL) + { + errno = EOPNOTSUPP ; /* manufactured error */ + ret = -1 ; + } ; + +#endif /* !HAVE_TCP_MD5SIG */ -#ifdef IPV6_RECVHOPLIMIT /*2292bis-01*/ - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &val, - sizeof(val)); if (ret < 0) { int err = errno ; - zlog_warn("cannot setsockopt IPV6_RECVHOPLIMIT: %s", errtoa(err, 0).str); + zlog_err("sockopt_tcp_signature: setsockopt(%d): %s", + sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#else /*RFC2292*/ - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_HOPLIMIT, &val, sizeof(val)); + + return ret ; +} ; + +/*------------------------------------------------------------------------------ + * Set SO_RCVBUF option on socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_so_recvbuf (int sock_fd, int size) +{ + int ret; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)) ; if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_HOPLIMIT: %s", errtoa(err, 0).str); + zlog_err ("cannot set sockopt SO_RCVBUF to %d on socket %d: %s", + size, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; -#endif + return ret; } -/* Set multicast loop zero to the socket. */ -int -setsockopt_ipv6_multicast_loop (int sock_fd, int val) +/*------------------------------------------------------------------------------ + * Set SO_SNDBUF option on socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_so_sendbuf (int sock_fd, int size) { - int ret; + int ret ; + + ret = setsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, - sizeof (val)); if (ret < 0) { int err = errno ; - zlog_warn ("cannot setsockopt IPV6_MULTICAST_LOOP: %s", - errtoa(err, 0).str); + zlog_err("cannot set sockopt SO_SNDBUF to %d on socket %d: %s", + size, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; + return ret; } -static int -getsockopt_ipv6_ifindex (struct msghdr *msgh) +/*------------------------------------------------------------------------------ + * Get SO_SNDBUF option value from socket. + * + * Returns: >= 0 => OK == value of option + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +getsockopt_so_sendbuf (int sock_fd) { - struct in6_pktinfo *pktinfo; + u_int32_t optval; + socklen_t optlen = sizeof (optval); - pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); + int ret = getsockopt (sock_fd, SOL_SOCKET, SO_SNDBUF, &optval, &optlen); + if (ret < 0) + { + int err = errno ; + zlog_err ("cannot get sockopt SO_SNDBUF on socket %d: %s", + sock_fd, errtoa(err, 0).str) ; + errno = err ; + return ret; + } - return pktinfo->ipi6_ifindex; + return optval; } -#endif /* HAVE_IPV6 */ +/*------------------------------------------------------------------------------ + * Set IP_TOS option for AF_INET socket. + * + * Returns: >= 0 => OK + * < 0 => failed, see errno. + * + * Logs a LOG_ERR message if fails + */ +extern int +setsockopt_ipv4_tos(int sock_fd, int tos) +{ + int ret; -/* + ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IP_TOS option %#x on socket %d: %s", + tos, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} ; + +/*------------------------------------------------------------------------------ * Process multicast socket options for IPv4 in an OS-dependent manner. * Supported options are IP_MULTICAST_IF and IP_{ADD,DROP}_MEMBERSHIP. * @@ -252,7 +619,7 @@ getsockopt_ipv6_ifindex (struct msghdr *msgh) * but this behavior should not be harmful if they behave the same way, * allow leaves, or implicitly leave all groups joined to down interfaces. */ -int +extern int setsockopt_multicast_ipv4(int sock_fd, int optname, struct in_addr if_addr /* required */, @@ -365,27 +732,83 @@ setsockopt_multicast_ipv4(int sock_fd, } +/*============================================================================== + * Set pktinfo and get ifindex etc + */ + +static int setsockopt_ipv4_pktinfo (int sock_fd, int val) ; +static int getsockopt_ipv4_ifindex (struct msghdr *msgh) ; + +#ifdef HAVE_IPV6 +static int getsockopt_ipv6_ifindex (struct msghdr *msgh) ; +#endif + +static void * getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) ; + +/*------------------------------------------------------------------------------ + * Set IP_PKTINFO/IP_RECVIF or IPV6_RECVPKTINFO/IPV6_PKTINFO -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_pktinfo (int af, int sock_fd, int val) +{ + int ret = -1; + + switch (af) + { + case AF_INET: + ret = setsockopt_ipv4_pktinfo (sock_fd, val); + break; +#ifdef HAVE_IPV6 + case AF_INET6: + ret = setsockopt_ipv6_pktinfo (sock_fd, val); + break; +#endif + default: + zlog_warn("setsockopt_ifindex: unknown address family %d", af) ; + ret = -1 ; + errno = EINVAL; + break ; + } + return ret; +} + +/*------------------------------------------------------------------------------ + * Set IP_PKTINFO or IP_RECVIF -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ static int -setsockopt_ipv4_ifindex (int sock_fd, int val) +setsockopt_ipv4_pktinfo (int sock_fd, int val) { int ret; -#if defined (IP_PKTINFO) - ret = setsockopt (sock_fd, IPPROTO_IP, IP_PKTINFO, &val, sizeof (val)) ; - if (ret < 0) - { - int err = errno ; - zlog_warn ("Can't set IP_PKTINFO option for fd %d to %d: %s", - sock_fd, val, errtoa(err, 0).str); - errno = err ; - } ; -#elif defined (IP_RECVIF) - ret = setsockopt (sock_fd, IPPROTO_IP, IP_RECVIF, &val, sizeof (val)) ; +#if defined(IP_PKTINFO) || defined(IP_RECVIF) + + int opt ; + const char* name ; + +# if defined(IP_PKTINFO) + opt = IP_PKTINFO ; + name = "IP_PKTINFO" ; +# else + opt = IP_RECVIF ; + name = "IP_RECVIF" ; +# endif + + ret = setsockopt (sock_fd, IPPROTO_IP, opt, &val, sizeof (val)) ; if (ret < 0) { int err = errno ; - zlog_warn ("Can't set IP_RECVIF option for fd %d to %d: %s", - sock_fd, val, errtoa(err, 0).str); + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); errno = err ; } ; #else @@ -393,53 +816,82 @@ setsockopt_ipv4_ifindex (int sock_fd, int val) #warning "Will not be able to receive link info." #warning "Things might be seriously broken.." /* XXX Does this ever happen? Should there be a zlog_warn message here? */ - ret = -1; + ret = -1; + errno = EOPNOTSUPP ; /* manufactured error */ #endif return ret; } -int -setsockopt_ipv4_tos(int sock_fd, int tos) +/*------------------------------------------------------------------------------ + * Set IPV6_RECVPKTINFO (RFC3542) or IPV6_RECVIF (RFC2292) -- if available. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +#ifdef HAVE_IPV6 +extern int +setsockopt_ipv6_pktinfo (int sock_fd, int val) { int ret; + int opt ; + const char* name ; - ret = setsockopt (sock_fd, IPPROTO_IP, IP_TOS, &tos, sizeof (tos)); +# ifdef IPV6_RECVPKTINFO + opt = IPV6_RECVPKTINFO ; /* RFC3542 == RFC2292-bis */ + name = "IPV6_RECVPKTINFO" ; +# else + opt = IPV6_PKTINFO ; /* RFC2292 */ + name = "IPV6_PKTINFO" ; +# endif + + ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val)); if (ret < 0) { int err = errno ; - zlog_warn ("Can't set IP_TOS option for fd %d to %#x: %s", - sock_fd, tos, errtoa(err, 0).str); + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); errno = err ; } ; + return ret; } +#endif - -int -setsockopt_ifindex (int af, int sock_fd, int val) +/*------------------------------------------------------------------------------ + * Given a struct msghdr*, extract and return ifindex. + * + * Returns: > 0 => OK == ifindex + * 0 => none found or error + * + * Note: this is badly named, since it is not really a getsockopt() operation, + * but extracting data from a sendmsg/recvmsg struct msghdr. + * + * To have the ifindex returned, need to have setsockopt_pktinfo(). + */ +extern int +getsockopt_ifindex (int af, struct msghdr *msgh) { - int ret = -1; - switch (af) { case AF_INET: - ret = setsockopt_ipv4_ifindex (sock_fd, val); - break; + return (getsockopt_ipv4_ifindex (msgh)); + #ifdef HAVE_IPV6 case AF_INET6: - ret = setsockopt_ipv6_pktinfo (sock_fd, val); - break; + return (getsockopt_ipv6_ifindex (msgh)); #endif + default: - zlog_warn ("setsockopt_ifindex: unknown address family %d", af); - ret = -1 ; - errno = EINVAL; - break ; + zlog_warn ("getsockopt_ifindex: unknown address family %d", af); + return 0 ; } - return ret; } -/* +/*------------------------------------------------------------------------------ + * AF_INET: extract ifindex from struct msghdr, if can + * * Requires: msgh is not NULL and points to a valid struct msghdr, which * may or may not have control data about the incoming interface. * @@ -449,17 +901,17 @@ setsockopt_ifindex (int af, int sock_fd, int val) static int getsockopt_ipv4_ifindex (struct msghdr *msgh) { - /* XXX: initialize to zero? (Always overwritten, so just cosmetic.) */ - int ifindex = -1; + int ifindex ; #if defined(IP_PKTINFO) /* Linux pktinfo based ifindex retrieval */ struct in_pktinfo *pktinfo; - pktinfo = - (struct in_pktinfo *)getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); - /* XXX Can pktinfo be NULL? Clean up post 0.98. */ - ifindex = pktinfo->ipi_ifindex; + pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IP, IP_PKTINFO); + if (pktinfo != NULL) + ifindex = pktinfo->ipi_ifindex ; + else + ifindex = 0 ; #elif defined(IP_RECVIF) @@ -510,34 +962,73 @@ getsockopt_ipv4_ifindex (struct msghdr *msgh) return ifindex; } -/* return ifindex, 0 if none found */ -int -getsockopt_ifindex (int af, struct msghdr *msgh) +/*------------------------------------------------------------------------------ + * AF_INET6: extract ifindex from struct msghdr, if can + * + * Requires: msgh is not NULL and points to a valid struct msghdr, which + * may or may not have control data about the incoming interface. + * + * Returns the interface index (small integer >= 1) if it can be + * determined, or else 0. + */ +#ifdef HAVE_IPV6 +static int +getsockopt_ipv6_ifindex (struct msghdr *msgh) { - int ifindex = 0; + struct in6_pktinfo *pktinfo; - switch (af) - { - case AF_INET: - return (getsockopt_ipv4_ifindex (msgh)); - break; -#ifdef HAVE_IPV6 - case AF_INET6: - return (getsockopt_ipv6_ifindex (msgh)); - break; + pktinfo = getsockopt_cmsg_data (msgh, IPPROTO_IPV6, IPV6_PKTINFO); + + if (pktinfo != NULL) + return pktinfo->ipi6_ifindex; + else + return 0 ; +} #endif - default: - zlog_warn ("getsockopt_ifindex: unknown address family %d", af); - return (ifindex = 0); - } + +/*------------------------------------------------------------------------------ + * Scan msg_control portion of struct msghdr, looking for a cmsg with the given + * level and type. + * + * Requires: msgh is not NULL and points to a valid struct msghdr, which + * may or may not have control data about the incoming interface. + * + * Returns: address of data part of cmsg + * or: NULL => not found + */ +static void * +getsockopt_cmsg_data (struct msghdr *msgh, int level, int type) +{ + struct cmsghdr *cmsg; + + for (cmsg = ZCMSG_FIRSTHDR(msgh); + cmsg != NULL; + cmsg = CMSG_NXTHDR(msgh, cmsg)) + if ((cmsg->cmsg_level == level) && (cmsg->cmsg_type == type)) + return (void*)CMSG_DATA(cmsg); + + return NULL; } -/* swab iph between order system uses for IP_HDRINCL and host order */ -void +/*------------------------------------------------------------------------------ + * swab iph between order system uses for IP_HDRINCL and host order + * + * This is done before handing struct ip to the system. + * + * There are four u_short fields in the IPv4 header: + * + * u_short ip_len -- convert to network order, except as noted below + * u_short ip_id -- convert to network order + * u_short ip_off -- convert to network order, except as noted below + * u_short ip_sum -- which we don't touch -- set by kernel + */ +extern void sockopt_iphdrincl_swab_htosys (struct ip *iph) { - /* BSD and derived take iph in network order, except for - * ip_len and ip_off + /* BSD and derived take iph in network order, except for ip_len and ip_off. + * + * So if *not* BSD-like, then need to convert ip_len and ip_off to network + * order. */ #ifndef HAVE_IP_HDRINCL_BSD_ORDER iph->ip_len = htons(iph->ip_len); @@ -547,7 +1038,12 @@ sockopt_iphdrincl_swab_htosys (struct ip *iph) iph->ip_id = htons(iph->ip_id); } -void +/*------------------------------------------------------------------------------ + * swab iph between order system uses for IP_HDRINCL and host order + * + * This is done after receiving struct ip from the system -- see notes above. + */ +extern void sockopt_iphdrincl_swab_systoh (struct ip *iph) { #ifndef HAVE_IP_HDRINCL_BSD_ORDER @@ -559,190 +1055,171 @@ sockopt_iphdrincl_swab_systoh (struct ip *iph) } /*============================================================================== - * Set TCP MD5 signature socket option. + * IPv6 Stuff + */ +#ifdef HAVE_IPV6 + +/*------------------------------------------------------------------------------ + * Set IPV6_V6ONLY. * * Returns: >= 0 => OK - * < 0 => failed, see errno. + * < 0 => failed -- see errno * - * NB: returns ENOSYS if TCP MD5 is not supported + * Logs a LOG_ERR message if fails. */ -int -sockopt_tcp_signature (int sock_fd, union sockunion *su, const char *password) +extern int +setsockopt_ipv6_v6only(int sock_fd) { -#if defined(HAVE_TCP_MD5_LINUX24) && defined(GNU_LINUX) - int ret ; - - /* Support for the old Linux 2.4 TCP-MD5 patch, taken from Hasso Tepper's - * version of the Quagga patch (based on work by Rick Payne, and Bruce - * Simpson) - */ -#define TCP_MD5_AUTH 13 -#define TCP_MD5_AUTH_ADD 1 -#define TCP_MD5_AUTH_DEL 2 - struct tcp_rfc2385_cmd { - u_int8_t command; /* Command - Add/Delete */ - u_int32_t address; /* IPV4 address associated */ - u_int8_t keylen; /* MD5 Key len (do NOT assume 0 terminated ascii) */ - void *key; /* MD5 Key */ - } cmd; - struct in_addr *addr = &su->sin.sin_addr; - - cmd.command = (password != NULL ? TCP_MD5_AUTH_ADD : TCP_MD5_AUTH_DEL); - cmd.address = addr->s_addr; - cmd.keylen = (password != NULL ? strlen (password) : 0); - cmd.key = password; - - ret = setsockopt (sock_fd, IPPROTO_TCP, TCP_MD5_AUTH, &cmd, sizeof cmd) ; - - return (ret >= 0) ? 0 : -1 ; - -#elif HAVE_DECL_TCP_MD5SIG - int ret, err ; -# ifndef GNU_LINUX - /* - * XXX Need to do PF_KEY operation here to add/remove an SA entry, - * and add/remove an SP entry for this peer's packet flows also. - */ - int md5sig = password && *password ? 1 : 0; -# else - int keylen = password ? strlen (password) : 0 ; - struct tcp_md5sig md5sig ; - union sockunion *su2 ; - union sockunion susock ; + int ret; + int on = 1 ; - /* Figure out whether the socket and the sockunion are the same family.. - * adding AF_INET to AF_INET6 needs to be v4 mapped, you'd think.. - */ - ret = sockunion_getsockname(sock_fd, &susock) ; + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); if (ret < 0) - return ret ; - - if (susock.sa.sa_family == su->sa.sa_family) - su2 = su ; - else { - /* oops.. */ - su2 = &susock ; - - if (su2->sa.sa_family == AF_INET) - return 0 ; /* TODO: find out what this is doing ?? */ - -# ifdef HAVE_IPV6 - /* If this does not work, then all users of this sockopt will need to - * differentiate between IPv4 and IPv6, and keep separate sockets for - * each. - * - * Sadly, it doesn't seem to work at present. It's unknown whether - * this is a bug or not. - */ - if (su2->sa.sa_family == AF_INET6 - && su->sa.sa_family == AF_INET) - { - su2->sin6.sin6_family = AF_INET6; - /* V4Map the address */ - memset (&su2->sin6.sin6_addr, 0, sizeof (struct in6_addr)); - su2->sin6.sin6_addr.s6_addr[10] = 0xff ; - su2->sin6.sin6_addr.s6_addr[11] = 0xff ; -# ifdef s6_addr32 - su2->sin6.sin6_addr.s6_addr32[3] = su->sin.sin_addr.s_addr ; -# else - memcpy (&su2->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4); -# endif - } -# endif + int err = errno ; + zlog_err ("cannot set sockopt IPV6_V6ONLY on socket %d: %s", + sock_fd, errtoa(err, 0).str) ; + errno = err ; } + return ret ; +} ; - memset (&md5sig, 0, sizeof (md5sig)); - memcpy (&md5sig.tcpm_addr, su2, sizeof (*su2)); - md5sig.tcpm_keylen = keylen; - if (keylen) - memcpy (md5sig.tcpm_key, password, keylen); - -# endif /* GNU_LINUX */ +/*------------------------------------------------------------------------------ + * Set IPV6_CHECKSUM + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_checksum (int sock_fd, int val) +{ + int ret; - err = 0 ; - ret = setsockopt(sock_fd, IPPROTO_TCP, TCP_MD5SIG, &md5sig, sizeof(md5sig)) ; +#ifdef GNU_LINUX + ret = setsockopt(sock_fd, IPPROTO_RAW, IPV6_CHECKSUM, &val, sizeof(val)); +#else + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_CHECKSUM, &val, sizeof(val)); +#endif /* GNU_LINUX */ if (ret < 0) { - err = errno ; - /* ENOENT is harmless. It is returned when we clear a password for which - one was not previously set. */ - if (err == ENOENT) - err = 0 ; - else - { - zlog_err ("sockopt_tcp_signature: setsockopt(%d): %s", + int err = errno ; + zlog_err("cannot set sockopt IPV6_CHECKSUM to %d on socket %d: %s", val, sock_fd, errtoa(err, 0).str) ; - errno = err ; - } ; - } - - return (err == 0) ? 0 : -1 ; - -#else + errno = err ; + } ; + return ret; +} - /* TCP MD5 is not supported */ +/*------------------------------------------------------------------------------ + * Set unicast hops val to the socket (cf IP_TTL). + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_unicast_hops (int sock_fd, int val) +{ + int ret; - if ((password == NULL) || (*password == '\0')) - return 0 ; /* OK if not required ! */ + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &val, sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IPV6_UNICAST_HOPS to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} - errno = ENOSYS ; /* manufactured error */ - return -1 ; +/*------------------------------------------------------------------------------ + * Set multicast hops val to the socket. + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_multicast_hops (int sock_fd, int val) +{ + int ret; -#endif /* !HAVE_TCP_MD5SIG */ -} ; + ret = setsockopt(sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &val, + sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt IPV6_MULTICAST_HOPS to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; + return ret; +} -/*============================================================================== - * Set TTL for socket +/*------------------------------------------------------------------------------ + * Set IPV6_RECVHOPLIMIT option (or IPV6_HOPLIMIT) * * Returns: >= 0 => OK - * < 0 => failed, see errno. + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. */ -int -sockopt_ttl (int sock_fd, int ttl) +extern int +setsockopt_ipv6_hoplimit (int sock_fd, int val) { - const char* msg ; - int ret ; - int family ; - - ret = 0 ; - msg = NULL ; + int ret; + int opt ; + const char* name ; - family = sockunion_getsockfamily(sock_fd) ; - if (family < 0) - return -1 ; +# ifdef IPV6_RECVHOPLIMIT + opt = IPV6_RECVHOPLIMIT ; /* RFC3542 == RFC2292-bis */ + name = "IPV6_RECVHOPLIMIT" ; +# else + opt = IPV6_HOPLIMIT ; /* RFC2292 */ + name = "IPV6_HOPLIMIT" ; +# endif - switch (family) - { - case AF_INET: -#ifdef IP_TTL - ret = setsockopt (sock_fd, IPPROTO_IP, IP_TTL,(void*)&ttl, sizeof(int)); - msg = "IP_TTL" ; -#endif /* IP_TTL */ - break ; + ret = setsockopt(sock_fd, IPPROTO_IPV6, opt, &val, sizeof(val)); + if (ret < 0) + { + int err = errno ; + zlog_err("cannot set sockopt %s to %d on socket %d: %s", name, + val, sock_fd, errtoa(err, 0).str); + errno = err ; + } ; -#ifdef HAVE_IPV6 - case AF_INET6: - ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_UNICAST_HOPS, - (void*)&ttl, sizeof(int)); - msg = "IPV6_UNICAST_HOPS" ; - break ; -#endif + return ret; +} - default: /* ignore unknown family */ - ret = 0 ; - break ; - } ; +/*------------------------------------------------------------------------------ + * Set IPV6_MULTICAST_LOOP option + * + * Returns: >= 0 => OK + * < 0 => failed -- see errno + * + * Logs a LOG_ERR message if fails. + */ +extern int +setsockopt_ipv6_multicast_loop (int sock_fd, int val) +{ + int ret; - if (ret != 0) + ret = setsockopt (sock_fd, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &val, + sizeof (val)); + if (ret < 0) { int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt %s %d to socket %d: %s", msg, ttl, sock_fd, - errtoa(err, 0).str) ; + zlog_err("cannot set sockopt IPV6_MULTICAST_LOOP to %d on socket %d: %s", + val, sock_fd, errtoa(err, 0).str); errno = err ; - return -1 ; } ; + return ret; +} +#endif /* HAVE_IPV6 */ + - return 0 ; -} ; diff --git a/lib/sockopt.h b/lib/sockopt.h index ad86f053..c706a74f 100644 --- a/lib/sockopt.h +++ b/lib/sockopt.h @@ -1,4 +1,4 @@ -/* Router advertisement +/* Setting and getting socket options -- utility functions. * Copyright (C) 1999 Kunihiro Ishiguro * * This file is part of GNU Zebra. @@ -24,18 +24,20 @@ #include "sockunion.h" +extern int setsockopt_reuseaddr (int sock_fd) ; +extern int setsockopt_reuseport (int sock_fd) ; +extern int setsockopt_broadcast (int sock_fd) ; + +extern int setsockopt_ttl (int sock_fd, int ttl); +extern int setsockopt_minttl (int sock_fd, int ttl); +extern int setsockopt_cork (int sock_fd, int onoff); + extern int setsockopt_so_recvbuf (int sock_fd, int size); -extern int setsockopt_so_sendbuf (const int sock_fd, int size); -extern int getsockopt_so_sendbuf (const int sock_fd); +extern int setsockopt_so_sendbuf (int sock_fd, int size); +extern int getsockopt_so_sendbuf (int sock_fd); -#ifdef HAVE_IPV6 -extern int setsockopt_ipv6_pktinfo (int, int); -extern int setsockopt_ipv6_checksum (int, int); -extern int setsockopt_ipv6_multicast_hops (int, int); -extern int setsockopt_ipv6_unicast_hops (int, int); -extern int setsockopt_ipv6_hoplimit (int, int); -extern int setsockopt_ipv6_multicast_loop (int, int); -#endif /* HAVE_IPV6 */ +extern int setsockopt_tcp_signature(int sock_fd, union sockunion *su, + const char *password); /* * It is OK to reference in6_pktinfo here without a protecting #if @@ -92,7 +94,7 @@ extern int setsockopt_multicast_ipv4(int sock_fd, int optname, extern int setsockopt_ipv4_tos(int sock_fd, int tos); /* Ask for, and get, ifindex, by whatever method is supported. */ -extern int setsockopt_ifindex (int, int, int); +extern int setsockopt_pktinfo (int, int, int); extern int getsockopt_ifindex (int, struct msghdr *); /* swab the fields in iph between the host order and system order expected @@ -101,7 +103,16 @@ extern int getsockopt_ifindex (int, struct msghdr *); extern void sockopt_iphdrincl_swab_htosys (struct ip *iph); extern void sockopt_iphdrincl_swab_systoh (struct ip *iph); -extern int sockopt_ttl (int sock_fd, int ttl); -extern int sockopt_tcp_signature(int sock_fd, union sockunion *su, - const char *password); +#ifdef HAVE_IPV6 + +extern int setsockopt_ipv6_v6only(int sock_fd) ; +extern int setsockopt_ipv6_pktinfo (int, int); +extern int setsockopt_ipv6_checksum (int, int); +extern int setsockopt_ipv6_multicast_hops (int, int); +extern int setsockopt_ipv6_unicast_hops (int, int); +extern int setsockopt_ipv6_hoplimit (int, int); +extern int setsockopt_ipv6_multicast_loop (int, int); + +#endif /* HAVE_IPV6 */ + #endif /*_ZEBRA_SOCKOPT_H */ diff --git a/lib/sockunion.c b/lib/sockunion.c index deac292c..9084c27e 100644 --- a/lib/sockunion.c +++ b/lib/sockunion.c @@ -249,11 +249,36 @@ sockunion_init_new(sockunion su, sa_family_t family) if (family != AF_UNSPEC) sockunion_set_family(su, family) ; + else + confirm(AF_UNSPEC == 0) ; return su ; } ; /*------------------------------------------------------------------------------ + * Get the length of the address in the given sockunion. + * + * Returns zero if AF_UNSPEC or not any known address family. + */ +extern int +sockunion_get_len(sockunion su) +{ + switch (su->sa.sa_family) + { + case AF_INET: + return sizeof(struct sockaddr_in) ; + +#ifdef HAVE_IPV6 + case AF_INET6: + return sizeof(struct sockaddr_in6) ; +#endif + + default: + return 0 ; + } ; +} ; + +/*------------------------------------------------------------------------------ * From the given string, fill in the given sockunion. * * Returns: 0 => OK -- sockunion filled in @@ -357,20 +382,19 @@ sockunion_su2str (union sockunion *su, enum MTYPE type) * If have an IPv6 mapped IPv4 address, convert it to an IPv4 address. */ extern void -sockunion_unmap_ipv4 (union sockunion *su) +sockunion_unmap_ipv4 (sockunion su) { #ifdef HAVE_IPV6 - union sockunion sux ; - struct sockaddr_in* su_in = &sux.sin ; - - if ( (su->sa.sa_family == AF_INET6) - && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) ) + if ( (sockunion_family(su) == AF_INET6) + && IN6_IS_ADDR_V4MAPPED (&su->sin6.sin6_addr) ) { - sockunion_init_new(&sux, AF_INET) ; - memcpy (&su_in->sin_addr, ((char *)&su->sin6.sin6_addr) + 12, 4) ; - su_in->sin_port = su->sin6.sin6_port ; - memcpy (su, &sux, sizeof(sux)) ; - confirm(sizeof(*su) == sizeof(sux)) ; + union sockunion sux[1] ; + + sockunion_init_new(sux, AF_INET) ; + memcpy (&sux->sin.sin_addr, &su->sin6.sin6_addr.s6_addr[12], 4) ; + sux->sin.sin_port = su->sin6.sin6_port ; + memcpy (su, sux, sizeof(*sux)) ; + confirm(sizeof(*su) == sizeof(*sux)) ; } #endif /* HAVE_IPV6 */ } @@ -379,20 +403,19 @@ sockunion_unmap_ipv4 (union sockunion *su) * If have an IPv4 address, convert it to an IPv6 mapped IPv4 address. */ extern void -sockunion_map_ipv4 (union sockunion *su) +sockunion_map_ipv4 (sockunion su) { #ifdef HAVE_IPV6 - union sockunion sux ; - struct sockaddr_in6* su_in6 = &sux.sin6 ; - - if (su->sa.sa_family == AF_INET) + if (sockunion_family(su) == AF_INET) { - sockunion_init_new(&sux, AF_INET6) ; - memset (((char *)&su_in6->sin6_addr) + 10, 0xFF, 2) ; - memcpy (((char *)&su_in6->sin6_addr) + 12, &su->sin.sin_addr, 4) ; - su_in6->sin6_port = su->sin.sin_port ; - memcpy (su, &sux, sizeof(sux)) ; - confirm(sizeof(*su) == sizeof(sux)) ; + union sockunion sux[1] ; + + sockunion_init_new(sux, AF_INET6) ; + memset (&sux->sin6.sin6_addr.s6_addr[10], 0xFF, 2) ; + memcpy (&sux->sin6.sin6_addr.s6_addr[12], &su->sin.sin_addr, 4) ; + sux->sin6.sin6_port = su->sin.sin_port ; + memcpy (su, sux, sizeof(*sux)) ; + confirm(sizeof(*su) == sizeof(*sux)) ; } #endif /* HAVE_IPV6 */ } @@ -414,7 +437,7 @@ sockunion_map_ipv4 (union sockunion *su) * -1 -- error -- not one of the above * -2 -- error -- one of the above */ -int +extern int sockunion_accept (int sock_fd, union sockunion *su) { socklen_t len; @@ -443,23 +466,22 @@ sockunion_accept (int sock_fd, union sockunion *su) * Returns: -1 : failed -- see errno * otherwise : socket * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ extern int -sockunion_socket(sa_family_t family, int type, int protocol) +sockunion_socket(sockunion su, int type, int protocol) { int sock_fd ; int err ; - sock_fd = socket(family, type, protocol); + sock_fd = socket(sockunion_family(su), type, protocol); if (sock_fd >= 0) return sock_fd ; err = errno ; - zlog (NULL, LOG_WARNING, - "Cannot make socket family=%d, type=%d, protocol=%d: %s", - (int)family, type, protocol, errtoa(err, 0).str) ; + zlog_err("Cannot make socket family=%d, type=%d, protocol=%d: %s", + (int)sockunion_family(su), type, protocol, errtoa(err, 0).str) ; errno = err ; return -1; } @@ -470,15 +492,15 @@ sockunion_socket(sa_family_t family, int type, int protocol) * Returns: -1 : failed -- see errno * otherwise : socket * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ -int -sockunion_stream_socket (union sockunion *su) +extern int +sockunion_stream_socket (sockunion su) { if (su->sa.sa_family == 0) su->sa.sa_family = AF_INET_UNION; - return sockunion_socket (su->sa.sa_family, SOCK_STREAM, 0); + return sockunion_socket (su, SOCK_STREAM, 0); } /*------------------------------------------------------------------------------ @@ -541,25 +563,24 @@ sockunion_connect(int sock_fd, union sockunion* peer_su, unsigned short port, /*------------------------------------------------------------------------------ * Start listening on given socket * - * Returns: 0 : OK (so far so good) - * < 0 : failed -- see errno + * Returns: >= 0 : OK (so far so good) + * < 0 : failed -- see errno * - * Logs a LOG_WARNING message if fails. + * Logs a LOG_ERR message if fails. */ extern int sockunion_listen(int sock_fd, int backlog) { - int ret, err ; + int ret ; ret = listen(sock_fd, backlog) ; - if (ret == 0) - return 0 ; - - err = errno ; - zlog (NULL, LOG_WARNING, "cannot listen on socket %d: %s", - sock_fd, errtoa(err, 0).str) ; - errno = err ; + if (ret < 0) + { + int err = errno ; + zlog_err("cannot listen on socket %d: %s", sock_fd, errtoa(err, 0).str) ; + errno = err ; + } ; return ret ; } ; @@ -567,28 +588,72 @@ sockunion_listen(int sock_fd, int backlog) /*------------------------------------------------------------------------------ * Bind socket to address/port. * - * Sets the given port into the sockunion su. + * If the 'any' parameter is true, sets the given sockunion to INADDR_ANY or + * the *socket* address family equivalent. * - * If the 'any' parameter is NULL, set the address part of sockunion to - * INADDR_ANY or the family equivalent. Note that for IPv6 this does not - * affect the flow/scope in the su. + * Sets the given port into the sockunion su. * * For good measure, sets sin_len or family equivalent if required. * + * If not 'any', and the given su does not have the same address family as the + * socket, then attempts to convert the su to the same family as the socket, + * by mapping or unmapping IPv4. + * * Performs bind() and logs a LOG_WARNING message if fails. * * Returns: >= 0 => OK * < 0 => failed -- see errno */ -int -sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, - void* any) +extern int +sockunion_bind(int sock_fd, sockunion su, unsigned short port, bool any) { int sa_len ; int ret ; + int sock_family ; - if (any == NULL) - sockunion_set_addr_any(su) ; + sock_family = sockunion_getsockfamily(sock_fd) ; + + if (any) + { + /* Create an "any" -- of same family as the socket */ + sockunion_init_new(su, sock_family) ; + sockunion_set_addr_any(su) ; + } + else + { + /* Want to bind to a specific address. + * + * We provide bind with an address which matches the address family of + * the *socket*. + * + * If the socket is AF_INET, address may be AF_INET, or an AF_INET6 + * *provided* it is an IPv4 mapped address. + * + * If the socket is AF_INET6, address may be AF_INET or AF_NET6, and + * will map any IPv4 address. + * + * If we don't HAVE_IPV6, or we don't recognise an address family, + * then do nothing and let bind() return some sort of error. + */ +#ifdef HAVE_IPV6 + if (sock_family != sockunion_family(su)) + { + switch (sock_family) + { + case AF_INET: + sockunion_unmap_ipv4(su) ; /* unmap if AF_INET6 mapped IPv4 */ + break ; + + case AF_INET6: + sockunion_map_ipv4(su) ; /* map if AF_INET */ + break ; + + default: + break ; + } ; + } ; +#endif + } ; sa_len = sockunion_set_port(su, port) ; @@ -596,7 +661,7 @@ sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, if (ret < 0) { int err = errno ; - zlog (NULL, LOG_WARNING, "cannot bind to %s port %d socket %d: %s", + zlog_warn("cannot bind to %s port %d socket %d: %s", sutoa(su).str, port, sock_fd, errtoa(err, 0).str) ; errno = err ; } ; @@ -605,72 +670,11 @@ sockunion_bind (int sock_fd, union sockunion *su, unsigned short port, } /*------------------------------------------------------------------------------ - * Set socket SO_REUSEADDR option - * - * Returns: >= 0 => OK - * < 0 => failed -- see errno - * - * Logs a LOG_WARNING message if fails. - */ -int -sockopt_reuseaddr (int sock_fd) -{ - int ret; - int on = 1; - - ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *) &on, - sizeof (on)); - if (ret < 0) - { - int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt SO_REUSEADDR to socket %d: %s", sock_fd, - errtoa(err, 0).str) ; - errno = err ; - } ; - - return ret ; -} - -/*------------------------------------------------------------------------------ - * Set socket SO_REUSEPORT option -- if it is locally supported. - * - * Returns: >= 0 => OK - * < 0 => failed -- see errno - * - * Logs a LOG_WARNING message if fails. - */ -int -sockopt_reuseport (int sock_fd) -{ - int ret; - -#ifdef SO_REUSEPORT - int on = 1; - ret = setsockopt (sock_fd, SOL_SOCKET, SO_REUSEPORT, - (void *) &on, sizeof (on)); -#else - ret = 0 ; -#endif - - if (ret < 0) - { - int err = errno ; - zlog (NULL, LOG_WARNING, - "cannot set sockopt SO_REUSEPORT to socket %d: %s", sock_fd, - errtoa(err, 0).str) ; - errno = err ; - } ; - - return ret ; -} ; - -/*------------------------------------------------------------------------------ - * If same family and same prefix return 1. + * If same (known) family and same prefix return 1, otherwise return 0. * * Returns 0 if same family, but not a known family. */ -int +extern int sockunion_same (union sockunion *su1, union sockunion *su2) { int ret = 0; @@ -686,7 +690,7 @@ sockunion_same (union sockunion *su1, union sockunion *su2) #ifdef HAVE_IPV6 case AF_INET6: ret = memcmp (&su1->sin6.sin6_addr, &su2->sin6.sin6_addr, - sizeof (struct in6_addr)); + sizeof (struct in6_addr)); return (ret == 0) ; #endif /* HAVE_IPV6 */ @@ -696,125 +700,142 @@ sockunion_same (union sockunion *su1, union sockunion *su2) } ; /*------------------------------------------------------------------------------ - * Get the address family the given socket is set to. + * Get local (getsockname) or remote (getpeername) address and port. * * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) * < 0 => failed -- see errno * - * NB: gets the actual address family -- does NOT look for mapped IPv4. - */ -extern int -sockunion_getsockfamily(int sock_fd) -{ - union sockunion su ; - int ret ; - socklen_t len ; - - if (sock_fd < 0) - return AF_UNSPEC ; - - sockunion_init_new(&su, AF_UNSPEC) ; - len = sizeof(su) ; - - ret = getsockname(sock_fd, (struct sockaddr *)&su, &len) ; - if (ret < 0) - { - int err = errno ; - zlog_warn ("Failed in getsockname for socket %d: %s", - sock_fd, errtoa(err, 0).str) ; - errno = err ; - return -1 ; - } ; - - return su.sa.sa_family ; -} ; - -/*------------------------------------------------------------------------------ - * Get local or remote address and port. - * - * Returns: >= 0 => OK - * < 0 => failed (or unknown family) -- see errno - * - * If address is an IPv4 mapped IPv6 address, returns the IPv4 address. + * If "unmap": if address is an IPv4 mapped IPv6 address, returns AF_INET. * * NB: returns EAFNOSUPPORT if don't recognise the address family. + * + * Logs a LOG_ERR message if fails in getsockname/getpeername. */ static int -sockunion_get_name(int sock_fd, union sockunion* su, int local) +sockunion_get_name(int sock_fd, union sockunion* su, bool local, bool unmap) { int ret ; - + socklen_t len ; union { - struct sockaddr sa; - struct sockaddr_in sin; -#ifdef HAVE_IPV6 - struct sockaddr_in6 sin6; -#endif /* HAVE_IPV6 */ + union sockunion su ; char tmp_buffer[128]; } name ; - socklen_t len = sizeof(name) ; + memset(su, 0, sizeof(union sockunion)) ; + + confirm(AF_UNSPEC == 0) ; + if (sock_fd < 0) + return AF_UNSPEC ; + + len = sizeof(name) ; memset(&name, 0, len); - memset(su, 0, sizeof(union sockunion)) ; - if (local) - ret = getsockname(sock_fd, (struct sockaddr *)&name, &len) ; + if (local) + ret = getsockname(sock_fd, &name.su.sa, &len) ; else - ret = getpeername(sock_fd, (struct sockaddr *)&name, &len) ; + ret = getpeername(sock_fd, &name.su.sa, &len) ; if (ret < 0) { int err = errno ; - zlog_warn ("Cannot get %s address and port: %s", - local ? "local" : "remote", errtoa(err, 0).str) ; + zlog_err("failed in %s for socket %d: %s", + local ? "getsockname" : "getpeername", + sock_fd, errtoa(err, 0).str) ; errno = err ; - return ret ; } + else + { + ret = name.su.sa.sa_family ; - ret = 0 ; /* assume all will be well */ - switch (name.sa.sa_family) - { - case AF_INET: - memcpy(su, &name, sizeof (struct sockaddr_in)) ; - break ; + switch (ret) + { + case AF_INET: + su->sin = name.su.sin ; + break ; #ifdef HAVE_IPV6 - case AF_INET6: - memcpy(su, &name, sizeof (struct sockaddr_in6)) ; - sockunion_unmap_ipv4(su) ; - break ; + case AF_INET6: + su->sin6 = name.su.sin6 ; + if (unmap) + sockunion_unmap_ipv4(su) ; + break ; #endif /* HAVE_IPV6 */ - default: - errno = EAFNOSUPPORT ; - ret = -1 ; - } ; + default: + errno = EAFNOSUPPORT ; + ret = -1 ; + } ; + } ; return ret ; } ; /*------------------------------------------------------------------------------ - * Get local address and port -- ie getsockname(). + * Get the address family the given socket is set to. + * + * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) + * < 0 => failed -- see errno + * + * NB: gets the actual address family -- does NOT look for mapped IPv4. + */ +extern int +sockunion_getsockfamily(int sock_fd) +{ + union sockunion su[1] ; + int ret ; + + ret = sockunion_get_name(sock_fd, su, true, /* true => local */ + false) ; /* false => don't unmap */ + return (ret >= 0) ? sockunion_family(su) : ret ; +} ; + +/*------------------------------------------------------------------------------ + * Get the address family the given socket's protocol is set to. + * + * If this is an AF_INET, that's easy. + * + * If this is an AF_INET6, then needs to look out for IN6_IS_ADDR_V4MAPPED. + * + * Returns: >= 0 == the address family (AF_UNSPEC if fd sock_fd < 0) + * < 0 => failed -- see errno + * + * NB: gets the underlying address family -- ie: looks for mapped IPv4. + */ +extern int +sockunion_getprotofamily(int sock_fd) +{ + union sockunion su[1] ; + int ret ; + + ret = sockunion_get_name(sock_fd, su, true, /* true => local */ + true) ; /* true => unmap */ + return (ret >= 0) ? sockunion_family(su) : ret ; +} ; + +/*------------------------------------------------------------------------------ + * Get local address and port -- ie getsockname(), except unmaps IPv4 mapped. * * See: sockunion_get_name() */ -int -sockunion_getsockname(int sock_fd, union sockunion* su_local) +extern int +sockunion_getsockname(int sock_fd, sockunion su_local) { - return sockunion_get_name(sock_fd, su_local, 1) ; + return sockunion_get_name(sock_fd, su_local, true, /* true => local */ + true) ; /* true => unmap */ } ; /*------------------------------------------------------------------------------ - * Get remote address and port -- ie getpeername(). + * Get remote address and port -- ie getpeername(), except unmaps IPv4 mapped. * * See: sockunion_get_name() */ -int -sockunion_getpeername (int sock_fd, union sockunion* su_remote) +extern int +sockunion_getpeername (int sock_fd, sockunion su_remote) { - return sockunion_get_name(sock_fd, su_remote, 0) ; + return sockunion_get_name(sock_fd, su_remote, false, /* false => remote */ + true) ; /* true => unmap */ } ; /*------------------------------------------------------------------------------ diff --git a/lib/sockunion.h b/lib/sockunion.h index 0b23ae63..eeae72d5 100644 --- a/lib/sockunion.h +++ b/lib/sockunion.h @@ -24,6 +24,7 @@ #define _ZEBRA_SOCKUNION_H #include "zebra.h" +#include <stdbool.h> #include "symtab.h" #include "prefix.h" #include "memory.h" @@ -101,36 +102,36 @@ struct sockunion_string /* Prototypes. */ extern sockunion sockunion_init_new(sockunion su, sa_family_t family) ; +extern int sockunion_get_len(sockunion su) ; extern int sockunion_set_port(sockunion su, in_port_t port) ; -extern int str2sockunion (const char *, union sockunion *); -extern const char *sockunion2str (union sockunion *, char *, size_t); +extern int str2sockunion (const char * str, sockunion su); +extern const char *sockunion2str (sockunion su, char* buf, size_t size); extern sockunion_string_t sutoa(sockunion su) ; -extern int sockunion_cmp (union sockunion *, union sockunion *); -extern int sockunion_same (union sockunion *, union sockunion *); - -extern char* sockunion_su2str (union sockunion* su, enum MTYPE type) ; -extern union sockunion *sockunion_str2su (const char *str); -extern struct in_addr sockunion_get_in_addr (union sockunion *su); -extern int sockunion_accept (int sock_fd, union sockunion *); -extern int sockunion_stream_socket (union sockunion *); -extern int sockopt_reuseaddr (int); -extern int sockopt_reuseport (int); -extern int sockunion_bind (int sock_fd, union sockunion *, - unsigned short, void* any); -extern int sockunion_socket (sa_family_t family, int type, int protocol) ; -extern int sockunion_connect (int sock_fd, union sockunion *su, - unsigned short port, unsigned int) ; +extern int sockunion_cmp (sockunion su1, sockunion su2); +extern int sockunion_same (sockunion su1, sockunion su2); + +extern char* sockunion_su2str (sockunion su, enum MTYPE type) ; +extern sockunion sockunion_str2su (const char *str); +extern struct in_addr sockunion_get_in_addr (sockunion su); +extern int sockunion_accept (int sock_fd, sockunion su); +extern int sockunion_stream_socket (sockunion su); +extern int sockunion_bind (int sock_fd, sockunion su, + unsigned short port, bool any) ; +extern int sockunion_socket (sockunion su, int type, int protocol) ; +extern int sockunion_connect (int sock_fd, sockunion su, + unsigned short port, unsigned int ifindex) ; extern int sockunion_listen(int sock_fd, int backlog) ; extern int sockunion_getsockfamily(int sock_fd) ; -extern int sockunion_getsockname (int, union sockunion*); -extern int sockunion_getpeername (int, union sockunion*); -extern void sockunion_unmap_ipv4 (union sockunion *su) ; -extern void sockunion_map_ipv4 (union sockunion *su) ; +extern int sockunion_getprotofamily(int sock_fd) ; +extern int sockunion_getsockname (int sock_fd, sockunion su); +extern int sockunion_getpeername (int sock_fd, sockunion su); +extern void sockunion_unmap_ipv4 (sockunion su) ; +extern void sockunion_map_ipv4 (sockunion su) ; -extern union sockunion *sockunion_dup (union sockunion *); +extern sockunion sockunion_dup (sockunion src); extern void sockunion_copy (sockunion dst, sockunion src) ; -extern void sockunion_free (union sockunion *); +extern void sockunion_free (sockunion su); extern sockunion sockunion_new_prefix(sockunion su, prefix p) ; extern sockunion sockunion_new_sockaddr(sockunion su, struct sockaddr* sa) ; diff --git a/lib/thread.c b/lib/thread.c index 7f8ff5f6..c130d876 100644 --- a/lib/thread.c +++ b/lib/thread.c @@ -438,8 +438,7 @@ DEFUN_CALL(show_thread_cpu, } static void -cpu_record_hash_clear (struct hash_backet *bucket, - void *args) +cpu_record_hash_clear (struct hash_backet *bucket, void *args) { thread_type *filter = args; struct cpu_thread_history *a = bucket->data; @@ -1217,7 +1216,6 @@ thread_timer_process (struct thread_list *list, struct timeval *timenow) /*------------------------------------------------------------------------------ * Move the given list of threads to the back of the THREAD_READY queue. */ -/* process a list en masse, e.g. for event thread lists */ static unsigned int thread_process (struct thread_list *list) { @@ -1246,9 +1244,9 @@ thread_fetch (struct thread_master *m, struct thread *fetch) fd_set readfd; fd_set writefd; fd_set exceptfd; - struct timeval timer_val; + struct timeval timer_val ; struct timeval timer_val_bg; - struct timeval *timer_wait; + struct timeval *timer_wait ; struct timeval *timer_wait_bg; while (1) diff --git a/lib/vty_io.c b/lib/vty_io.c index 33947342..81af4e5e 100644 --- a/lib/vty_io.c +++ b/lib/vty_io.c @@ -40,6 +40,7 @@ #include "filter.h" #include "privs.h" #include "sockunion.h" +#include "sockopt.h" #include "network.h" #include <arpa/telnet.h> diff --git a/lib/vty_io_term.c b/lib/vty_io_term.c index 61f35761..0660afb6 100644 --- a/lib/vty_io_term.c +++ b/lib/vty_io_term.c @@ -41,6 +41,7 @@ #include "filter.h" #include "privs.h" #include "sockunion.h" +#include "sockopt.h" #include "network.h" #include <arpa/telnet.h> @@ -1173,8 +1174,8 @@ static int uty_term_listen_open(sa_family_t family, int type, int protocol, struct sockaddr* sa, unsigned short port) { - union sockunion su ; - int sock ; + union sockunion su[1] ; + int sock_fd ; int ret ; VTY_ASSERT_LOCKED() ; @@ -1182,55 +1183,52 @@ uty_term_listen_open(sa_family_t family, int type, int protocol, /* Is there an address and is it for this family ? */ if ((sa != NULL) || (sa->sa_family == family)) /* Set up sockunion containing required family and address */ - sockunion_new_sockaddr(&su, sa) ; + sockunion_new_sockaddr(su, sa) ; else { /* no address or wrong family -- set up empty sockunion of * required family */ - sockunion_init_new(&su, family) ; + sockunion_init_new(su, family) ; sa = NULL ; } ; /* Open the socket and set its properties */ - sock = sockunion_socket(family, type, protocol) ; - if (sock < 0) + sock_fd = sockunion_socket(su, type, protocol) ; + if (sock_fd < 0) return -1 ; - ret = sockopt_reuseaddr (sock); + ret = setsockopt_reuseaddr (sock_fd); if (ret >= 0) - ret = sockopt_reuseport (sock); + ret = setsockopt_reuseport (sock_fd); if (ret >= 0) - ret = set_nonblocking(sock); + ret = set_nonblocking(sock_fd); -#if defined(HAVE_IPV6) && defined(IPV6_V6ONLY) - /* Want only IPV6 on ipv6 socket (not mapped addresses) +#ifdef HAVE_IPV6 + /* Want only IPv6 on AF_INET6 socket (not mapped addresses) * * This distinguishes 0.0.0.0 from :: -- without this, bind() will reject the * attempt to bind to :: after binding to 0.0.0.0. */ - if ((ret >= 0) && (sa->sa_family == AF_INET6)) - { - int on = 1; - ret = setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&on, sizeof(on)); - } + if ((ret >= 0) && (family == AF_INET6)) + ret = setsockopt_ipv6_v6only(sock_fd) ; #endif if (ret >= 0) - ret = sockunion_bind (sock, &su, port, sa) ; + ret = sockunion_bind (sock_fd, su, port, (sa == NULL)) ; if (ret >= 0) - ret = sockunion_listen (sock, 3); + ret = sockunion_listen (sock_fd, 3); if (ret < 0) { - close (sock); + close (sock_fd); return -1 ; } /* Socket is open -- set VTY_TERMINAL listener going */ - uty_add_listener(sock, uty_term_accept) ; + uty_add_listener(sock_fd, uty_term_accept) ; /* Return OK and signal whether used address or not */ return (sa != NULL) ? 1 : 0 ; |