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