xref: /netbsd-src/usr.sbin/npf/npfctl/npf_data.c (revision b757af438b42b93f8c6571f026d8b8ef3eaf5fc9)
1 /*	$NetBSD: npf_data.c,v 1.11 2012/02/26 21:50:05 christos Exp $	*/
2 
3 /*-
4  * Copyright (c) 2009-2012 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 /*
30  * npfctl(8) data manipulation and helper routines.
31  */
32 
33 #include <sys/cdefs.h>
34 __RCSID("$NetBSD: npf_data.c,v 1.11 2012/02/26 21:50:05 christos Exp $");
35 
36 #include <sys/types.h>
37 #include <sys/null.h>
38 
39 #include <netinet/in.h>
40 #include <netinet/in_systm.h>
41 #include <netinet/ip.h>
42 #define ICMP_STRINGS
43 #include <netinet/ip_icmp.h>
44 #include <netinet/tcp.h>
45 #include <net/if.h>
46 
47 #include <stdlib.h>
48 #include <string.h>
49 #include <err.h>
50 #include <errno.h>
51 #include <ifaddrs.h>
52 #include <netdb.h>
53 
54 #include "npfctl.h"
55 
56 static struct ifaddrs *		ifs_list = NULL;
57 
58 unsigned long
59 npfctl_find_ifindex(const char *ifname)
60 {
61 	return if_nametoindex(ifname);
62 }
63 
64 static bool
65 npfctl_copy_address(sa_family_t fam, npf_addr_t *addr, const void *ptr)
66 {
67 	switch (fam) {
68 	case AF_INET: {
69 		const struct sockaddr_in *sin = ptr;
70 		memcpy(addr, &sin->sin_addr, sizeof(sin->sin_addr));
71 		return true;
72 	}
73 	case AF_INET6: {
74 		const struct sockaddr_in6 *sin6 = ptr;
75 		memcpy(addr, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
76 		return true;
77 	}
78 	default:
79 		yyerror("unknown address family %u", fam);
80 		return false;
81 	}
82 }
83 
84 static bool
85 npfctl_parse_fam_addr(const char *name, sa_family_t *fam, npf_addr_t *addr)
86 {
87 	static const struct addrinfo hint = {
88 		.ai_family = AF_UNSPEC,
89 		.ai_flags = AI_NUMERICHOST
90 	};
91 	struct addrinfo *ai;
92 	int ret;
93 
94 	ret = getaddrinfo(name, NULL, &hint, &ai);
95 	if (ret) {
96 		yyerror("cannot parse '%s' (%s)", name, gai_strerror(ret));
97 		return false;
98 	}
99 	if (fam) {
100 		*fam = ai->ai_family;
101 	}
102 	if (!npfctl_copy_address(*fam, addr, ai->ai_addr)) {
103 		return false;
104 	}
105 	freeaddrinfo(ai);
106 	return true;
107 }
108 
109 static bool
110 npfctl_parse_mask(const char *s, sa_family_t fam, npf_netmask_t *mask)
111 {
112 	char *ep = NULL;
113 	npf_addr_t addr;
114 	uint8_t *ap;
115 
116 	if (s) {
117 		errno = 0;
118 		*mask = (npf_netmask_t)strtol(s, &ep, 0);
119 		if (*ep == '\0' && s != ep && errno != ERANGE)
120 			return true;
121 		if (!npfctl_parse_fam_addr(s, &fam, &addr))
122 			return false;
123 	}
124 
125 	switch (fam) {
126 	case AF_INET:
127 		*mask = 32;
128 		break;
129 	case AF_INET6:
130 		*mask = 128;
131 		break;
132 	default:
133 		yyerror("unknown address family %u", fam);
134 		return false;
135 	}
136 
137 	if (ep == NULL) {
138 		return true;
139 	}
140 	ap = addr.s6_addr + (*mask / 8) - 1;
141 	while (ap >= addr.s6_addr) {
142 		for (int j = 8; j > 0; j--) {
143 			if (*ap & 1)
144 				return true;
145 			*ap >>= 1;
146 			(*mask)--;
147 			if (*mask == 0)
148 				return true;
149 		}
150 		ap--;
151 	}
152 	return true;
153 }
154 
155 /*
156  * npfctl_parse_fam_addr_mask: return address family, address and mask.
157  *
158  * => Mask is optional and can be NULL.
159  * => Returns true on success or false if unable to parse.
160  */
161 npfvar_t *
162 npfctl_parse_fam_addr_mask(const char *addr, const char *mask,
163     unsigned long *nummask)
164 {
165 	npfvar_t *vp = npfvar_create(".addr");
166 	fam_addr_mask_t fam;
167 
168 	memset(&fam, 0, sizeof(fam));
169 
170 	if (!npfctl_parse_fam_addr(addr, &fam.fam_family, &fam.fam_addr))
171 		goto out;
172 
173 	/*
174 	 * Note: both mask and nummask may be NULL.  In such case,
175 	 * npfctl_parse_mask() will handle and will set full mask.
176 	 */
177 	if (nummask) {
178 		fam.fam_mask = *nummask;
179 	} else if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) {
180 		goto out;
181 	}
182 
183 	if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
184 		goto out;
185 
186 	return vp;
187 out:
188 	npfvar_destroy(vp);
189 	return NULL;
190 }
191 
192 npfvar_t *
193 npfctl_parse_table_id(const char *id)
194 {
195 	npfvar_t *vp;
196 
197 	if (!npfctl_table_exists_p(id)) {
198 		yyerror("table '%s' is not defined", id);
199 		return NULL;
200 	}
201 	vp = npfvar_create(".table");
202 
203 	if (!npfvar_add_element(vp, NPFVAR_TABLE, id, strlen(id) + 1))
204 		goto out;
205 
206 	return vp;
207 out:
208 	npfvar_destroy(vp);
209 	return NULL;
210 }
211 
212 /*
213  * npfctl_parse_port_range: create a port-range variable.  Note that the
214  * passed port numbers are in network byte order.
215  */
216 npfvar_t *
217 npfctl_parse_port_range(in_port_t s, in_port_t e)
218 {
219 	npfvar_t *vp = npfvar_create(".port_range");
220 	port_range_t pr;
221 
222 	pr.pr_start = s;
223 	pr.pr_end = e;
224 
225 	if (!npfvar_add_element(vp, NPFVAR_PORT_RANGE, &pr, sizeof(pr)))
226 		goto out;
227 
228 	return vp;
229 out:
230 	npfvar_destroy(vp);
231 	return NULL;
232 }
233 
234 npfvar_t *
235 npfctl_parse_port_range_variable(const char *v)
236 {
237 	npfvar_t *vp = npfvar_lookup(v);
238 	in_port_t p;
239 	port_range_t *pr;
240 	size_t count = npfvar_get_count(vp);
241 	npfvar_t *pvp = npfvar_create(".port_range");
242 
243 	for (size_t i = 0; i < count; i++) {
244 		int type = npfvar_get_type(vp, i);
245 		void *data = npfvar_get_data(vp, type, i);
246 		switch (type) {
247 		case NPFVAR_IDENTIFIER:
248 		case NPFVAR_STRING:
249 			p = npfctl_portno(data);
250 			npfvar_add_elements(pvp, npfctl_parse_port_range(p, p));
251 			break;
252 		case NPFVAR_PORT_RANGE:
253 			pr = data;
254 			npfvar_add_element(pvp, NPFVAR_PORT_RANGE, pr,
255 			    sizeof(*pr));
256 			break;
257 		case NPFVAR_NUM:
258 			p = *(unsigned long *)data;
259 			npfvar_add_elements(pvp, npfctl_parse_port_range(p, p));
260 			break;
261 		default:
262 			yyerror("wrong variable '%s' type '%s' for port range",
263 			    v, npfvar_type(type));
264 			goto out;
265 		}
266 	}
267 	return pvp;
268 out:
269 	npfvar_destroy(pvp);
270 	return NULL;
271 }
272 
273 npfvar_t *
274 npfctl_parse_iface(const char *ifname)
275 {
276 	npfvar_t *vp = npfvar_create(".iface");
277 	struct ifaddrs *ifa;
278 	fam_addr_mask_t fam;
279 	bool gotif = false;
280 
281 	if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) {
282 		err(EXIT_FAILURE, "getifaddrs");
283 	}
284 	memset(&fam, 0, sizeof(fam));
285 
286 	npfvar_t *ip = npfvar_create(".ifname");
287 	if (!npfvar_add_element(ip, NPFVAR_STRING, ifname, strlen(ifname) + 1))
288 		goto out;
289 
290 	for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) {
291 		struct sockaddr *sa;
292 		sa_family_t family;
293 
294 		if (strcmp(ifa->ifa_name, ifname) != 0)
295 			continue;
296 
297 		gotif = true;
298 		if ((ifa->ifa_flags & IFF_UP) == 0)
299 			warnx("interface '%s' is down", ifname);
300 
301 		sa = ifa->ifa_addr;
302 		family = sa->sa_family;
303 		if (family != AF_INET && family != AF_INET6)
304 			continue;
305 
306 		fam.fam_family = family;
307 		fam.fam_interface = ip;
308 
309 		if (!npfctl_copy_address(family, &fam.fam_addr, sa))
310 			goto out;
311 
312 		if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask))
313 			goto out;
314 
315 		if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
316 			goto out;
317 	}
318 	if (!gotif) {
319 		yyerror("interface '%s' not found", ifname);
320 		goto out;
321 	}
322 	if (npfvar_get_count(vp) == 0) {
323 		yyerror("no addresses matched for interface '%s'", ifname);
324 		goto out;
325 	}
326 	return vp;
327 out:
328 	npfvar_destroy(vp);
329 	npfvar_destroy(ip);
330 	return NULL;
331 }
332 
333 fam_addr_mask_t *
334 npfctl_parse_cidr(char *cidr)
335 {
336 	npfvar_t *vp;
337 	char *p;
338 
339 	p = strchr(cidr, '/');
340 	if (p) {
341 		*p++ = '\0';
342 	}
343 	vp = npfctl_parse_fam_addr_mask(cidr, p, NULL);
344 	if (vp == NULL) {
345 		return NULL;
346 	}
347 	return npfvar_get_data(vp, NPFVAR_FAM, 0);
348 }
349 
350 /*
351  * npfctl_portno: convert port identifier (string) to a number.
352  *
353  * => Returns port number in network byte order.
354  */
355 in_port_t
356 npfctl_portno(const char *port)
357 {
358 	struct addrinfo *ai, *rai;
359 	in_port_t p = 0;
360 	int e;
361 
362 	e = getaddrinfo(NULL, port, NULL, &rai);
363 	if (e != 0) {
364 		yyerror("invalid port name: '%s' (%s)", port, gai_strerror(e));
365 		return 0;
366 	}
367 
368 	for (ai = rai; ai; ai = ai->ai_next) {
369 		switch (ai->ai_family) {
370 		case AF_INET: {
371 			struct sockaddr_in *sin = (void *)ai->ai_addr;
372 			p = sin->sin_port;
373 			goto out;
374 		}
375 		case AF_INET6: {
376 			struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;
377 			p = sin6->sin6_port;
378 			goto out;
379 		}
380 		default:
381 			break;
382 		}
383 	}
384 out:
385 	freeaddrinfo(rai);
386 	return p;
387 }
388 
389 npfvar_t *
390 npfctl_parse_tcpflag(const char *s)
391 {
392 	uint8_t tfl = 0;
393 
394 	while (*s) {
395 		switch (*s) {
396 		case 'F': tfl |= TH_FIN; break;
397 		case 'S': tfl |= TH_SYN; break;
398 		case 'R': tfl |= TH_RST; break;
399 		case 'P': tfl |= TH_PUSH; break;
400 		case 'A': tfl |= TH_ACK; break;
401 		case 'U': tfl |= TH_URG; break;
402 		case 'E': tfl |= TH_ECE; break;
403 		case 'W': tfl |= TH_CWR; break;
404 		default:
405 			yyerror("invalid flag '%c'", *s);
406 			return NULL;
407 		}
408 		s++;
409 	}
410 
411 	npfvar_t *vp = npfvar_create(".tcp_flag");
412 	if (!npfvar_add_element(vp, NPFVAR_TCPFLAG, &tfl, sizeof(tfl))) {
413 		npfvar_destroy(vp);
414 		return NULL;
415 	}
416 
417 	return vp;
418 }
419 
420 uint8_t
421 npfctl_icmptype(const char *type)
422 {
423 	for (uint8_t ul = 0; icmp_type[ul]; ul++)
424 		if (strcmp(icmp_type[ul], type) == 0)
425 			return ul;
426 	return ~0;
427 }
428 
429 uint8_t
430 npfctl_icmpcode(uint8_t type, const char *code)
431 {
432 	const char **arr;
433 
434 	switch (type) {
435 	case ICMP_ECHOREPLY:
436 	case ICMP_SOURCEQUENCH:
437 	case ICMP_ALTHOSTADDR:
438 	case ICMP_ECHO:
439 	case ICMP_ROUTERSOLICIT:
440 	case ICMP_TSTAMP:
441 	case ICMP_TSTAMPREPLY:
442 	case ICMP_IREQ:
443 	case ICMP_IREQREPLY:
444 	case ICMP_MASKREQ:
445 	case ICMP_MASKREPLY:
446 		arr = icmp_code_none;
447 		break;
448 	case ICMP_ROUTERADVERT:
449 		arr = icmp_code_routeradvert;
450 		break;
451 	case ICMP_UNREACH:
452 		arr = icmp_code_unreach;
453 		break;
454 	case ICMP_REDIRECT:
455 		arr = icmp_code_redirect;
456 		break;
457 	case ICMP_TIMXCEED:
458 		arr = icmp_code_timxceed;
459 		break;
460 	case ICMP_PARAMPROB:
461 		arr = icmp_code_paramprob;
462 		break;
463 	case ICMP_PHOTURIS:
464 		arr = icmp_code_photuris;
465 		break;
466 	default:
467 		return ~0;
468 	}
469 
470 	for (uint8_t ul = 0; arr[ul]; ul++) {
471 		if (strcmp(arr[ul], code) == 0)
472 			return ul;
473 	}
474 	return ~0;
475 }
476 
477 npfvar_t *
478 npfctl_parse_icmp(uint8_t type, uint8_t code)
479 {
480 	npfvar_t *vp = npfvar_create(".icmp");
481 
482 	if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type)))
483 		goto out;
484 
485 	if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code)))
486 		goto out;
487 
488 	return vp;
489 out:
490 	npfvar_destroy(vp);
491 	return NULL;
492 }
493