7 /* CIDR-style pattern matching
9 /* #include <cidr_match.h>
11 /* VSTRING *cidr_match_parse(info, pattern, why)
16 /* int cidr_match_execute(info, address)
18 /* const char *address;
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().
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.
34 /* cidr_match_execute() matches the specified address against
35 /* a list of parsed expressions, and returns the matching
36 /* expression's data structure.
38 /* dict_cidr(3) CIDR-style lookup table
41 /* IBM T.J. Watson Research
43 /* Yorktown Heights, NY 10598, USA
53 #include <sys/socket.h>
54 #include <netinet/in.h>
55 #include <arpa/inet.h>
57 /* Utility library. */
61 #include <stringops.h>
63 #include <myaddrinfo.h>
64 #include <mask_addr.h>
65 #include <cidr_match.h>
67 /* Application-specific. */
70 * This is how we figure out the address family, address bit count and
71 * address byte count for a CIDR_MATCH entry.
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))
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))
93 /* cidr_match_execute - match address against compiled CIDR pattern list */
95 CIDR_MATCH
*cidr_match_execute(CIDR_MATCH
*list
, const char *addr
)
97 unsigned char addr_bytes
[CIDR_MATCH_ABYTES
];
104 addr_family
= CIDR_MATCH_ADDR_FAMILY(addr
);
105 if (inet_pton(addr_family
, addr
, addr_bytes
) != 1)
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
)
116 if ((*ap
& *mp
) != *np
)
120 /* Optimized case: all 1 netmask (i.e. no netmask specified). */
122 for (np
= entry
->net_bytes
,
123 ap
= addr_bytes
; /* void */ ; np
++, ap
++) {
124 if (ap
>= addr_bytes
+ entry
->addr_byte_count
)
135 /* cidr_match_parse - parse CIDR pattern */
137 VSTRING
*cidr_match_parse(CIDR_MATCH
*ip
, char *pattern
, VSTRING
*why
)
139 const char *myname
= "cidr_match_parse";
142 MAI_HOSTADDR_STR hostaddr
;
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
152 if (*pattern
== '[') {
154 if ((mask_search
= split_at(pattern
, ']')) == 0) {
155 vstring_sprintf(why
? why
: (why
= vstring_alloc(20)),
156 "missing ']' character after \"[%s\"", pattern
);
158 } else if (*mask_search
!= '/') {
159 if (*mask_search
!= 0) {
160 vstring_sprintf(why
? why
: (why
= vstring_alloc(20)),
161 "garbage after \"[%s]\"", pattern
);
164 mask_search
= pattern
;
167 mask_search
= pattern
;
170 * Parse the pattern into network and mask, destroying the pattern.
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
);
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
);
183 if (ip
->mask_shift
> 0) {
184 /* Allow for bytes > 8. */
185 memset(ip
->mask_bytes
, (unsigned char) -1, ip
->addr_byte_count
);
186 mask_addr(ip
->mask_bytes
, ip
->addr_byte_count
, ip
->mask_shift
);
188 memset(ip
->mask_bytes
, 0, ip
->addr_byte_count
);
191 * Sanity check: all host address bits must be zero.
193 for (np
= ip
->net_bytes
, mp
= ip
->mask_bytes
;
194 np
< ip
->net_bytes
+ ip
->addr_byte_count
; 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
);
210 * No /mask specified. Treat a bare network address as /allbits.
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
);
221 ip
->mask_shift
= ip
->addr_bit_count
;
222 /* Allow for bytes > 8. */
223 memset(ip
->mask_bytes
, (unsigned char) -1, ip
->addr_byte_count
);
227 * Wrap up the result.