From e93640e6119e61a24d8c1f6d36b60c83b3573f90 Mon Sep 17 00:00:00 2001 From: Rhys Hanrahan Date: Wed, 22 Dec 2021 22:13:47 +1100 Subject: [PATCH] core: listen can now have a "virtual" flag to check for nonlocal floating IPs. A new option to "listen" has been added called "virtual". This sets a flag on the listening socket to modify the behaviour of grep_sock_info. When this flag is set, grep_sock_info will only consider the listening IP a match if the IP is found in the system's current list of local IP addresses. If the IP is not currently local, then the matching IP is ignored. If the virtual flag is not set on the socket then existing behaviour used instead. This is useful in scenarios with an active/active cluster where Kamailio must know if a floating IP is currently local or not. --- src/core/cfg.lex | 2 + src/core/cfg.y | 83 +++++++++++++++++++++++++++++++++++++++++ src/core/ip_addr.h | 1 + src/core/socket_info.c | 85 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 164 insertions(+), 7 deletions(-) diff --git a/src/core/cfg.lex b/src/core/cfg.lex index 9162e56d89c..6f63f5c9c30 100644 --- a/src/core/cfg.lex +++ b/src/core/cfg.lex @@ -305,6 +305,7 @@ XAVPVIAPARAMS xavp_via_params XAVPVIAFIELDS xavp_via_fields LISTEN listen ADVERTISE advertise|ADVERTISE +VIRTUAL virtual STRNAME name|NAME ALIAS alias SR_AUTO_ALIASES auto_aliases @@ -741,6 +742,7 @@ IMPORTFILE "import_file" {XAVPVIAFIELDS} { yylval.strval=yytext; return XAVPVIAFIELDS; } {LISTEN} { count(); yylval.strval=yytext; return LISTEN; } {ADVERTISE} { count(); yylval.strval=yytext; return ADVERTISE; } +{VIRTUAL} { count(); yylval.strval=yytext; return VIRTUAL; } {STRNAME} { count(); yylval.strval=yytext; return STRNAME; } {ALIAS} { count(); yylval.strval=yytext; return ALIAS; } {SR_AUTO_ALIASES} { count(); yylval.strval=yytext; diff --git a/src/core/cfg.y b/src/core/cfg.y index 2c76a4411ad..d1ba90e5446 100644 --- a/src/core/cfg.y +++ b/src/core/cfg.y @@ -328,6 +328,7 @@ extern char *default_routename; %token XAVPVIAFIELDS %token LISTEN %token ADVERTISE +%token VIRTUAL %token STRNAME %token ALIAS %token SR_AUTO_ALIASES @@ -1503,6 +1504,19 @@ assign_stm: } free_socket_id_lst($3); } + | LISTEN EQUAL id_lst VIRTUAL { + for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { + lst_tmp->flags |= SI_IS_VIRTUAL; + if (add_listen_iface( lst_tmp->addr_lst->name, + lst_tmp->addr_lst->next, + lst_tmp->port, lst_tmp->proto, + lst_tmp->flags)!=0) { + LM_CRIT("cfg. parser: failed to add listen address\n"); + break; + } + } + free_socket_id_lst($3); + } | LISTEN EQUAL id_lst STRNAME STRING { for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { if (add_listen_iface_name(lst_tmp->addr_lst->name, @@ -1515,6 +1529,19 @@ assign_stm: } free_socket_id_lst($3); } + | LISTEN EQUAL id_lst STRNAME STRING VIRTUAL { + for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { + lst_tmp->flags |= SI_IS_VIRTUAL; + if (add_listen_iface_name(lst_tmp->addr_lst->name, + lst_tmp->addr_lst->next, + lst_tmp->port, lst_tmp->proto, $5, + lst_tmp->flags)!=0) { + LM_CRIT("cfg. parser: failed to add listen address\n"); + break; + } + } + free_socket_id_lst($3); + } | LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER { for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { if (add_listen_advertise_iface( lst_tmp->addr_lst->name, @@ -1528,6 +1555,20 @@ assign_stm: } free_socket_id_lst($3); } + | LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER VIRTUAL { + for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { + lst_tmp->flags |= SI_IS_VIRTUAL; + if (add_listen_advertise_iface( lst_tmp->addr_lst->name, + lst_tmp->addr_lst->next, + lst_tmp->port, lst_tmp->proto, + $5, $7, + lst_tmp->flags)!=0) { + LM_CRIT("cfg. parser: failed to add listen address\n"); + break; + } + } + free_socket_id_lst($3); + } | LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER STRNAME STRING { for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name, @@ -1541,6 +1582,20 @@ assign_stm: } free_socket_id_lst($3); } + | LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER STRNAME STRING VIRTUAL { + for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { + lst_tmp->flags |= SI_IS_VIRTUAL; + if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name, + lst_tmp->addr_lst->next, + lst_tmp->port, lst_tmp->proto, + $5, $7, $9, + lst_tmp->flags)!=0) { + LM_CRIT("cfg. parser: failed to add listen address\n"); + break; + } + } + free_socket_id_lst($3); + } | LISTEN EQUAL id_lst ADVERTISE listen_id { for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { if (add_listen_advertise_iface( lst_tmp->addr_lst->name, @@ -1554,6 +1609,20 @@ assign_stm: } free_socket_id_lst($3); } + | LISTEN EQUAL id_lst ADVERTISE listen_id VIRTUAL { + for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { + lst_tmp->flags |= SI_IS_VIRTUAL; + if (add_listen_advertise_iface( lst_tmp->addr_lst->name, + lst_tmp->addr_lst->next, + lst_tmp->port, lst_tmp->proto, + $5, 0, + lst_tmp->flags)!=0) { + LM_CRIT("cfg. parser: failed to add listen address\n"); + break; + } + } + free_socket_id_lst($3); + } | LISTEN EQUAL id_lst ADVERTISE listen_id STRNAME STRING { for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name, @@ -1567,6 +1636,20 @@ assign_stm: } free_socket_id_lst($3); } + | LISTEN EQUAL id_lst ADVERTISE listen_id STRNAME STRING VIRTUAL { + for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) { + lst_tmp->flags |= SI_IS_VIRTUAL; + if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name, + lst_tmp->addr_lst->next, + lst_tmp->port, lst_tmp->proto, + $5, 0, $7, + lst_tmp->flags)!=0) { + LM_CRIT("cfg. parser: failed to add listen address\n"); + break; + } + } + free_socket_id_lst($3); + } | LISTEN EQUAL error { yyerror("ip address, interface name or" " hostname expected"); } | ALIAS EQUAL id_lst { diff --git a/src/core/ip_addr.h b/src/core/ip_addr.h index 132f653255f..9205def1231 100644 --- a/src/core/ip_addr.h +++ b/src/core/ip_addr.h @@ -84,6 +84,7 @@ typedef enum si_flags { SI_IS_MCAST = (1<<2), SI_IS_ANY = (1<<3), SI_IS_MHOMED = (1<<4), + SI_IS_VIRTUAL = (1<<5), } si_flags_t; typedef struct addr_info { diff --git a/src/core/socket_info.c b/src/core/socket_info.c index d55f506bcc5..8ee6cd0b320 100644 --- a/src/core/socket_info.c +++ b/src/core/socket_info.c @@ -562,6 +562,66 @@ struct socket_info** get_sock_info_list(unsigned short proto) return 0; } +/* Check list of active local IPs for grep_sock_info + * This function is only used for sockets with the SI_IS_VIRTUAL flag set. This + * is so floating (virtual) IPs that are not currently local, are not returned + * as matches by grep_sock_info. + * + * Params: + * - si - Socket info of socket that has been flagged with SI_IS_VIRTUAL, + * that we want to check if it's actually local right now. + * + * Returns 1 if socket is local, or 0 if not. + */ +static int check_local_addresses(struct socket_info* si) +{ + struct hostent* he; + struct utsname myname; + + if (si == NULL) { + LM_ERR("Socket info is NULL. Returning no match.\n"); + return 0; + } + + if (!(si->flags & SI_IS_VIRTUAL)) { + LM_ERR("Have been passed a socket without the virtual flag set. This should " + "not happen. Returning a match to maintain standard behaviour.\n"); + return 1; + } + + if (uname(&myname) <0){ + LM_ERR("Cannot determine hostname. Guessing a not local virtual IP.\n"); + return 0; + } + + //Should return a list of local IPs + he = _resolvehost(myname.nodename); + if (he == NULL) { + LM_ERR("Cannot get list of local IPs. Guessing not a local virtual IP.\n"); + return 0; + } + char** paddrlist = he->h_addr_list; + int i = 0; + while (*paddrlist != NULL) + { + struct ip_addr local_addr; + hostent2ip_addr(&local_addr, he, i); + + LM_DBG("Checking local address: %s\n", ip_addr2a(&local_addr)); + if (ip_addr_cmp(&si->address, &local_addr)) { + LM_DBG("Found matching local IP for virtual socket: %s\n", ip_addr2a(&local_addr)); + return 1; + } + + i++; + paddrlist++; + } + + //Default to not local if no match is found + LM_DBG("No matching local IP found.\n"); + return 0; +} + /* helper function for grep_sock_info * params: @@ -653,7 +713,16 @@ struct socket_info* grep_sock_info(str* host, unsigned short port, } if (si_hname_cmp(&hname, &si->name, &si->address_str, &si->address, si->flags)==0) { - goto found; + if (si->flags & SI_IS_VIRTUAL) { + LM_DBG("Checking virtual socket: [%.*s]\n", si->name.len, si->name.s); + if (check_local_addresses(si)) { + goto found; + } else { + LM_DBG("Skipping virtual socket that is not local.\n"); + } + } else { + goto found; + } } if(si->useinfo.name.s!=NULL) { LM_DBG("checking advertise if host==us:" @@ -2071,10 +2140,11 @@ void print_all_socket_lists() for (ai=si->addr_info_lst; ai; ai=ai->next) { printf(", %s", ai->address_str.s); } - printf("):%s%s%s\n", + printf("):%s%s%s%s\n", si->port_no_str.s, - si->flags & SI_IS_MCAST ? " mcast" : "", - si->flags & SI_IS_MHOMED? " mhomed" : ""); + si->flags & SI_IS_MCAST ? " mcast" : "", + si->flags & SI_IS_MHOMED ? " mhomed" : "", + si->flags & SI_IS_VIRTUAL? " virtual" : ""); }else{ printf(" %s: %s", get_valid_proto_name(proto), @@ -2082,10 +2152,11 @@ void print_all_socket_lists() if (!(si->flags & SI_IS_IP)) { printf(" [%s]", si->address_str.s); } - printf( ":%s%s%s", + printf( ":%s%s%s%s", si->port_no_str.s, - si->flags & SI_IS_MCAST ? " mcast" : "", - si->flags & SI_IS_MHOMED? " mhomed" : ""); + si->flags & SI_IS_MCAST ? " mcast" : "", + si->flags & SI_IS_MHOMED ? " mhomed" : "", + si->flags & SI_IS_VIRTUAL? " virtual" : ""); if (si->sockname.s) { printf(" name %s", si->sockname.s); }