xref: /netbsd-src/usr.sbin/npf/npfctl/npf_data.c (revision e8bec33be121040b935e764acaa45ddbf4d7353c)
1 /*	$NetBSD: npf_data.c,v 1.10 2012/01/08 21:34:21 rmind 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.10 2012/01/08 21:34:21 rmind 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_iface(const char *ifname)
236 {
237 	npfvar_t *vp = npfvar_create(".iface");
238 	struct ifaddrs *ifa;
239 	fam_addr_mask_t fam;
240 	bool gotif = false;
241 
242 	if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) {
243 		err(EXIT_FAILURE, "getifaddrs");
244 	}
245 	memset(&fam, 0, sizeof(fam));
246 
247 	npfvar_t *ip = npfvar_create(".ifname");
248 	if (!npfvar_add_element(ip, NPFVAR_STRING, ifname, strlen(ifname) + 1))
249 		goto out;
250 
251 	for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) {
252 		struct sockaddr *sa;
253 		sa_family_t family;
254 
255 		if (strcmp(ifa->ifa_name, ifname) != 0)
256 			continue;
257 
258 		gotif = true;
259 		if ((ifa->ifa_flags & IFF_UP) == 0)
260 			warnx("interface '%s' is down", ifname);
261 
262 		sa = ifa->ifa_addr;
263 		family = sa->sa_family;
264 		if (family != AF_INET && family != AF_INET6)
265 			continue;
266 
267 		fam.fam_family = family;
268 		fam.fam_interface = ip;
269 
270 		if (!npfctl_copy_address(family, &fam.fam_addr, sa))
271 			goto out;
272 
273 		if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask))
274 			goto out;
275 
276 		if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
277 			goto out;
278 	}
279 	if (!gotif) {
280 		yyerror("interface '%s' not found", ifname);
281 		goto out;
282 	}
283 	if (npfvar_get_count(vp) == 0) {
284 		yyerror("no addresses matched for interface '%s'", ifname);
285 		goto out;
286 	}
287 	return vp;
288 out:
289 	npfvar_destroy(vp);
290 	npfvar_destroy(ip);
291 	return NULL;
292 }
293 
294 fam_addr_mask_t *
295 npfctl_parse_cidr(char *cidr)
296 {
297 	npfvar_t *vp;
298 	char *p;
299 
300 	p = strchr(cidr, '/');
301 	if (p) {
302 		*p++ = '\0';
303 	}
304 	vp = npfctl_parse_fam_addr_mask(cidr, p, NULL);
305 	if (vp == NULL) {
306 		return NULL;
307 	}
308 	return npfvar_get_data(vp, NPFVAR_FAM, 0);
309 }
310 
311 /*
312  * npfctl_portno: convert port identifier (string) to a number.
313  *
314  * => Returns port number in network byte order.
315  */
316 in_port_t
317 npfctl_portno(const char *port)
318 {
319 	struct addrinfo *ai, *rai;
320 	in_port_t p = 0;
321 	int e;
322 
323 	e = getaddrinfo(NULL, port, NULL, &rai);
324 	if (e != 0) {
325 		yyerror("invalid port name: '%s' (%s)", port, gai_strerror(e));
326 		return 0;
327 	}
328 
329 	for (ai = rai; ai; ai = ai->ai_next) {
330 		switch (ai->ai_family) {
331 		case AF_INET: {
332 			struct sockaddr_in *sin = (void *)ai->ai_addr;
333 			p = sin->sin_port;
334 			goto out;
335 		}
336 		case AF_INET6: {
337 			struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;
338 			p = sin6->sin6_port;
339 			goto out;
340 		}
341 		default:
342 			break;
343 		}
344 	}
345 out:
346 	freeaddrinfo(rai);
347 	return p;
348 }
349 
350 npfvar_t *
351 npfctl_parse_tcpflag(const char *s)
352 {
353 	uint8_t tfl = 0;
354 
355 	while (*s) {
356 		switch (*s) {
357 		case 'F': tfl |= TH_FIN; break;
358 		case 'S': tfl |= TH_SYN; break;
359 		case 'R': tfl |= TH_RST; break;
360 		case 'P': tfl |= TH_PUSH; break;
361 		case 'A': tfl |= TH_ACK; break;
362 		case 'U': tfl |= TH_URG; break;
363 		case 'E': tfl |= TH_ECE; break;
364 		case 'W': tfl |= TH_CWR; break;
365 		default:
366 			yyerror("invalid flag '%c'", *s);
367 			return NULL;
368 		}
369 		s++;
370 	}
371 
372 	npfvar_t *vp = npfvar_create(".tcp_flag");
373 	if (!npfvar_add_element(vp, NPFVAR_TCPFLAG, &tfl, sizeof(tfl))) {
374 		npfvar_destroy(vp);
375 		return NULL;
376 	}
377 
378 	return vp;
379 }
380 
381 uint8_t
382 npfctl_icmptype(const char *type)
383 {
384 	for (uint8_t ul = 0; icmp_type[ul]; ul++)
385 		if (strcmp(icmp_type[ul], type) == 0)
386 			return ul;
387 	return ~0;
388 }
389 
390 uint8_t
391 npfctl_icmpcode(uint8_t type, const char *code)
392 {
393 	const char **arr;
394 
395 	switch (type) {
396 	case ICMP_ECHOREPLY:
397 	case ICMP_SOURCEQUENCH:
398 	case ICMP_ALTHOSTADDR:
399 	case ICMP_ECHO:
400 	case ICMP_ROUTERSOLICIT:
401 	case ICMP_TSTAMP:
402 	case ICMP_TSTAMPREPLY:
403 	case ICMP_IREQ:
404 	case ICMP_IREQREPLY:
405 	case ICMP_MASKREQ:
406 	case ICMP_MASKREPLY:
407 		arr = icmp_code_none;
408 		break;
409 	case ICMP_ROUTERADVERT:
410 		arr = icmp_code_routeradvert;
411 		break;
412 	case ICMP_UNREACH:
413 		arr = icmp_code_unreach;
414 		break;
415 	case ICMP_REDIRECT:
416 		arr = icmp_code_redirect;
417 		break;
418 	case ICMP_TIMXCEED:
419 		arr = icmp_code_timxceed;
420 		break;
421 	case ICMP_PARAMPROB:
422 		arr = icmp_code_paramprob;
423 		break;
424 	case ICMP_PHOTURIS:
425 		arr = icmp_code_photuris;
426 		break;
427 	default:
428 		return ~0;
429 	}
430 
431 	for (uint8_t ul = 0; arr[ul]; ul++) {
432 		if (strcmp(arr[ul], code) == 0)
433 			return ul;
434 	}
435 	return ~0;
436 }
437 
438 npfvar_t *
439 npfctl_parse_icmp(uint8_t type, uint8_t code)
440 {
441 	npfvar_t *vp = npfvar_create(".icmp");
442 
443 	if (!npfvar_add_element(vp, NPFVAR_ICMP, &type, sizeof(type)))
444 		goto out;
445 
446 	if (!npfvar_add_element(vp, NPFVAR_ICMP, &code, sizeof(code)))
447 		goto out;
448 
449 	return vp;
450 out:
451 	npfvar_destroy(vp);
452 	return NULL;
453 }
454