diff --git a/client/fwknop_common.h b/client/fwknop_common.h index 52134de7..45fc75bc 100644 --- a/client/fwknop_common.h +++ b/client/fwknop_common.h @@ -87,8 +87,8 @@ typedef struct fko_cli_options int no_save_args; int use_hmac; char spa_server_str[MAX_SERVER_STR_LEN]; /* may be a hostname */ - char allow_ip_str[MAX_IPV4_STR_LEN]; - char spoof_ip_src_str[MAX_IPV4_STR_LEN]; + char allow_ip_str[MAX_IPV46_STR_LEN]; + char spoof_ip_src_str[MAX_IPV46_STR_LEN]; char spoof_user[MAX_USERNAME_LEN]; int rand_port; char gpg_recipient_key[MAX_GPG_KEY_ID]; diff --git a/client/http_resolve_host.c b/client/http_resolve_host.c index 6db56270..37f86774 100644 --- a/client/http_resolve_host.c +++ b/client/http_resolve_host.c @@ -58,13 +58,12 @@ struct url static int try_url(struct url *url, fko_cli_options_t *options) { - int sock=-1, sock_success=0, res, error, http_buf_len, i; + int sock=-1, sock_success=0, i, res, error, http_buf_len; int bytes_read = 0, position = 0; - int o1, o2, o3, o4; struct addrinfo *result=NULL, *rp, hints; char http_buf[HTTP_MAX_REQUEST_LEN] = {0}; char http_response[HTTP_MAX_RESPONSE_LEN] = {0}; - char *ndx; + char *ndx, c; #ifdef WIN32 WSADATA wsa_data; @@ -197,45 +196,46 @@ try_url(struct url *url, fko_cli_options_t *options) } ndx += 4; - /* Walk along the content to try to find the end of the IP address. - * Note: We are expecting the content to be just an IP address - * (possibly followed by whitespace or other not-digit value). + /* Walk along the content to try to find the end of the IP address. + * Note: We are expecting the content to be just an IP address + * (possibly followed by whitespace or other not-digit value). + */ + for(i=0; i= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) && c != '.' && c != ':') + break; + } + + /* Terminate at the first non-digit and non-dot. */ - for(i=0; i= 0 && o1 <= 255 - && o2 >= 0 && o2 <= 255 - && o3 >= 0 && o3 <= 255 - && o4 >= 0 && o4 <= 255) - { - strlcpy(options->allow_ip_str, ndx, sizeof(options->allow_ip_str)); - - log_msg(LOG_VERBOSITY_INFO, - "\n[+] Resolved external IP (via http://%s%s) as: %s", - url->host, - url->path, - options->allow_ip_str); + *(ndx+i) = '\0'; - return(1); - } - else + /* Try to parse the content as an IP address. */ + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */ + hints.ai_flags = AI_NUMERICHOST | AI_CANONNAME; + error = getaddrinfo(ndx, NULL, &hints, &result); + if (error != 0) { log_msg(LOG_VERBOSITY_ERROR, "[-] From http://%s%s\n Invalid IP (%s) in HTTP response:\n\n%s", url->host, url->path, ndx, http_response); return(-1); } + for (rp = result; rp != NULL; rp = rp->ai_next) { + strlcpy(options->allow_ip_str, + rp->ai_canonname, sizeof(options->allow_ip_str)); + break; + } + freeaddrinfo(result); + + log_msg(LOG_VERBOSITY_INFO, + "\n[+] Resolved external IP (via http://%s%s) as: %s", + url->host, + url->path, + options->allow_ip_str); + + return(1); } static int diff --git a/client/spa_comm.c b/client/spa_comm.c index 1afd914c..a99a8c1d 100644 --- a/client/spa_comm.c +++ b/client/spa_comm.c @@ -98,7 +98,7 @@ send_spa_packet_tcp_or_udp(const char *spa_data, const int sd_len, memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET; /* Allow IPv4 only */ + hints.ai_family = AF_UNSPEC; if (options->spa_proto == FKO_PROTO_UDP) { @@ -230,7 +230,7 @@ send_spa_packet_tcp_raw(const char *spa_data, const int sd_len, return res; } - sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); + sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_tcp_raw: create socket: ", strerror(errno)); @@ -343,7 +343,7 @@ send_spa_packet_udp_raw(const char *spa_data, const int sd_len, return res; } - sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); + sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { log_msg(LOG_VERBOSITY_ERROR, "send_spa_packet_udp_raw: create socket: ", strerror(errno)); @@ -441,7 +441,7 @@ send_spa_packet_icmp(const char *spa_data, const int sd_len, return res; } - sock = socket (PF_INET, SOCK_RAW, IPPROTO_RAW); + sock = socket (AF_INET, SOCK_RAW, IPPROTO_RAW); if (sock < 0) { diff --git a/configure.ac b/configure.ac index 873f8b3a..fa877d35 100644 --- a/configure.ac +++ b/configure.ac @@ -356,7 +356,15 @@ AC_HEADER_STDC AC_HEADER_TIME AC_HEADER_RESOLV -AC_CHECK_HEADERS([arpa/inet.h ctype.h endian.h errno.h locale.h netdb.h net/ethernet.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/byteorder.h sys/endian.h sys/ethernet.h sys/socket.h sys/stat.h sys/time.h sys/wait.h termios.h time.h unistd.h]) +AC_CHECK_HEADERS([arpa/inet.h ctype.h endian.h errno.h locale.h netdb.h net/ethernet.h netinet/in.h stdint.h stdlib.h string.h strings.h sys/byteorder.h sys/endian.h sys/ethernet.h sys/socket.h sys/stat.h sys/time.h sys/types.h sys/wait.h termios.h time.h unistd.h]) +AC_CHECK_HEADERS([netinet/ip6.h netinet/icmp6.h], [], [], +[#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_NETINET_IN_H +# include +#endif +]) # Type checks. # diff --git a/lib/fko_limits.h b/lib/fko_limits.h index 5f02cc18..2a326008 100644 --- a/lib/fko_limits.h +++ b/lib/fko_limits.h @@ -59,6 +59,12 @@ #define MAX_IPV4_STR_LEN 16 #define MIN_IPV4_STR_LEN 7 +#define MAX_IPV46_STR_LEN 40 +#define MIN_IPV46_STR_LEN 3 + +#define MAX_IPV6_STR_LEN 40 +#define MIN_IPV6_STR_LEN 3 + #define MAX_PROTO_STR_LEN 4 /* tcp, udp, icmp for now */ #define MAX_PORT_STR_LEN 5 #define MAX_PORT 65535 diff --git a/server/access.c b/server/access.c index d2abee91..b069b606 100644 --- a/server/access.c +++ b/server/access.c @@ -373,8 +373,8 @@ add_int_ent(acc_int_list_t **ilist, const char *ip) */ if(strcasecmp(ip, "ANY") == 0) { - new_sle->maddr = 0x0; - new_sle->mask = 0x0; + new_sle->family = AF_UNSPEC; + memset(&new_sle->acc_int, 0, sizeof(new_sle->acc_int)); } else { @@ -473,17 +473,18 @@ add_int_ent(acc_int_list_t **ilist, const char *ip) /* Store our mask converted from CIDR to a 32-bit value. */ + new_sle->family = AF_INET; if(mask == 32) - new_sle->mask = 0xFFFFFFFF; + new_sle->acc_int.inet.mask = 0xFFFFFFFF; else if(need_shift && (mask > 0 && mask < 32)) - new_sle->mask = (0xFFFFFFFF << (32 - mask)); + new_sle->acc_int.inet.mask = (0xFFFFFFFF << (32 - mask)); else - new_sle->mask = mask; + new_sle->acc_int.inet.mask = mask; /* Store our masked address for comparisons with future incoming * packets. */ - new_sle->maddr = ntohl(in.s_addr) & new_sle->mask; + new_sle->acc_int.inet.maddr = ntohl(in.s_addr) & new_sle->acc_int.inet.mask; } /* If this is not the first entry, we walk our pointer to the @@ -2069,23 +2070,61 @@ int valid_access_stanzas(acc_stanza_t *acc) return 1; } +static int +compare_addr_list_ipv4(acc_int_list_t *ip_list, uint32_t ip) +{ + for(; ip_list; ip_list = ip_list->next) + { + if(ip_list->family == AF_UNSPEC) + return 1; + if(ip_list->family != AF_INET6) + continue; + if((ip & ip_list->acc_int.inet.mask) == (ip_list->acc_int.inet.maddr & ip_list->acc_int.inet.mask)) + return 1; + } + return 0; +} + +static int +compare_addr_list_ipv6(acc_int_list_t *ip_list, struct in6_addr *ip6) +{ + for(; ip_list; ip_list = ip_list->next) + { + if(ip_list->family == AF_UNSPEC) + return 1; + if(ip_list->family != AF_INET6) + continue; + if(memcmp(&ip_list->acc_int.inet6.maddr, ip6, sizeof(*ip6)) == 0) + return 1; + } + return 0; +} + int -compare_addr_list(acc_int_list_t *ip_list, const uint32_t ip) +compare_addr_list(acc_int_list_t *ip_list, int family, ...) { - int match = 0; + int res; + va_list ap; + uint32_t ip; + struct in6_addr * ip6; - while(ip_list) + va_start(ap, family); + switch(family) { - if((ip & ip_list->mask) == (ip_list->maddr & ip_list->mask)) - { - match = 1; + case AF_INET: + ip = va_arg(ap, uint32_t); + res = compare_addr_list_ipv4(ip_list, ip); + break; + case AF_INET6: + ip6 = va_arg(ap, struct in6_addr *); + res = compare_addr_list_ipv6(ip_list, ip6); + break; + default: + res = 0; break; - } - - ip_list = ip_list->next; } - - return(match); + va_end(ap); + return res; } /** diff --git a/server/access.h b/server/access.h index 02dc9b19..d4fb045e 100644 --- a/server/access.h +++ b/server/access.h @@ -114,7 +114,7 @@ int valid_access_stanzas(acc_stanza_t *acc); * \return Returns true on a match * */ -int compare_addr_list(acc_int_list_t *source_list, const uint32_t ip); +int compare_addr_list(acc_int_list_t *source_list, int family, ...); /** * \brief Check for a proto-port string diff --git a/server/cmd_opts.h b/server/cmd_opts.h index a2feed5d..e51506df 100644 --- a/server/cmd_opts.h +++ b/server/cmd_opts.h @@ -181,9 +181,9 @@ enum { /* Our getopt_long options string. */ #if USE_LIBNETFILTER_QUEUE - #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:Kl:nO:p:P:Rr:StUvV" + #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:6Kl:nO:p:P:Rr:StUvV" #else - #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:Kl:O:p:P:Rr:StUvV" + #define GETOPTS_OPTION_STRING "Aa:c:C:d:Dfhi:6Kl:O:p:P:Rr:StUvV" #endif /* Our program command-line options... @@ -206,6 +206,7 @@ static struct option cmd_opts[] = {"fault-injection-tag", 1, NULL, FAULT_INJECTION_TAG}, {"help", 0, NULL, 'h'}, {"interface", 1, NULL, 'i'}, + {"ipv6", 0, NULL, '6'}, {"key-gen", 0, NULL, 'k'}, {"key-gen-file", 1, NULL, KEY_GEN_FILE }, {"key-len", 1, NULL, KEY_LEN }, diff --git a/server/config_init.c b/server/config_init.c index 7f231336..4a412905 100644 --- a/server/config_init.c +++ b/server/config_init.c @@ -1372,6 +1372,9 @@ config_init(fko_srv_options_t *opts, int argc, char **argv) case 'i': set_config_entry(opts, CONF_PCAP_INTF, optarg); break; + case '6': + opts->ipv6 = 1; + break; case FIREWD_DISABLE_CHECK_SUPPORT: opts->firewd_disable_check_support = 1; break; @@ -1498,6 +1501,7 @@ usage(void) " a background daemon).\n" " -i, --interface - Specify interface to listen for incoming SPA\n" " packets.\n" + " -6, --ipv6 - Start the server in IPv6 mode (TCP/UDP).\n" " -C, --packet-limit - Limit the number of candidate SPA packets to\n" " process and exit when this limit is reached.\n" " -d, --digest-file - Specify an alternate digest.cache file.\n" diff --git a/server/fwknopd.c b/server/fwknopd.c index 55e9c60d..7956d6b0 100644 --- a/server/fwknopd.c +++ b/server/fwknopd.c @@ -259,7 +259,7 @@ main(int argc, char **argv) if(opts.enable_udp_server || strncasecmp(opts.config[CONF_ENABLE_UDP_SERVER], "Y", 1) == 0) { - if(run_udp_server(&opts) < 0) + if(run_udp_server(&opts, opts.ipv6 ? AF_INET6 : AF_INET) < 0) { log_msg(LOG_ERR, "Fatal run_udp_server() error"); clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE); @@ -280,7 +280,7 @@ main(int argc, char **argv) */ if(strncasecmp(opts.config[CONF_ENABLE_TCP_SERVER], "Y", 1) == 0) { - if(run_tcp_server(&opts) < 0) + if(run_tcp_server(&opts, opts.ipv6 ? AF_INET6 : AF_INET) < 0) { log_msg(LOG_ERR, "Fatal run_tcp_server() error"); clean_exit(&opts, FW_CLEANUP, EXIT_FAILURE); diff --git a/server/fwknopd_common.h b/server/fwknopd_common.h index 29959952..6c1396a3 100644 --- a/server/fwknopd_common.h +++ b/server/fwknopd_common.h @@ -358,8 +358,20 @@ enum { */ typedef struct acc_int_list { - unsigned int maddr; - unsigned int mask; + int family; + union + { + struct + { + unsigned int maddr; + unsigned int mask; + } inet; + struct + { + struct in6_addr maddr; + unsigned int prefix; + } inet6; + } acc_int; struct acc_int_list *next; } acc_int_list_t; @@ -600,12 +612,28 @@ typedef struct spa_pkt_info { unsigned int packet_data_len; unsigned int packet_proto; - unsigned int packet_src_ip; - unsigned int packet_dst_ip; + unsigned int packet_family; + union + { + struct + { + unsigned int src_ip; + unsigned int dst_ip; + } inet; +#if HAVE_NETINET_IP6_H + struct + { + struct in6_addr src_ip; + struct in6_addr dst_ip; + } inet6; +#endif + } packet_addr; unsigned short packet_src_port; unsigned short packet_dst_port; unsigned char packet_data[MAX_SPA_PACKET_LEN+1]; } spa_pkt_info_t; +#define packet_src_ip packet_addr.inet.src_ip +#define packet_dst_ip packet_addr.inet.dst_ip /* Struct for (processed and verified) SPA data used by the server. */ @@ -616,10 +644,10 @@ typedef struct spa_data char *version; short message_type; char *spa_message; - char spa_message_src_ip[MAX_IPV4_STR_LEN]; - char pkt_source_ip[MAX_IPV4_STR_LEN]; - char pkt_source_xff_ip[MAX_IPV4_STR_LEN]; - char pkt_destination_ip[MAX_IPV4_STR_LEN]; + char spa_message_src_ip[MAX_IPV46_STR_LEN]; + char pkt_source_ip[MAX_IPV46_STR_LEN]; + char pkt_source_xff_ip[MAX_IPV46_STR_LEN]; + char pkt_destination_ip[MAX_IPV46_STR_LEN]; char spa_message_remain[1024]; /* --DSS FIXME: arbitrary bounds */ char *nat_access; char *server_auth; @@ -657,6 +685,7 @@ typedef struct fko_srv_options unsigned char enable_nfq_capture; /* Enable Netfilter Queue capture mode */ unsigned char enable_fw; /* Command modes by themselves don't need firewall support. */ + unsigned char ipv6; /* Enable IPv6 mode (TCP/UDP) */ unsigned char firewd_disable_check_support; /* Don't use firewall-cmd ... -C */ unsigned char ipt_disable_check_support; /* Don't use iptables -C */ diff --git a/server/incoming_spa.c b/server/incoming_spa.c index 968162c9..34964fb0 100644 --- a/server/incoming_spa.c +++ b/server/incoming_spa.c @@ -254,7 +254,7 @@ get_raw_digest(char **digest, char *pkt_data) return res; } -/* Popluate a spa_data struct from an initialized (and populated) FKO context. +/* Populate a spa_data struct from an initialized (and populated) FKO context. */ static int get_spa_data_fields(fko_ctx_t ctx, spa_data_t *spdat) @@ -347,14 +347,28 @@ check_stanza_expiration(acc_stanza_t *acc, spa_data_t *spadat, * source IP */ static int -is_src_match(acc_stanza_t *acc, const uint32_t ip) +is_src_match(acc_stanza_t *acc, const spa_pkt_info_t *spa_pkt) { - while (acc) + switch (spa_pkt->packet_family) { - if(compare_addr_list(acc->source_list, ip)) - return 1; + case AF_INET: + while (acc) + { + if(compare_addr_list(acc->source_list, AF_INET, ntohl(spa_pkt->packet_addr.inet.src_ip))) + return 1; - acc = acc->next; + acc = acc->next; + } + break; + case AF_INET6: + while (acc) + { + if(compare_addr_list(acc->source_list, AF_INET6, &spa_pkt->packet_addr.inet6.src_ip)) + return 1; + + acc = acc->next; + } + break; } return 0; } @@ -363,7 +377,7 @@ static int src_check(fko_srv_options_t *opts, spa_pkt_info_t *spa_pkt, spa_data_t *spadat, char **raw_digest) { - if (is_src_match(opts->acc_stanzas, ntohl(spa_pkt->packet_src_ip))) + if (is_src_match(opts->acc_stanzas, spa_pkt)) { if(strncasecmp(opts->config[CONF_ENABLE_DIGEST_PERSISTENCE], "Y", 1) == 0) { @@ -427,9 +441,9 @@ static int src_dst_check(acc_stanza_t *acc, spa_pkt_info_t *spa_pkt, spa_data_t *spadat, const int stanza_num) { - if(! compare_addr_list(acc->source_list, ntohl(spa_pkt->packet_src_ip)) || + if(! compare_addr_list(acc->source_list, AF_INET, ntohl(spa_pkt->packet_src_ip)) || (acc->destination_list != NULL - && ! compare_addr_list(acc->destination_list, ntohl(spa_pkt->packet_dst_ip)))) + && ! compare_addr_list(acc->destination_list, AF_INET, ntohl(spa_pkt->packet_dst_ip)))) { log_msg(LOG_DEBUG, "(stanza #%d) SPA packet (%s -> %s) filtered by SOURCE and/or DESTINATION criteria", @@ -930,11 +944,25 @@ incoming_spa(fko_srv_options_t *opts) */ acc_stanza_t *acc = opts->acc_stanzas; - inet_ntop(AF_INET, &(spa_pkt->packet_src_ip), - spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); + switch(spa_pkt->packet_family) + { + case AF_INET: + inet_ntop(AF_INET, &(spa_pkt->packet_addr.inet.src_ip), + spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); - inet_ntop(AF_INET, &(spa_pkt->packet_dst_ip), - spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); + inet_ntop(AF_INET, &(spa_pkt->packet_addr.inet.dst_ip), + spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); + break; + case AF_INET6: + inet_ntop(AF_INET6, &(spa_pkt->packet_addr.inet6.src_ip), + spadat.pkt_source_ip, sizeof(spadat.pkt_source_ip)); + + inet_ntop(AF_INET6, &(spa_pkt->packet_addr.inet6.dst_ip), + spadat.pkt_destination_ip, sizeof(spadat.pkt_destination_ip)); + break; + default: + return; + } /* At this point, we want to validate and (if needed) preprocess the * SPA data and/or to be reasonably sure we have a SPA packet (i.e diff --git a/server/pcap_capture.c b/server/pcap_capture.c index e25904b5..f32aeca8 100644 --- a/server/pcap_capture.c +++ b/server/pcap_capture.c @@ -211,7 +211,7 @@ pcap_capture(fko_srv_options_t *opts) /* Attempt to restart tcp server ? */ usleep(1000000); - run_tcp_server(opts); + run_tcp_server(opts, opts->ipv6 ? AF_INET6 : AF_INET); } } diff --git a/server/process_packet.c b/server/process_packet.c index 99fdc07c..8231af99 100644 --- a/server/process_packet.c +++ b/server/process_packet.c @@ -41,54 +41,65 @@ #include "log_msg.h" +static void +process_packet_ethernet(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen, int offset); +static void +process_packet_ipv4(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end); +static void +process_packet_ipv6(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end); +static void +process_packet_loop(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen); +static void +process_packet_raw(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen); + + void process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, const unsigned char *packet) { - struct ether_header *eth_p; - struct iphdr *iph_p; - struct tcphdr *tcph_p; - struct udphdr *udph_p; - struct icmphdr *icmph_p; - - unsigned char *pkt_data; - unsigned short pkt_data_len; - unsigned char *pkt_end; - unsigned char *fr_end; - - unsigned int ip_hdr_words; + fko_srv_options_t *opts = (fko_srv_options_t *)args; - unsigned char proto; - unsigned int src_ip; - unsigned int dst_ip; + int offset = opts->data_link_offset; - unsigned short src_port = 0; - unsigned short dst_port = 0; +#if USE_LIBPCAP + if (offset == sizeof(struct ether_header)) + process_packet_ethernet(opts, packet, packet_header->len, + packet_header->caplen, offset); + else if (offset == 4) + process_packet_loop(opts, packet, packet_header->len, + packet_header->caplen); + else if (offset == 0) + process_packet_raw(opts, packet, packet_header->len, + packet_header->caplen); +#else + process_packet_raw(opts, packet, pkt_len, pkt_len); +#endif +} - unsigned short eth_type; - fko_srv_options_t *opts = (fko_srv_options_t *)args; +static void +process_packet_ethernet(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen, int offset) +{ + struct ether_header *eth_p; - int offset = opts->data_link_offset; + unsigned char *fr_end; -#if USE_LIBPCAP - unsigned short pkt_len = packet_header->len; + unsigned short eth_type; /* Gotta have a complete ethernet header. */ - if (packet_header->caplen < ETHER_HDR_LEN) + if (caplen < ETHER_HDR_LEN) return; /* Determine packet end. */ - fr_end = (unsigned char *) packet + packet_header->caplen; -#else - /* This is coming from NFQ and we get the packet lentgh as an arg. - */ - if (pkt_len < ETHER_HDR_LEN) - return; - fr_end = (unsigned char *) packet + pkt_len; -#endif + fr_end = (unsigned char *) packet + caplen; /* This is a hack to determine if we are using the linux cooked * interface. We base it on the offset being 16 which is the @@ -127,6 +138,35 @@ process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, if (! ETHER_IS_VALID_LEN(pkt_len) ) return; + if (eth_type == ETHERTYPE_IP) + process_packet_ipv4(opts, packet, offset, fr_end); + else if (eth_type == ETHERTYPE_IPV6) + process_packet_ipv6(opts, packet, offset, fr_end); +} + + +static void +process_packet_ipv4(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end) +{ + struct iphdr *iph_p; + struct tcphdr *tcph_p; + struct udphdr *udph_p; + struct icmphdr *icmph_p; + + unsigned char *pkt_data; + unsigned short pkt_data_len; + unsigned char *pkt_end; + + unsigned int ip_hdr_words; + + unsigned char proto; + unsigned int src_ip; + unsigned int dst_ip; + + unsigned short src_port = 0; + unsigned short dst_port = 0; + /* Pull the IP header. */ iph_p = (struct iphdr*)(packet + offset); @@ -223,14 +263,74 @@ process_packet(PROCESS_PKT_ARGS_TYPE *args, PACKET_HEADER_META, strlcpy((char *)opts->spa_pkt.packet_data, (char *)pkt_data, pkt_data_len+1); opts->spa_pkt.packet_data_len = pkt_data_len; opts->spa_pkt.packet_proto = proto; + opts->spa_pkt.packet_family = AF_INET; opts->spa_pkt.packet_src_ip = src_ip; opts->spa_pkt.packet_dst_ip = dst_ip; opts->spa_pkt.packet_src_port = src_port; opts->spa_pkt.packet_dst_port = dst_port; incoming_spa(opts); +} + + +static void +process_packet_ipv6(fko_srv_options_t * opts, const unsigned char * packet, + int offset, unsigned char const * fr_end) +{ + /* FIXME implement */ +} + + +static void +process_packet_loop(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen) +{ + uint32_t family; + + if (caplen < sizeof(family)) + return; + + memcpy(&family, packet, sizeof(family)); - return; + if (family == AF_INET) + process_packet_ipv4(opts, packet, 4, packet + caplen); + else if (family == AF_INET6) + process_packet_ipv6(opts, packet, 4, packet + caplen); +} + + +static void +process_packet_raw(fko_srv_options_t * opts, const unsigned char * packet, + unsigned short pkt_len, int caplen) +{ + struct iphdr *iph_p; + + unsigned char const *fr_end; + + /* Gotta have a complete ethernet header. + */ + if (caplen < ETHER_HDR_LEN) + return; + + /* Determine packet end. + */ + fr_end = (unsigned char *) packet + caplen; + + /* Tentatively pull an IP header. + */ + iph_p = (struct iphdr*)packet; + + /* If IP header is past calculated packet end, bail. + */ + if ((unsigned char*)(iph_p + 1) > fr_end) + return; + + /* Obtain the IP version. + */ + if (iph_p->version == 6) + process_packet_ipv6(opts, packet, 0, fr_end); + else + process_packet_ipv4(opts, packet, 0, fr_end); } diff --git a/server/tcp_server.c b/server/tcp_server.c index 7ba55bf1..6eaff064 100644 --- a/server/tcp_server.c +++ b/server/tcp_server.c @@ -51,17 +51,20 @@ * the child process or -1 if there is a fork error. */ int -run_tcp_server(fko_srv_options_t *opts) +run_tcp_server(fko_srv_options_t *opts, const int family) { #if !CODE_COVERAGE pid_t pid, ppid; #endif - int s_sock, c_sock, sfd_flags, clen, selval; + int s_sock, c_sock, sfd_flags, selval; + socklen_t slen, clen; int reuse_addr = 1, rv=1; fd_set sfd_set; - struct sockaddr_in saddr, caddr; + struct sockaddr_in saddr4, caddr4; + struct sockaddr_in6 saddr6, caddr6; + struct sockaddr *saddr, *caddr; struct timeval tv; - char sipbuf[MAX_IPV4_STR_LEN] = {0}; + char sipbuf[MAX_IPV46_STR_LEN] = {0}; log_msg(LOG_INFO, "Kicking off TCP server to listen on port %i.", opts->tcpserv_port); @@ -94,7 +97,7 @@ run_tcp_server(fko_srv_options_t *opts) /* Now, let's make a TCP server */ - if ((s_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + if ((s_sock = socket(family, SOCK_STREAM, IPPROTO_TCP)) < 0) { log_msg(LOG_ERR, "run_tcp_server: socket() failed: %s", strerror(errno)); @@ -135,13 +138,34 @@ run_tcp_server(fko_srv_options_t *opts) #endif /* Construct local address structure */ - memset(&saddr, 0, sizeof(saddr)); - saddr.sin_family = AF_INET; /* Internet address family */ - saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ - saddr.sin_port = htons(opts->tcpserv_port); /* Local port */ + if(family == AF_INET) + { + memset(&saddr4, 0, sizeof(saddr4)); + saddr4.sin_family = family; /* Internet address family */ + saddr4.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + saddr4.sin_port = htons(opts->tcpserv_port); /* Local port */ + saddr = (struct sockaddr *)&saddr4; + slen = sizeof(saddr4); + caddr = (struct sockaddr *)&caddr4; + } else if(family == AF_INET6) { + memset(&saddr6, 0, sizeof(saddr6)); + saddr6.sin6_family = family; /* Internet address family */ + saddr6.sin6_addr = in6addr_any; /* Any incoming interface */ + saddr6.sin6_port = htons(opts->tcpserv_port); /* Local port */ + saddr = (struct sockaddr *)&saddr6; + slen = sizeof(saddr6); + caddr = (struct sockaddr *)&caddr6; + } + else + { + log_msg(LOG_ERR, "run_tcp_server: unsupported protocol family (%d)", + family); + close(s_sock); + return -1; + } /* Bind to the local address */ - if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) + if (bind(s_sock, saddr, slen) < 0) { log_msg(LOG_ERR, "run_tcp_server: bind() failed: %s", strerror(errno)); @@ -172,8 +196,6 @@ run_tcp_server(fko_srv_options_t *opts) */ while(1) { - clen = sizeof(caddr); - /* Initialize and setup the socket for select. */ FD_SET(s_sock, &sfd_set); @@ -215,7 +237,8 @@ run_tcp_server(fko_srv_options_t *opts) /* Wait for a client to connect */ - if((c_sock = accept(s_sock, (struct sockaddr *) &caddr, (socklen_t *)&clen)) < 0) + clen = (family == AF_INET) ? sizeof(caddr) : sizeof(caddr6); + if((c_sock = accept(s_sock, caddr, &clen)) < 0) { log_msg(LOG_ERR, "run_tcp_server: accept() failed: %s", strerror(errno)); @@ -225,8 +248,11 @@ run_tcp_server(fko_srv_options_t *opts) if(opts->verbose) { - memset(sipbuf, 0x0, MAX_IPV4_STR_LEN); - inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN); + memset(sipbuf, 0, sizeof(sipbuf)); + if(family == AF_INET) + inet_ntop(family, &caddr4.sin_addr.s_addr, sipbuf, sizeof(sipbuf)); + else if(family == AF_INET6) + inet_ntop(family, &caddr6.sin6_addr, sipbuf, sizeof(sipbuf)); log_msg(LOG_INFO, "tcp_server: Got TCP connection from %s.", sipbuf); } diff --git a/server/tcp_server.h b/server/tcp_server.h index 13db296e..f4264ed7 100644 --- a/server/tcp_server.h +++ b/server/tcp_server.h @@ -32,7 +32,7 @@ /* Function prototypes */ -int run_tcp_server(fko_srv_options_t *opts); +int run_tcp_server(fko_srv_options_t *opts, const int family); #endif /* TCP_SERVER_H */ diff --git a/server/udp_server.c b/server/udp_server.c index d5e837fe..fa77cbc7 100644 --- a/server/udp_server.c +++ b/server/udp_server.c @@ -50,14 +50,15 @@ #include int -run_udp_server(fko_srv_options_t *opts) +run_udp_server(fko_srv_options_t *opts, const int family) { int s_sock, sfd_flags, selval, pkt_len; int rv=1, chk_rm_all=0; fd_set sfd_set; struct sockaddr_in saddr, caddr; + struct sockaddr_in6 saddr6, caddr6; struct timeval tv; - char sipbuf[MAX_IPV4_STR_LEN] = {0}; + char sipbuf[MAX_IPV46_STR_LEN] = {0}; char dgram_msg[MAX_SPA_PACKET_LEN+1] = {0}; socklen_t clen; @@ -66,7 +67,7 @@ run_udp_server(fko_srv_options_t *opts) /* Now, let's make a UDP server */ - if ((s_sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) + if ((s_sock = socket(family, SOCK_DGRAM, 0)) < 0) { log_msg(LOG_ERR, "run_udp_server: socket() failed: %s", strerror(errno)); @@ -95,13 +96,29 @@ run_udp_server(fko_srv_options_t *opts) } /* Construct local address structure */ - memset(&saddr, 0x0, sizeof(saddr)); - saddr.sin_family = AF_INET; /* Internet address family */ - saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ - saddr.sin_port = htons(opts->udpserv_port); /* Local port */ + if(family == AF_INET) + { + memset(&saddr, 0, sizeof(saddr)); + saddr.sin_family = family; /* Internet address family */ + saddr.sin_addr.s_addr = htonl(INADDR_ANY); /* Any incoming interface */ + saddr.sin_port = htons(opts->udpserv_port); /* Local port */ + } else if(family == AF_INET6) { + memset(&saddr6, 0, sizeof(saddr6)); + saddr6.sin6_family = family; /* Internet address family */ + saddr6.sin6_addr = in6addr_any; /* Any incoming interface */ + saddr6.sin6_port = htons(opts->udpserv_port); /* Local port */ + } + else + { + log_msg(LOG_ERR, "run_udp_server: unsupported protocol family (%d)", + family); + close(s_sock); + return -1; + } /* Bind to the local address */ - if (bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) + if ((family == AF_INET && bind(s_sock, (struct sockaddr *) &saddr, sizeof(saddr)) < 0) + || (family == AF_INET6 && bind(s_sock, (struct sockaddr *) &saddr6, sizeof(saddr6)) < 0)) { log_msg(LOG_ERR, "run_udp_server: bind() failed: %s", strerror(errno)); @@ -184,17 +201,27 @@ run_udp_server(fko_srv_options_t *opts) /* If we make it here then there is a datagram to process */ - clen = sizeof(caddr); - - pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN, - 0, (struct sockaddr *)&caddr, &clen); + if(family == AF_INET) { + clen = sizeof(caddr); + pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN, + 0, (struct sockaddr *)&caddr, &clen); + } + else if(family == AF_INET6) + { + clen = sizeof(caddr6); + pkt_len = recvfrom(s_sock, dgram_msg, MAX_SPA_PACKET_LEN, + 0, (struct sockaddr *)&caddr6, &clen); + } dgram_msg[pkt_len] = 0x0; if(opts->verbose) { - memset(sipbuf, 0x0, MAX_IPV4_STR_LEN); - inet_ntop(AF_INET, &(caddr.sin_addr.s_addr), sipbuf, MAX_IPV4_STR_LEN); + memset(sipbuf, 0, sizeof(sipbuf)); + if(family == AF_INET) + inet_ntop(family, &caddr.sin_addr.s_addr, sipbuf, sizeof(sipbuf)); + else if(family == AF_INET6) + inet_ntop(family, &caddr6.sin6_addr, sipbuf, sizeof(sipbuf)); log_msg(LOG_INFO, "udp_server: Got UDP datagram (%d bytes) from: %s", pkt_len, sipbuf); } @@ -208,15 +235,26 @@ run_udp_server(fko_srv_options_t *opts) strlcpy((char *)opts->spa_pkt.packet_data, dgram_msg, pkt_len+1); opts->spa_pkt.packet_data_len = pkt_len; opts->spa_pkt.packet_proto = IPPROTO_UDP; - opts->spa_pkt.packet_src_ip = caddr.sin_addr.s_addr; - opts->spa_pkt.packet_dst_ip = saddr.sin_addr.s_addr; - opts->spa_pkt.packet_src_port = ntohs(caddr.sin_port); - opts->spa_pkt.packet_dst_port = ntohs(saddr.sin_port); + opts->spa_pkt.packet_family = family; + if(family == AF_INET) + { + opts->spa_pkt.packet_src_ip = caddr.sin_addr.s_addr; + opts->spa_pkt.packet_dst_ip = saddr.sin_addr.s_addr; + opts->spa_pkt.packet_src_port = ntohs(caddr.sin_port); + opts->spa_pkt.packet_dst_port = ntohs(saddr.sin_port); + } + else if(family == AF_INET6) + { + opts->spa_pkt.packet_addr.inet6.src_ip = caddr6.sin6_addr; + opts->spa_pkt.packet_addr.inet6.dst_ip = saddr6.sin6_addr; + opts->spa_pkt.packet_src_port = ntohs(caddr6.sin6_port); + opts->spa_pkt.packet_dst_port = ntohs(saddr6.sin6_port); + } incoming_spa(opts); } - memset(dgram_msg, 0x0, sizeof(dgram_msg)); + memset(dgram_msg, 0, sizeof(dgram_msg)); opts->packet_ctr += 1; if(opts->foreground == 1 && opts->verbose > 2) diff --git a/server/udp_server.h b/server/udp_server.h index dcbdfec2..29ffb685 100644 --- a/server/udp_server.h +++ b/server/udp_server.h @@ -32,7 +32,7 @@ /* Function prototypes */ -int run_udp_server(fko_srv_options_t *opts); +int run_udp_server(fko_srv_options_t *opts, const int family); #endif /* UDP_SERVER_H */