npf_data.c revision 1.7 1 1.7 rmind /* $NetBSD: npf_data.c,v 1.7 2011/02/02 02:20:25 rmind Exp $ */
2 1.1 rmind
3 1.1 rmind /*-
4 1.6 rmind * Copyright (c) 2009-2011 The NetBSD Foundation, Inc.
5 1.1 rmind * All rights reserved.
6 1.1 rmind *
7 1.1 rmind * Redistribution and use in source and binary forms, with or without
8 1.1 rmind * modification, are permitted provided that the following conditions
9 1.1 rmind * are met:
10 1.1 rmind * 1. Redistributions of source code must retain the above copyright
11 1.1 rmind * notice, this list of conditions and the following disclaimer.
12 1.1 rmind * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 rmind * notice, this list of conditions and the following disclaimer in the
14 1.1 rmind * documentation and/or other materials provided with the distribution.
15 1.1 rmind *
16 1.1 rmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 rmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 rmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 rmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 rmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 rmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 rmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 rmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 rmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 rmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 rmind * POSSIBILITY OF SUCH DAMAGE.
27 1.1 rmind */
28 1.1 rmind
29 1.1 rmind /*
30 1.7 rmind * npfctl(8) helper routines.
31 1.1 rmind */
32 1.1 rmind
33 1.4 rmind #include <sys/cdefs.h>
34 1.7 rmind __RCSID("$NetBSD: npf_data.c,v 1.7 2011/02/02 02:20:25 rmind Exp $");
35 1.4 rmind
36 1.1 rmind #include <sys/types.h>
37 1.1 rmind #include <sys/socket.h>
38 1.1 rmind #include <sys/ioctl.h>
39 1.1 rmind #include <net/if.h>
40 1.3 rmind #include <netinet/tcp.h>
41 1.1 rmind
42 1.1 rmind #include <arpa/inet.h>
43 1.1 rmind
44 1.1 rmind #include <stdlib.h>
45 1.1 rmind #include <string.h>
46 1.1 rmind #include <unistd.h>
47 1.1 rmind #include <ctype.h>
48 1.1 rmind #include <err.h>
49 1.1 rmind #include <ifaddrs.h>
50 1.1 rmind #include <netdb.h>
51 1.1 rmind #include <assert.h>
52 1.1 rmind
53 1.1 rmind #include "npfctl.h"
54 1.1 rmind
55 1.1 rmind static struct ifaddrs * ifs_list = NULL;
56 1.7 rmind nl_config_t * npf_conf = NULL;
57 1.1 rmind
58 1.1 rmind void
59 1.1 rmind npfctl_init_data(void)
60 1.1 rmind {
61 1.1 rmind
62 1.7 rmind npf_conf = npf_config_create();
63 1.7 rmind if (npf_conf == NULL) {
64 1.7 rmind errx(EXIT_FAILURE, "npf_config_create");
65 1.7 rmind }
66 1.7 rmind if (getifaddrs(&ifs_list) == -1) {
67 1.1 rmind err(EXIT_FAILURE, "getifaddrs");
68 1.7 rmind }
69 1.1 rmind }
70 1.1 rmind
71 1.1 rmind int
72 1.1 rmind npfctl_ioctl_send(int fd)
73 1.1 rmind {
74 1.7 rmind int error = npf_config_submit(npf_conf, fd);
75 1.7 rmind npf_config_destroy(npf_conf);
76 1.7 rmind return error;
77 1.5 rmind }
78 1.5 rmind
79 1.1 rmind /*
80 1.1 rmind * Helper routines:
81 1.1 rmind *
82 1.1 rmind * npfctl_getif() - get interface addresses and index number from name.
83 1.1 rmind * npfctl_parse_v4mask() - parse address/mask integers from CIDR block.
84 1.3 rmind * npfctl_parse_port() - parse port number (which may be a service name).
85 1.3 rmind * npfctl_parse_tcpfl() - parse TCP flags.
86 1.1 rmind */
87 1.1 rmind
88 1.6 rmind struct ifaddrs *
89 1.6 rmind npfctl_getif(char *ifname, unsigned int *if_idx, bool reqaddr)
90 1.1 rmind {
91 1.1 rmind struct ifaddrs *ifent;
92 1.1 rmind struct sockaddr_in *sin;
93 1.1 rmind
94 1.1 rmind for (ifent = ifs_list; ifent != NULL; ifent = ifent->ifa_next) {
95 1.1 rmind sin = (struct sockaddr_in *)ifent->ifa_addr;
96 1.6 rmind if (sin->sin_family != AF_INET && reqaddr)
97 1.1 rmind continue;
98 1.1 rmind if (strcmp(ifent->ifa_name, ifname) == 0)
99 1.1 rmind break;
100 1.1 rmind }
101 1.1 rmind if (ifent) {
102 1.1 rmind *if_idx = if_nametoindex(ifname);
103 1.1 rmind }
104 1.1 rmind return ifent;
105 1.1 rmind }
106 1.1 rmind
107 1.1 rmind bool
108 1.3 rmind npfctl_parse_v4mask(char *ostr, in_addr_t *addr, in_addr_t *mask)
109 1.1 rmind {
110 1.3 rmind char *str = xstrdup(ostr);
111 1.1 rmind char *p = strchr(str, '/');
112 1.1 rmind u_int bits;
113 1.3 rmind bool ret;
114 1.1 rmind
115 1.1 rmind /* In network byte order. */
116 1.1 rmind if (p) {
117 1.1 rmind *p++ = '\0';
118 1.1 rmind bits = (u_int)atoi(p);
119 1.1 rmind *mask = bits ? htonl(0xffffffff << (32 - bits)) : 0;
120 1.1 rmind } else {
121 1.1 rmind *mask = 0xffffffff;
122 1.1 rmind }
123 1.3 rmind ret = inet_aton(str, (struct in_addr *)addr) != 0;
124 1.3 rmind free(str);
125 1.3 rmind return ret;
126 1.3 rmind }
127 1.3 rmind
128 1.7 rmind bool
129 1.3 rmind npfctl_parse_port(char *ostr, bool *range, in_port_t *fport, in_port_t *tport)
130 1.3 rmind {
131 1.3 rmind char *str = xstrdup(ostr), *sep;
132 1.3 rmind
133 1.3 rmind *range = false;
134 1.3 rmind if ((sep = strchr(str, ':')) != NULL) {
135 1.3 rmind /* Port range (only numeric). */
136 1.3 rmind *range = true;
137 1.3 rmind *sep = '\0';
138 1.3 rmind
139 1.3 rmind } else if (isalpha((unsigned char)*str)) {
140 1.3 rmind struct servent *se;
141 1.3 rmind
142 1.3 rmind se = getservbyname(str, NULL);
143 1.3 rmind if (se == NULL) {
144 1.3 rmind free(str);
145 1.3 rmind return false;
146 1.3 rmind }
147 1.3 rmind *fport = se->s_port;
148 1.3 rmind } else {
149 1.3 rmind *fport = htons(atoi(str));
150 1.3 rmind }
151 1.3 rmind *tport = sep ? htons(atoi(sep + 1)) : *fport;
152 1.3 rmind free(str);
153 1.3 rmind return true;
154 1.1 rmind }
155 1.1 rmind
156 1.7 rmind void
157 1.1 rmind npfctl_parse_cidr(char *str, in_addr_t *addr, in_addr_t *mask)
158 1.1 rmind {
159 1.1 rmind
160 1.6 rmind if (strcmp(str, "any") == 0) {
161 1.6 rmind *addr = 0x0;
162 1.6 rmind *mask = 0x0;
163 1.6 rmind
164 1.6 rmind } else if (isalpha((unsigned char)*str)) {
165 1.1 rmind struct ifaddrs *ifa;
166 1.1 rmind struct sockaddr_in *sin;
167 1.1 rmind u_int idx;
168 1.1 rmind
169 1.6 rmind if ((ifa = npfctl_getif(str, &idx, true)) == NULL) {
170 1.1 rmind errx(EXIT_FAILURE, "invalid interface '%s'", str);
171 1.1 rmind }
172 1.1 rmind /* Interface address. */
173 1.1 rmind sin = (struct sockaddr_in *)ifa->ifa_addr;
174 1.1 rmind *addr = sin->sin_addr.s_addr;
175 1.1 rmind *mask = 0xffffffff;
176 1.1 rmind
177 1.1 rmind } else if (!npfctl_parse_v4mask(str, addr, mask)) {
178 1.1 rmind errx(EXIT_FAILURE, "invalid CIDR '%s'\n", str);
179 1.1 rmind }
180 1.1 rmind }
181 1.1 rmind
182 1.3 rmind static bool
183 1.3 rmind npfctl_parse_tcpfl(char *s, uint8_t *tfl, uint8_t *tfl_mask)
184 1.3 rmind {
185 1.3 rmind uint8_t tcpfl = 0;
186 1.3 rmind bool mask = false;
187 1.3 rmind
188 1.3 rmind while (*s) {
189 1.3 rmind switch (*s) {
190 1.3 rmind case 'F': tcpfl |= TH_FIN; break;
191 1.3 rmind case 'S': tcpfl |= TH_SYN; break;
192 1.3 rmind case 'R': tcpfl |= TH_RST; break;
193 1.3 rmind case 'P': tcpfl |= TH_PUSH; break;
194 1.3 rmind case 'A': tcpfl |= TH_ACK; break;
195 1.3 rmind case 'U': tcpfl |= TH_URG; break;
196 1.3 rmind case 'E': tcpfl |= TH_ECE; break;
197 1.3 rmind case 'W': tcpfl |= TH_CWR; break;
198 1.3 rmind case '/':
199 1.3 rmind *s = '\0';
200 1.3 rmind *tfl = tcpfl;
201 1.3 rmind tcpfl = 0;
202 1.3 rmind mask = true;
203 1.3 rmind break;
204 1.3 rmind default:
205 1.3 rmind return false;
206 1.3 rmind }
207 1.3 rmind s++;
208 1.3 rmind }
209 1.3 rmind if (!mask) {
210 1.3 rmind *tfl = tcpfl;
211 1.3 rmind }
212 1.3 rmind *tfl_mask = tcpfl;
213 1.3 rmind return true;
214 1.3 rmind }
215 1.3 rmind
216 1.1 rmind void
217 1.7 rmind npfctl_fill_table(nl_table_t *tl, char *fname)
218 1.1 rmind {
219 1.1 rmind char *buf;
220 1.1 rmind FILE *fp;
221 1.1 rmind size_t n;
222 1.1 rmind int l;
223 1.1 rmind
224 1.1 rmind fp = fopen(fname, "r");
225 1.1 rmind if (fp == NULL) {
226 1.6 rmind err(EXIT_FAILURE, "open '%s'", fname);
227 1.1 rmind }
228 1.1 rmind l = 1;
229 1.1 rmind buf = NULL;
230 1.1 rmind while (getline(&buf, &n, fp) != -1) {
231 1.1 rmind in_addr_t addr, mask;
232 1.1 rmind
233 1.1 rmind if (*buf == '\n' || *buf == '#')
234 1.1 rmind continue;
235 1.1 rmind
236 1.1 rmind /* IPv4 CIDR: a.b.c.d/mask */
237 1.7 rmind if (!npfctl_parse_v4mask(buf, &addr, &mask)) {
238 1.1 rmind errx(EXIT_FAILURE, "invalid table entry at line %d", l);
239 1.7 rmind }
240 1.1 rmind
241 1.1 rmind /* Create and add table entry. */
242 1.7 rmind npf_table_add_entry(tl, addr, mask);
243 1.1 rmind l++;
244 1.1 rmind }
245 1.1 rmind if (buf != NULL) {
246 1.1 rmind free(buf);
247 1.1 rmind }
248 1.1 rmind }
249 1.1 rmind
250 1.1 rmind /*
251 1.7 rmind * N-code generation helpers.
252 1.1 rmind */
253 1.1 rmind
254 1.1 rmind static void
255 1.1 rmind npfctl_rulenc_v4cidr(void **nc, int nblocks[], var_t *dat, bool sd)
256 1.1 rmind {
257 1.1 rmind element_t *el = dat->v_elements;
258 1.1 rmind int foff;
259 1.1 rmind
260 1.1 rmind /* If table, generate a single table matching block. */
261 1.1 rmind if (dat->v_type == VAR_TABLE) {
262 1.1 rmind u_int tid = atoi(el->e_data);
263 1.1 rmind
264 1.1 rmind nblocks[0]--;
265 1.1 rmind foff = npfctl_failure_offset(nblocks);
266 1.1 rmind npfctl_gennc_tbl(nc, foff, tid, sd);
267 1.1 rmind return;
268 1.1 rmind }
269 1.1 rmind
270 1.1 rmind /* Generate v4 CIDR matching blocks. */
271 1.1 rmind for (el = dat->v_elements; el != NULL; el = el->e_next) {
272 1.1 rmind in_addr_t addr, mask;
273 1.1 rmind
274 1.1 rmind npfctl_parse_cidr(el->e_data, &addr, &mask);
275 1.1 rmind
276 1.1 rmind nblocks[1]--;
277 1.1 rmind foff = npfctl_failure_offset(nblocks);
278 1.1 rmind npfctl_gennc_v4cidr(nc, foff, addr, mask, sd);
279 1.1 rmind }
280 1.1 rmind }
281 1.1 rmind
282 1.1 rmind static void
283 1.5 rmind npfctl_rulenc_ports(void **nc, int nblocks[], var_t *dat, bool tcpudp,
284 1.5 rmind bool both, bool sd)
285 1.1 rmind {
286 1.1 rmind element_t *el = dat->v_elements;
287 1.1 rmind int foff;
288 1.1 rmind
289 1.1 rmind assert(dat->v_type != VAR_TABLE);
290 1.1 rmind
291 1.1 rmind /* Generate TCP/UDP port matching blocks. */
292 1.1 rmind for (el = dat->v_elements; el != NULL; el = el->e_next) {
293 1.3 rmind in_port_t fport, tport;
294 1.3 rmind bool range;
295 1.1 rmind
296 1.3 rmind if (!npfctl_parse_port(el->e_data, &range, &fport, &tport)) {
297 1.3 rmind errx(EXIT_FAILURE, "invalid service '%s'", el->e_data);
298 1.1 rmind }
299 1.1 rmind nblocks[0]--;
300 1.5 rmind foff = both ? 0 : npfctl_failure_offset(nblocks);
301 1.3 rmind npfctl_gennc_ports(nc, foff, fport, tport, tcpudp, sd);
302 1.1 rmind }
303 1.1 rmind }
304 1.1 rmind
305 1.1 rmind static void
306 1.1 rmind npfctl_rulenc_block(void **nc, int nblocks[], var_t *cidr, var_t *ports,
307 1.1 rmind bool both, bool tcpudp, bool sd)
308 1.1 rmind {
309 1.1 rmind
310 1.1 rmind npfctl_rulenc_v4cidr(nc, nblocks, cidr, sd);
311 1.1 rmind if (ports == NULL) {
312 1.1 rmind return;
313 1.1 rmind }
314 1.5 rmind npfctl_rulenc_ports(nc, nblocks, ports, tcpudp, both, sd);
315 1.1 rmind if (!both) {
316 1.1 rmind return;
317 1.1 rmind }
318 1.5 rmind npfctl_rulenc_ports(nc, nblocks, ports, !tcpudp, false, sd);
319 1.1 rmind }
320 1.1 rmind
321 1.1 rmind void
322 1.7 rmind npfctl_rule_ncode(nl_rule_t *rl, char *proto, char *tcpfl, int icmp_type,
323 1.7 rmind int icmp_code, var_t *from, var_t *fports, var_t *to, var_t *tports)
324 1.1 rmind {
325 1.7 rmind int nblocks[3] = { 0, 0, 0 };
326 1.1 rmind bool icmp, tcpudp, both;
327 1.1 rmind void *ncptr, *nc;
328 1.7 rmind size_t sz, foff;
329 1.1 rmind
330 1.1 rmind /*
331 1.1 rmind * Default: both TCP and UDP.
332 1.1 rmind */
333 1.1 rmind icmp = false;
334 1.1 rmind tcpudp = true;
335 1.1 rmind if (proto == NULL) {
336 1.5 rmind both = true;
337 1.1 rmind goto skip_proto;
338 1.1 rmind }
339 1.5 rmind both = false;
340 1.1 rmind
341 1.1 rmind if (strcmp(proto, "icmp") == 0) {
342 1.1 rmind /* ICMP case. */
343 1.1 rmind fports = NULL;
344 1.1 rmind tports = NULL;
345 1.1 rmind icmp = true;
346 1.1 rmind
347 1.1 rmind } else if (strcmp(proto, "tcp") == 0) {
348 1.1 rmind /* Just TCP. */
349 1.1 rmind tcpudp = true;
350 1.1 rmind
351 1.1 rmind } else if (strcmp(proto, "udp") == 0) {
352 1.1 rmind /* Just UDP. */
353 1.1 rmind tcpudp = false;
354 1.1 rmind
355 1.1 rmind } else {
356 1.1 rmind /* Default. */
357 1.1 rmind }
358 1.1 rmind skip_proto:
359 1.6 rmind if (icmp || icmp_type != -1) {
360 1.7 rmind assert(tcpfl == NULL);
361 1.3 rmind icmp = true;
362 1.3 rmind nblocks[2] += 1;
363 1.3 rmind }
364 1.7 rmind if (tcpudp && tcpfl) {
365 1.3 rmind assert(icmp_type == -1 && icmp_code == -1);
366 1.3 rmind nblocks[2] += 1;
367 1.3 rmind }
368 1.1 rmind
369 1.1 rmind /* Calculate how blocks to determince n-code. */
370 1.1 rmind if (from && from->v_count) {
371 1.1 rmind if (from->v_type == VAR_TABLE)
372 1.1 rmind nblocks[0] += 1;
373 1.1 rmind else
374 1.1 rmind nblocks[1] += from->v_count;
375 1.1 rmind if (fports && fports->v_count)
376 1.1 rmind nblocks[0] += fports->v_count * (both ? 2 : 1);
377 1.1 rmind }
378 1.1 rmind if (to && to->v_count) {
379 1.1 rmind if (to->v_type == VAR_TABLE)
380 1.1 rmind nblocks[0] += 1;
381 1.1 rmind else
382 1.1 rmind nblocks[1] += to->v_count;
383 1.1 rmind if (tports && tports->v_count)
384 1.1 rmind nblocks[0] += tports->v_count * (both ? 2 : 1);
385 1.1 rmind }
386 1.1 rmind
387 1.3 rmind /* Any n-code to generate? */
388 1.6 rmind if (!icmp && (nblocks[0] + nblocks[1] + nblocks[2]) == 0) {
389 1.3 rmind /* Done, if none. */
390 1.3 rmind return;
391 1.3 rmind }
392 1.3 rmind
393 1.1 rmind /* Allocate memory for the n-code. */
394 1.1 rmind sz = npfctl_calc_ncsize(nblocks);
395 1.1 rmind ncptr = malloc(sz);
396 1.1 rmind if (ncptr == NULL) {
397 1.7 rmind err(EXIT_FAILURE, "malloc");
398 1.1 rmind }
399 1.1 rmind nc = ncptr;
400 1.1 rmind
401 1.3 rmind /*
402 1.3 rmind * Generate v4 CIDR matching blocks and TCP/UDP port matching.
403 1.3 rmind */
404 1.1 rmind if (from) {
405 1.1 rmind npfctl_rulenc_block(&nc, nblocks, from, fports,
406 1.1 rmind both, tcpudp, true);
407 1.1 rmind }
408 1.1 rmind if (to) {
409 1.1 rmind npfctl_rulenc_block(&nc, nblocks, to, tports,
410 1.1 rmind both, tcpudp, false);
411 1.1 rmind }
412 1.3 rmind
413 1.1 rmind if (icmp) {
414 1.3 rmind /*
415 1.3 rmind * ICMP case.
416 1.3 rmind */
417 1.3 rmind nblocks[2]--;
418 1.3 rmind foff = npfctl_failure_offset(nblocks);
419 1.3 rmind npfctl_gennc_icmp(&nc, foff, icmp_type, icmp_code);
420 1.3 rmind
421 1.7 rmind } else if (tcpudp && tcpfl) {
422 1.3 rmind /*
423 1.3 rmind * TCP case, flags.
424 1.3 rmind */
425 1.3 rmind uint8_t tfl = 0, tfl_mask;
426 1.3 rmind
427 1.3 rmind nblocks[2]--;
428 1.3 rmind foff = npfctl_failure_offset(nblocks);
429 1.7 rmind if (!npfctl_parse_tcpfl(tcpfl, &tfl, &tfl_mask)) {
430 1.7 rmind errx(EXIT_FAILURE, "invalid TCP flags '%s'", tcpfl);
431 1.3 rmind }
432 1.3 rmind npfctl_gennc_tcpfl(&nc, foff, tfl, tfl_mask);
433 1.1 rmind }
434 1.1 rmind npfctl_gennc_complete(&nc);
435 1.1 rmind
436 1.3 rmind if ((uintptr_t)nc - (uintptr_t)ncptr != sz) {
437 1.2 jnemeth errx(EXIT_FAILURE, "n-code size got wrong (%tu != %zu)",
438 1.1 rmind (uintptr_t)nc - (uintptr_t)ncptr, sz);
439 1.3 rmind }
440 1.1 rmind
441 1.1 rmind #ifdef DEBUG
442 1.1 rmind uint32_t *op = ncptr;
443 1.1 rmind size_t n = sz;
444 1.1 rmind do {
445 1.1 rmind DPRINTF(("\t> |0x%02x|\n", (u_int)*op));
446 1.1 rmind op++;
447 1.1 rmind n -= sizeof(*op);
448 1.1 rmind } while (n);
449 1.1 rmind #endif
450 1.1 rmind
451 1.1 rmind /* Create a final memory block of data, ready to send. */
452 1.7 rmind if (npf_rule_setcode(rl, NPF_CODE_NCODE, ncptr, sz) == -1) {
453 1.7 rmind errx(EXIT_FAILURE, "npf_rule_setcode");
454 1.1 rmind }
455 1.1 rmind free(ncptr);
456 1.1 rmind }
457