1219820Sjeff/* 2219820Sjeff This software is available to you under a choice of one of two 3219820Sjeff licenses. You may choose to be licensed under the terms of the GNU 4219820Sjeff General Public License (GPL) Version 2, available at 5219820Sjeff <http://www.fsf.org/copyleft/gpl.html>, or the OpenIB.org BSD 6219820Sjeff license, available in the LICENSE.TXT file accompanying this 7219820Sjeff software. These details are also available at 8219820Sjeff <http://openib.org/license.html>. 9219820Sjeff 10219820Sjeff THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 11219820Sjeff EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 12219820Sjeff MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 13219820Sjeff NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 14219820Sjeff BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 15219820Sjeff ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 16219820Sjeff CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17219820Sjeff SOFTWARE. 18219820Sjeff 19219820Sjeff Copyright (c) 2004 Topspin Communications. All rights reserved. 20219820Sjeff Copyright (c) 2005-2006 Mellanox Technologies Ltd. All rights reserved. 21219820Sjeff 22219820Sjeff $Id$ 23219820Sjeff*/ 24219820Sjeff 25219820Sjeff/* 26219820Sjeff * system includes 27219820Sjeff */ 28219820Sjeff#if HAVE_CONFIG_H 29219820Sjeff# include <config.h> 30219820Sjeff#endif /* HAVE_CONFIG_H */ 31219820Sjeff 32219820Sjeff#include <unistd.h> 33219820Sjeff#include <errno.h> 34219820Sjeff#include <stdio.h> 35219820Sjeff#include <stdlib.h> 36219820Sjeff#include <string.h> 37219820Sjeff#include <fnmatch.h> 38219820Sjeff#include <sys/socket.h> 39219820Sjeff#include <netinet/in.h> 40219820Sjeff#include <arpa/inet.h> 41219820Sjeff 42219820Sjeff/* 43219820Sjeff * SDP specific includes 44219820Sjeff */ 45219820Sjeff#include "libsdp.h" 46219820Sjeff 47219820Sjeff/* --------------------------------------------------------------------- */ 48219820Sjeff/* library static and global variables */ 49219820Sjeff/* --------------------------------------------------------------------- */ 50219820Sjeffextern char *program_invocation_name, *program_invocation_short_name; 51219820Sjeff 52219820Sjeffstatic void 53219820Sjeffget_rule_str( 54219820Sjeff struct use_family_rule *rule, 55219820Sjeff char *buf, 56219820Sjeff size_t len ) 57219820Sjeff{ 58219820Sjeff char addr_buf[MAX_ADDR_STR_LEN]; 59219820Sjeff char ports_buf[16]; 60219820Sjeff char *target = __sdp_get_family_str( rule->target_family ); 61219820Sjeff char *prog = rule->prog_name_expr; 62219820Sjeff 63219820Sjeff /* TODO: handle IPv6 in rule */ 64219820Sjeff if ( rule->match_by_addr ) { 65219820Sjeff if ( rule->prefixlen != 32 ) 66219820Sjeff sprintf( addr_buf, "%s/%d", inet_ntoa( rule->ipv4 ), 67219820Sjeff rule->prefixlen ); 68219820Sjeff else 69219820Sjeff sprintf( addr_buf, "%s", inet_ntoa( rule->ipv4 ) ); 70219820Sjeff } else { 71219820Sjeff strcpy( addr_buf, "*" ); 72219820Sjeff } 73219820Sjeff 74219820Sjeff if ( rule->match_by_port ) 75219820Sjeff if ( rule->eport > rule->sport ) 76219820Sjeff sprintf( ports_buf, "%d", rule->sport ); 77219820Sjeff else 78219820Sjeff sprintf( ports_buf, "%d-%d", rule->sport, rule->eport ); 79219820Sjeff else 80219820Sjeff sprintf( ports_buf, "*" ); 81219820Sjeff 82219820Sjeff snprintf( buf, len, "use %s %s %s:%s", target, prog, addr_buf, ports_buf ); 83219820Sjeff} 84219820Sjeff 85219820Sjeff/* return 0 if the addresses match */ 86219820Sjeffstatic inline int 87219820Sjeffmatch_ipv4_addr( 88219820Sjeff struct use_family_rule *rule, 89219820Sjeff const struct sockaddr_in *sin ) 90219820Sjeff{ 91219820Sjeff return ( rule->ipv4.s_addr != 92219820Sjeff ( sin->sin_addr. 93219820Sjeff s_addr & htonl( SDP_NETMASK( rule->prefixlen ) ) ) ); 94219820Sjeff} 95219820Sjeff 96219820Sjeffstatic int 97219820Sjeffmatch_ip_addr_and_port( 98219820Sjeff struct use_family_rule *rule, 99219820Sjeff const struct sockaddr *addr_in, 100219820Sjeff const socklen_t addrlen ) 101219820Sjeff{ 102219820Sjeff const struct sockaddr_in *sin = ( const struct sockaddr_in * )addr_in; 103219820Sjeff const struct sockaddr_in6 *sin6 = ( const struct sockaddr_in6 * )addr_in; 104219820Sjeff struct sockaddr_in tmp_sin; 105219820Sjeff unsigned short port; 106219820Sjeff int match = 1; 107219820Sjeff char addr_buf[MAX_ADDR_STR_LEN]; 108219820Sjeff const char *addr_str; 109219820Sjeff char rule_str[512]; 110219820Sjeff 111219820Sjeff if ( __sdp_log_get_level( ) <= 3 ) { 112219820Sjeff if ( sin6->sin6_family == AF_INET6 ) { 113219820Sjeff addr_str = 114219820Sjeff inet_ntop( AF_INET6, ( void * )&( sin6->sin6_addr ), addr_buf, 115219820Sjeff MAX_ADDR_STR_LEN ); 116219820Sjeff port = ntohs( sin6->sin6_port ); 117219820Sjeff } else { 118219820Sjeff addr_str = 119219820Sjeff inet_ntop( AF_INET, ( void * )&( sin->sin_addr ), addr_buf, 120219820Sjeff MAX_ADDR_STR_LEN ); 121219820Sjeff port = ntohs( sin->sin_port ); 122219820Sjeff } 123219820Sjeff if ( addr_str == NULL ) 124219820Sjeff addr_str = "INVALID_ADDR"; 125219820Sjeff 126219820Sjeff get_rule_str( rule, rule_str, sizeof( rule_str ) ); 127219820Sjeff 128219820Sjeff __sdp_log( 3, "MATCH: matching %s:%d to %s => \n", addr_str, port, 129219820Sjeff rule_str ); 130219820Sjeff } 131219820Sjeff 132219820Sjeff /* We currently only support IPv4 and IPv4 embedded in IPv6 */ 133219820Sjeff if ( rule->match_by_port ) { 134219820Sjeff if ( sin6->sin6_family == AF_INET6 ) 135219820Sjeff port = ntohs( sin6->sin6_port ); 136219820Sjeff else 137219820Sjeff port = ntohs( sin->sin_port ); 138219820Sjeff 139219820Sjeff if ( ( port < rule->sport ) || ( port > rule->eport ) ) { 140219820Sjeff __sdp_log( 3, "NEGATIVE by port range\n" ); 141219820Sjeff match = 0; 142219820Sjeff } 143219820Sjeff } 144219820Sjeff 145219820Sjeff if ( match && rule->match_by_addr ) { 146219820Sjeff if ( __sdp_sockaddr_to_sdp( addr_in, addrlen, &tmp_sin, NULL ) || 147219820Sjeff match_ipv4_addr( rule, &tmp_sin ) ) { 148219820Sjeff __sdp_log( 3, "NEGATIVE by address\n" ); 149219820Sjeff match = 0; 150219820Sjeff } 151219820Sjeff } 152219820Sjeff 153219820Sjeff if ( match ) 154219820Sjeff __sdp_log( 3, "POSITIVE\n" ); 155219820Sjeff 156219820Sjeff return match; 157219820Sjeff} 158219820Sjeff 159219820Sjeff/* return 1 on match */ 160219820Sjeffstatic int 161219820Sjeffmatch_program_name( 162219820Sjeff struct use_family_rule *rule ) 163219820Sjeff{ 164219820Sjeff return !fnmatch( rule->prog_name_expr, program_invocation_short_name, 0 ); 165219820Sjeff} 166219820Sjeff 167219820Sjeffstatic use_family_t 168219820Sjeffget_family_by_first_matching_rule( 169219820Sjeff const struct sockaddr *sin, 170219820Sjeff const socklen_t addrlen, 171219820Sjeff struct use_family_rule *rules ) 172219820Sjeff{ 173219820Sjeff struct use_family_rule *rule; 174219820Sjeff 175219820Sjeff for ( rule = rules; rule != NULL; rule = rule->next ) { 176219820Sjeff /* skip if not our program */ 177219820Sjeff if ( !match_program_name( rule ) ) 178219820Sjeff continue; 179219820Sjeff 180219820Sjeff /* first rule wins */ 181219820Sjeff if ( match_ip_addr_and_port( rule, sin, addrlen ) ) 182219820Sjeff return ( rule->target_family ); 183219820Sjeff } 184219820Sjeff 185219820Sjeff return ( USE_BOTH ); 186219820Sjeff} 187219820Sjeff 188219820Sjeff/* return the result of the first matching rule found */ 189219820Sjeffuse_family_t 190219820Sjeff__sdp_match_listen( 191219820Sjeff const struct sockaddr * sin, 192219820Sjeff const socklen_t addrlen ) 193219820Sjeff{ 194219820Sjeff use_family_t target_family; 195219820Sjeff 196219820Sjeff /* if we do not have any rules we use sdp */ 197219820Sjeff if ( __sdp_config_empty( ) ) 198219820Sjeff target_family = USE_SDP; 199219820Sjeff else 200219820Sjeff target_family = 201219820Sjeff get_family_by_first_matching_rule( sin, addrlen, 202219820Sjeff __sdp_servers_family_rules_head ); 203219820Sjeff 204219820Sjeff __sdp_log( 4, "MATCH LISTEN: => %s\n", 205219820Sjeff __sdp_get_family_str( target_family ) ); 206219820Sjeff 207219820Sjeff return ( target_family ); 208219820Sjeff} 209219820Sjeff 210219820Sjeffuse_family_t 211219820Sjeff__sdp_match_connect( 212219820Sjeff const struct sockaddr * sin, 213219820Sjeff const socklen_t addrlen ) 214219820Sjeff{ 215219820Sjeff use_family_t target_family; 216219820Sjeff 217219820Sjeff /* if we do not have any rules we use sdp */ 218219820Sjeff if ( __sdp_config_empty( ) ) 219219820Sjeff target_family = USE_SDP; 220219820Sjeff else 221219820Sjeff target_family = 222219820Sjeff get_family_by_first_matching_rule( sin, addrlen, 223219820Sjeff __sdp_clients_family_rules_head ); 224219820Sjeff 225219820Sjeff __sdp_log( 4, "MATCH CONNECT: => %s\n", 226219820Sjeff __sdp_get_family_str( target_family ) ); 227219820Sjeff 228219820Sjeff return ( target_family ); 229219820Sjeff} 230219820Sjeff 231219820Sjeff/* given a set of rules see if there is a global match for current program */ 232219820Sjeffstatic use_family_t 233219820Sjeffmatch_by_all_rules_program( 234219820Sjeff struct use_family_rule *rules ) 235219820Sjeff{ 236219820Sjeff int any_sdp = 0; 237219820Sjeff int any_tcp = 0; 238219820Sjeff use_family_t target_family = USE_BOTH; 239219820Sjeff struct use_family_rule *rule; 240219820Sjeff 241219820Sjeff for ( rule = rules; ( rule != NULL ) && ( target_family == USE_BOTH ); 242219820Sjeff rule = rule->next ) { 243219820Sjeff /* skip if not our program */ 244219820Sjeff if ( !match_program_name( rule ) ) 245219820Sjeff continue; 246219820Sjeff 247219820Sjeff /* 248219820Sjeff * to declare a dont care we either have a dont care address and port 249219820Sjeff * or the previous non global rules use the same target family as the 250219820Sjeff * global rule 251219820Sjeff */ 252219820Sjeff if ( rule->match_by_addr || rule->match_by_port ) { 253219820Sjeff /* not a glocal match rule - just track the target family */ 254219820Sjeff if ( rule->target_family == USE_SDP ) 255219820Sjeff any_sdp++; 256219820Sjeff else if ( rule->target_family == USE_TCP ) 257219820Sjeff any_tcp++; 258219820Sjeff } else { 259219820Sjeff /* a global match so we can declare a match by program */ 260219820Sjeff if ( ( rule->target_family == USE_SDP ) && ( any_tcp == 0 ) ) 261219820Sjeff target_family = USE_SDP; 262219820Sjeff else if ( ( rule->target_family == USE_TCP ) && ( any_sdp == 0 ) ) 263219820Sjeff target_family = USE_TCP; 264219820Sjeff } 265219820Sjeff } 266219820Sjeff return ( target_family ); 267219820Sjeff} 268219820Sjeff 269219820Sjeff/* return tcp or sdp if the port and role are dont cares */ 270219820Sjeffuse_family_t 271219820Sjeff__sdp_match_by_program( 272219820Sjeff ) 273219820Sjeff{ 274219820Sjeff use_family_t server_target_family; 275219820Sjeff use_family_t client_target_family; 276219820Sjeff use_family_t target_family = USE_BOTH; 277219820Sjeff 278219820Sjeff if ( __sdp_config_empty( ) ) { 279219820Sjeff target_family = USE_SDP; 280219820Sjeff } else { 281219820Sjeff /* need to try both server and client rules */ 282219820Sjeff server_target_family = 283219820Sjeff match_by_all_rules_program( __sdp_servers_family_rules_head ); 284219820Sjeff client_target_family = 285219820Sjeff match_by_all_rules_program( __sdp_clients_family_rules_head ); 286219820Sjeff 287219820Sjeff /* only if both agree */ 288219820Sjeff if ( server_target_family == client_target_family ) 289219820Sjeff target_family = server_target_family; 290219820Sjeff } 291219820Sjeff 292219820Sjeff __sdp_log( 4, "MATCH PROGRAM: => %s\n", 293219820Sjeff __sdp_get_family_str( target_family ) ); 294219820Sjeff 295219820Sjeff return ( target_family ); 296219820Sjeff} 297