1 /* $NetBSD: cidr_match.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* cidr_match 3 6 /* SUMMARY 7 /* CIDR-style pattern matching 8 /* SYNOPSIS 9 /* #include <cidr_match.h> 10 /* 11 /* VSTRING *cidr_match_parse(info, pattern, why) 12 /* CIDR_MATCH *info; 13 /* char *pattern; 14 /* VSTRING *why; 15 /* 16 /* int cidr_match_execute(info, address) 17 /* CIDR_MATCH *info; 18 /* const char *address; 19 /* DESCRIPTION 20 /* This module parses address or address/length patterns and 21 /* provides simple address matching. The implementation is 22 /* such that parsing and execution can be done without dynamic 23 /* memory allocation. The purpose is to minimize overhead when 24 /* called by functions that parse and execute on the fly, such 25 /* as match_hostaddr(). 26 /* 27 /* cidr_match_parse() parses an address or address/mask 28 /* expression and stores the result into the info argument. 29 /* The result is non-zero in case of problems: either the 30 /* value of the why argument, or a newly allocated VSTRING 31 /* (the caller should give the latter to vstring_free()). 32 /* The pattern argument is destroyed. 33 /* 34 /* cidr_match_execute() matches the specified address against 35 /* a list of parsed expressions, and returns the matching 36 /* expression's data structure. 37 /* SEE ALSO 38 /* dict_cidr(3) CIDR-style lookup table 39 /* AUTHOR(S) 40 /* Wietse Venema 41 /* IBM T.J. Watson Research 42 /* P.O. Box 704 43 /* Yorktown Heights, NY 10598, USA 44 /*--*/ 45 46 /* System library. */ 47 48 #include <sys_defs.h> 49 #include <stdlib.h> 50 #include <unistd.h> 51 #include <string.h> 52 #include <ctype.h> 53 #include <sys/socket.h> 54 #include <netinet/in.h> 55 #include <arpa/inet.h> 56 57 /* Utility library. */ 58 59 #include <msg.h> 60 #include <vstring.h> 61 #include <stringops.h> 62 #include <split_at.h> 63 #include <myaddrinfo.h> 64 #include <mask_addr.h> 65 #include <cidr_match.h> 66 67 /* Application-specific. */ 68 69 /* 70 * This is how we figure out the address family, address bit count and 71 * address byte count for a CIDR_MATCH entry. 72 */ 73 #ifdef HAS_IPV6 74 #define CIDR_MATCH_ADDR_FAMILY(a) (strchr((a), ':') ? AF_INET6 : AF_INET) 75 #define CIDR_MATCH_ADDR_BIT_COUNT(f) \ 76 ((f) == AF_INET6 ? MAI_V6ADDR_BITS : \ 77 (f) == AF_INET ? MAI_V4ADDR_BITS : \ 78 (msg_panic("%s: bad address family %d", myname, (f)), 0)) 79 #define CIDR_MATCH_ADDR_BYTE_COUNT(f) \ 80 ((f) == AF_INET6 ? MAI_V6ADDR_BYTES : \ 81 (f) == AF_INET ? MAI_V4ADDR_BYTES : \ 82 (msg_panic("%s: bad address family %d", myname, (f)), 0)) 83 #else 84 #define CIDR_MATCH_ADDR_FAMILY(a) (AF_INET) 85 #define CIDR_MATCH_ADDR_BIT_COUNT(f) \ 86 ((f) == AF_INET ? MAI_V4ADDR_BITS : \ 87 (msg_panic("%s: bad address family %d", myname, (f)), 0)) 88 #define CIDR_MATCH_ADDR_BYTE_COUNT(f) \ 89 ((f) == AF_INET ? MAI_V4ADDR_BYTES : \ 90 (msg_panic("%s: bad address family %d", myname, (f)), 0)) 91 #endif 92 93 /* cidr_match_execute - match address against compiled CIDR pattern list */ 94 95 CIDR_MATCH *cidr_match_execute(CIDR_MATCH *list, const char *addr) 96 { 97 unsigned char addr_bytes[CIDR_MATCH_ABYTES]; 98 unsigned addr_family; 99 unsigned char *mp; 100 unsigned char *np; 101 unsigned char *ap; 102 CIDR_MATCH *entry; 103 104 addr_family = CIDR_MATCH_ADDR_FAMILY(addr); 105 if (inet_pton(addr_family, addr, addr_bytes) != 1) 106 return (0); 107 108 for (entry = list; entry; entry = entry->next) { 109 if (entry->addr_family == addr_family) { 110 /* Unoptimized case: netmask with some or all bits zero. */ 111 if (entry->mask_shift < entry->addr_bit_count) { 112 for (np = entry->net_bytes, mp = entry->mask_bytes, 113 ap = addr_bytes; /* void */ ; np++, mp++, ap++) { 114 if (ap >= addr_bytes + entry->addr_byte_count) 115 return (entry); 116 if ((*ap & *mp) != *np) 117 break; 118 } 119 } 120 /* Optimized case: all 1 netmask (i.e. no netmask specified). */ 121 else { 122 for (np = entry->net_bytes, 123 ap = addr_bytes; /* void */ ; np++, ap++) { 124 if (ap >= addr_bytes + entry->addr_byte_count) 125 return (entry); 126 if (*ap != *np) 127 break; 128 } 129 } 130 } 131 } 132 return (0); 133 } 134 135 /* cidr_match_parse - parse CIDR pattern */ 136 137 VSTRING *cidr_match_parse(CIDR_MATCH *ip, char *pattern, VSTRING *why) 138 { 139 const char *myname = "cidr_match_parse"; 140 char *mask_search; 141 char *mask; 142 MAI_HOSTADDR_STR hostaddr; 143 unsigned char *np; 144 unsigned char *mp; 145 146 /* 147 * Strip [] from [addr/len] or [addr]/len, destroying the pattern. CIDR 148 * maps don't need [] to eliminate syntax ambiguity, but matchlists need 149 * it. While stripping [], figure out where we should start looking for 150 * /mask information. 151 */ 152 if (*pattern == '[') { 153 pattern++; 154 if ((mask_search = split_at(pattern, ']')) == 0) { 155 vstring_sprintf(why ? why : (why = vstring_alloc(20)), 156 "missing ']' character after \"[%s\"", pattern); 157 return (why); 158 } else if (*mask_search != '/') { 159 if (*mask_search != 0) { 160 vstring_sprintf(why ? why : (why = vstring_alloc(20)), 161 "garbage after \"[%s]\"", pattern); 162 return (why); 163 } 164 mask_search = pattern; 165 } 166 } else 167 mask_search = pattern; 168 169 /* 170 * Parse the pattern into network and mask, destroying the pattern. 171 */ 172 if ((mask = split_at(mask_search, '/')) != 0) { 173 ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern); 174 ip->addr_bit_count = CIDR_MATCH_ADDR_BIT_COUNT(ip->addr_family); 175 ip->addr_byte_count = CIDR_MATCH_ADDR_BYTE_COUNT(ip->addr_family); 176 if (!alldig(mask) 177 || (ip->mask_shift = atoi(mask)) > ip->addr_bit_count 178 || inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) { 179 vstring_sprintf(why ? why : (why = vstring_alloc(20)), 180 "bad net/mask pattern: \"%s/%s\"", pattern, mask); 181 return (why); 182 } 183 if (ip->mask_shift > 0) { 184 /* Allow for bytes > 8. */ 185 memset(ip->mask_bytes, ~0U, ip->addr_byte_count); 186 mask_addr(ip->mask_bytes, ip->addr_byte_count, ip->mask_shift); 187 } else 188 memset(ip->mask_bytes, 0, ip->addr_byte_count); 189 190 /* 191 * Sanity check: all host address bits must be zero. 192 */ 193 for (np = ip->net_bytes, mp = ip->mask_bytes; 194 np < ip->net_bytes + ip->addr_byte_count; np++, mp++) { 195 if (*np & ~(*mp)) { 196 mask_addr(ip->net_bytes, ip->addr_byte_count, ip->mask_shift); 197 if (inet_ntop(ip->addr_family, ip->net_bytes, hostaddr.buf, 198 sizeof(hostaddr.buf)) == 0) 199 msg_fatal("inet_ntop: %m"); 200 vstring_sprintf(why ? why : (why = vstring_alloc(20)), 201 "non-null host address bits in \"%s/%s\", " 202 "perhaps you should use \"%s/%d\" instead", 203 pattern, mask, hostaddr.buf, ip->mask_shift); 204 return (why); 205 } 206 } 207 } 208 209 /* 210 * No /mask specified. Treat a bare network address as /allbits. 211 */ 212 else { 213 ip->addr_family = CIDR_MATCH_ADDR_FAMILY(pattern); 214 ip->addr_bit_count = CIDR_MATCH_ADDR_BIT_COUNT(ip->addr_family); 215 ip->addr_byte_count = CIDR_MATCH_ADDR_BYTE_COUNT(ip->addr_family); 216 if (inet_pton(ip->addr_family, pattern, ip->net_bytes) != 1) { 217 vstring_sprintf(why ? why : (why = vstring_alloc(20)), 218 "bad address pattern: \"%s\"", pattern); 219 return (why); 220 } 221 ip->mask_shift = ip->addr_bit_count; 222 /* Allow for bytes > 8. */ 223 memset(ip->mask_bytes, ~0U, ip->addr_byte_count); 224 } 225 226 /* 227 * Wrap up the result. 228 */ 229 ip->next = 0; 230 231 return (0); 232 } 233