Browse code

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.

Rhys Hanrahan authored on 22/12/2021 11:13:47 • Daniel-Constantin Mierla committed on 07/01/2022 11:27:25
Showing 4 changed files
... ...
@@ -305,6 +305,7 @@ XAVPVIAPARAMS	xavp_via_params
305 305
 XAVPVIAFIELDS	xavp_via_fields
306 306
 LISTEN		listen
307 307
 ADVERTISE	advertise|ADVERTISE
308
+VIRTUAL		virtual
308 309
 STRNAME		name|NAME
309 310
 ALIAS		alias
310 311
 SR_AUTO_ALIASES	auto_aliases
... ...
@@ -741,6 +742,7 @@ IMPORTFILE      "import_file"
741 742
 <INITIAL>{XAVPVIAFIELDS}	{ yylval.strval=yytext; return XAVPVIAFIELDS; }
742 743
 <INITIAL>{LISTEN}	{ count(); yylval.strval=yytext; return LISTEN; }
743 744
 <INITIAL>{ADVERTISE}	{ count(); yylval.strval=yytext; return ADVERTISE; }
745
+<INITIAL>{VIRTUAL}	{ count(); yylval.strval=yytext; return VIRTUAL; }
744 746
 <INITIAL>{STRNAME}	{ count(); yylval.strval=yytext; return STRNAME; }
745 747
 <INITIAL>{ALIAS}	{ count(); yylval.strval=yytext; return ALIAS; }
746 748
 <INITIAL>{SR_AUTO_ALIASES}	{ count(); yylval.strval=yytext;
... ...
@@ -328,6 +328,7 @@ extern char *default_routename;
328 328
 %token XAVPVIAFIELDS
329 329
 %token LISTEN
330 330
 %token ADVERTISE
331
+%token VIRTUAL
331 332
 %token STRNAME
332 333
 %token ALIAS
333 334
 %token SR_AUTO_ALIASES
... ...
@@ -1503,6 +1504,19 @@ assign_stm:
1503 1504
 		}
1504 1505
 		free_socket_id_lst($3);
1505 1506
 	}
1507
+        | LISTEN EQUAL id_lst VIRTUAL {
1508
+                for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1509
+			lst_tmp->flags |= SI_IS_VIRTUAL;
1510
+                        if (add_listen_iface(   lst_tmp->addr_lst->name,
1511
+                                                                        lst_tmp->addr_lst->next,
1512
+                                                                        lst_tmp->port, lst_tmp->proto,
1513
+                                                                        lst_tmp->flags)!=0) {
1514
+                                LM_CRIT("cfg. parser: failed to add listen address\n");
1515
+                                break;
1516
+                        }
1517
+                }
1518
+                free_socket_id_lst($3);
1519
+        }
1506 1520
 	| LISTEN EQUAL id_lst STRNAME STRING {
1507 1521
 		for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1508 1522
 			if (add_listen_iface_name(lst_tmp->addr_lst->name,
... ...
@@ -1515,6 +1529,19 @@ assign_stm:
1515 1529
 		}
1516 1530
 		free_socket_id_lst($3);
1517 1531
 	}
1532
+        | LISTEN EQUAL id_lst STRNAME STRING VIRTUAL {
1533
+                for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1534
+                        lst_tmp->flags |= SI_IS_VIRTUAL;
1535
+                        if (add_listen_iface_name(lst_tmp->addr_lst->name,
1536
+                                                                        lst_tmp->addr_lst->next,
1537
+                                                                        lst_tmp->port, lst_tmp->proto, $5,
1538
+                                                                        lst_tmp->flags)!=0) {
1539
+                                LM_CRIT("cfg. parser: failed to add listen address\n");
1540
+                                break;
1541
+                        }
1542
+                }
1543
+                free_socket_id_lst($3);
1544
+        }
1518 1545
 	| LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER {
1519 1546
 		for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1520 1547
 			if (add_listen_advertise_iface(	lst_tmp->addr_lst->name,
... ...
@@ -1528,6 +1555,20 @@ assign_stm:
1528 1555
 		}
1529 1556
 		free_socket_id_lst($3);
1530 1557
 	}
1558
+        | LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER VIRTUAL {
1559
+                for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1560
+			lst_tmp->flags |= SI_IS_VIRTUAL;
1561
+                        if (add_listen_advertise_iface( lst_tmp->addr_lst->name,
1562
+                                                                        lst_tmp->addr_lst->next,
1563
+                                                                        lst_tmp->port, lst_tmp->proto,
1564
+                                                                        $5, $7,
1565
+                                                                        lst_tmp->flags)!=0) {
1566
+                                LM_CRIT("cfg. parser: failed to add listen address\n");
1567
+                                break;
1568
+                        }
1569
+                }
1570
+                free_socket_id_lst($3);
1571
+        }
1531 1572
 	| LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER STRNAME STRING {
1532 1573
 		for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1533 1574
 			if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name,
... ...
@@ -1541,6 +1582,20 @@ assign_stm:
1541 1582
 		}
1542 1583
 		free_socket_id_lst($3);
1543 1584
 	}
1585
+        | LISTEN EQUAL id_lst ADVERTISE listen_id COLON NUMBER STRNAME STRING VIRTUAL {
1586
+                for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1587
+			lst_tmp->flags |= SI_IS_VIRTUAL;
1588
+                        if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name,
1589
+                                                                        lst_tmp->addr_lst->next,
1590
+                                                                        lst_tmp->port, lst_tmp->proto,
1591
+                                                                        $5, $7, $9,
1592
+                                                                        lst_tmp->flags)!=0) {
1593
+                                LM_CRIT("cfg. parser: failed to add listen address\n");
1594
+                                break;
1595
+                        }
1596
+                }
1597
+                free_socket_id_lst($3);
1598
+        }
1544 1599
 	| LISTEN EQUAL id_lst ADVERTISE listen_id {
1545 1600
 		for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1546 1601
 			if (add_listen_advertise_iface(	lst_tmp->addr_lst->name,
... ...
@@ -1554,6 +1609,20 @@ assign_stm:
1554 1609
 		}
1555 1610
 		free_socket_id_lst($3);
1556 1611
 	}
1612
+        | LISTEN EQUAL id_lst ADVERTISE listen_id VIRTUAL {
1613
+                for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1614
+			lst_tmp->flags |= SI_IS_VIRTUAL;
1615
+                        if (add_listen_advertise_iface( lst_tmp->addr_lst->name,
1616
+                                                                        lst_tmp->addr_lst->next,
1617
+                                                                        lst_tmp->port, lst_tmp->proto,
1618
+                                                                        $5, 0,
1619
+                                                                        lst_tmp->flags)!=0) {
1620
+                                LM_CRIT("cfg. parser: failed to add listen address\n");
1621
+                                break;
1622
+                        }
1623
+                }
1624
+                free_socket_id_lst($3);
1625
+        }
1557 1626
 	| LISTEN EQUAL id_lst ADVERTISE listen_id STRNAME STRING {
1558 1627
 		for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1559 1628
 			if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name,
... ...
@@ -1567,6 +1636,20 @@ assign_stm:
1567 1636
 		}
1568 1637
 		free_socket_id_lst($3);
1569 1638
 	}
1639
+        | LISTEN EQUAL id_lst ADVERTISE listen_id STRNAME STRING VIRTUAL {
1640
+                for(lst_tmp=$3; lst_tmp; lst_tmp=lst_tmp->next) {
1641
+			lst_tmp->flags |= SI_IS_VIRTUAL;
1642
+                        if (add_listen_advertise_iface_name(lst_tmp->addr_lst->name,
1643
+                                                                        lst_tmp->addr_lst->next,
1644
+                                                                        lst_tmp->port, lst_tmp->proto,
1645
+                                                                        $5, 0, $7,
1646
+                                                                        lst_tmp->flags)!=0) {
1647
+                                LM_CRIT("cfg. parser: failed to add listen address\n");
1648
+                                break;
1649
+                        }
1650
+                }
1651
+                free_socket_id_lst($3);
1652
+        }
1570 1653
 	| LISTEN EQUAL  error { yyerror("ip address, interface name or"
1571 1654
 									" hostname expected"); }
1572 1655
 	| ALIAS EQUAL  id_lst {
... ...
@@ -84,6 +84,7 @@ typedef enum si_flags {
84 84
 	SI_IS_MCAST     = (1<<2),
85 85
 	SI_IS_ANY       = (1<<3),
86 86
 	SI_IS_MHOMED    = (1<<4),
87
+	SI_IS_VIRTUAL	= (1<<5),
87 88
 } si_flags_t;
88 89
 
89 90
 typedef struct addr_info {
... ...
@@ -562,6 +562,66 @@ struct socket_info** get_sock_info_list(unsigned short proto)
562 562
 	return 0;
563 563
 }
564 564
 
565
+/* Check list of active local IPs for grep_sock_info
566
+ * This function is only used for sockets with the SI_IS_VIRTUAL flag set. This
567
+ * is so floating (virtual) IPs that are not currently local, are not returned
568
+ * as matches by grep_sock_info.
569
+ *
570
+ * Params:
571
+ * - si - Socket info of socket that has been flagged with SI_IS_VIRTUAL, 
572
+ *   that we want to check if it's actually local right now.
573
+ *
574
+ * Returns 1 if socket is local, or 0 if not.
575
+ */
576
+static int check_local_addresses(struct socket_info* si)
577
+{
578
+	struct hostent* he;
579
+	struct utsname myname;
580
+
581
+	if (si == NULL) {
582
+		LM_ERR("Socket info is NULL. Returning no match.\n");
583
+		return 0;
584
+	}
585
+
586
+	if (!(si->flags & SI_IS_VIRTUAL)) {
587
+		LM_ERR("Have been passed a socket without the virtual flag set. This should "
588
+			"not happen. Returning a match to maintain standard behaviour.\n");
589
+		return 1;
590
+	}
591
+
592
+	if (uname(&myname) <0){
593
+		LM_ERR("Cannot determine hostname. Guessing a not local virtual IP.\n");
594
+		return 0;
595
+	}
596
+	
597
+	//Should return a list of local IPs
598
+	he = _resolvehost(myname.nodename);
599
+	if (he == NULL) {
600
+		LM_ERR("Cannot get list of local IPs. Guessing not a local virtual IP.\n");
601
+		return 0;
602
+	}
603
+	char** paddrlist = he->h_addr_list;
604
+	int i = 0;
605
+	while (*paddrlist != NULL)
606
+	{
607
+		struct ip_addr local_addr;
608
+		hostent2ip_addr(&local_addr, he, i);
609
+
610
+		LM_DBG("Checking local address: %s\n", ip_addr2a(&local_addr));
611
+		if (ip_addr_cmp(&si->address, &local_addr)) {
612
+			LM_DBG("Found matching local IP for virtual socket: %s\n", ip_addr2a(&local_addr));
613
+			return 1;
614
+		}
615
+
616
+		i++;
617
+		paddrlist++;
618
+	}
619
+
620
+	//Default to not local if no match is found
621
+	LM_DBG("No matching local IP found.\n");
622
+	return 0;
623
+}
624
+
565 625
 
566 626
 /* helper function for grep_sock_info
567 627
  * params:
... ...
@@ -653,7 +713,16 @@ retry:
653 713
 			}
654 714
 			if (si_hname_cmp(&hname, &si->name, &si->address_str,
655 715
 								&si->address, si->flags)==0) {
656
-				goto found;
716
+				if (si->flags & SI_IS_VIRTUAL) {
717
+					LM_DBG("Checking virtual socket: [%.*s]\n", si->name.len, si->name.s);
718
+					if (check_local_addresses(si)) {
719
+						goto found;
720
+					} else {
721
+						LM_DBG("Skipping virtual socket that is not local.\n");
722
+					}
723
+				} else {
724
+					goto found;
725
+				}
657 726
 			}
658 727
 			if(si->useinfo.name.s!=NULL) {
659 728
 				LM_DBG("checking advertise if host==us:"
... ...
@@ -2071,10 +2140,11 @@ void print_all_socket_lists()
2071 2140
 				for (ai=si->addr_info_lst; ai; ai=ai->next) {
2072 2141
 					printf(", %s", ai->address_str.s);
2073 2142
 				}
2074
-				printf("):%s%s%s\n",
2143
+				printf("):%s%s%s%s\n",
2075 2144
 						si->port_no_str.s,
2076
-						si->flags & SI_IS_MCAST ? " mcast" : "",
2077
-						si->flags & SI_IS_MHOMED? " mhomed" : "");
2145
+						si->flags & SI_IS_MCAST  ? " mcast" : "",
2146
+						si->flags & SI_IS_MHOMED ? " mhomed" : "",
2147
+						si->flags & SI_IS_VIRTUAL? " virtual" : "");
2078 2148
 			}else{
2079 2149
 				printf("             %s: %s",
2080 2150
 						get_valid_proto_name(proto),
... ...
@@ -2082,10 +2152,11 @@ void print_all_socket_lists()
2082 2152
 				if (!(si->flags & SI_IS_IP)) {
2083 2153
 					printf(" [%s]", si->address_str.s);
2084 2154
 				}
2085
-				printf( ":%s%s%s",
2155
+				printf( ":%s%s%s%s",
2086 2156
 						si->port_no_str.s,
2087
-						si->flags & SI_IS_MCAST ? " mcast" : "",
2088
-						si->flags & SI_IS_MHOMED? " mhomed" : "");
2157
+						si->flags & SI_IS_MCAST  ? " mcast" : "",
2158
+						si->flags & SI_IS_MHOMED ? " mhomed" : "",
2159
+						si->flags & SI_IS_VIRTUAL? " virtual" : "");
2089 2160
 				if (si->sockname.s) {
2090 2161
 					printf(" name %s", si->sockname.s);
2091 2162
 				}