Home | History | Annotate | Line # | Download | only in npfctl
npf_data.c revision 1.17
      1 /*	$NetBSD: npf_data.c,v 1.17 2012/07/19 22:22:53 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.17 2012/07/19 22:22:53 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 #define ICMP6_STRINGS
     45 #include <netinet/icmp6.h>
     46 #include <netinet/tcp.h>
     47 #include <net/if.h>
     48 
     49 #include <stdlib.h>
     50 #include <string.h>
     51 #include <err.h>
     52 #include <errno.h>
     53 #include <ifaddrs.h>
     54 #include <netdb.h>
     55 
     56 #include "npfctl.h"
     57 
     58 static struct ifaddrs *		ifs_list = NULL;
     59 
     60 unsigned long
     61 npfctl_find_ifindex(const char *ifname)
     62 {
     63 	return if_nametoindex(ifname);
     64 }
     65 
     66 static bool
     67 npfctl_copy_address(sa_family_t fam, npf_addr_t *addr, const void *ptr)
     68 {
     69 	memset(addr, 0, sizeof(npf_addr_t));
     70 
     71 	switch (fam) {
     72 	case AF_INET: {
     73 		const struct sockaddr_in *sin = ptr;
     74 		memcpy(addr, &sin->sin_addr, sizeof(sin->sin_addr));
     75 		return true;
     76 	}
     77 	case AF_INET6: {
     78 		const struct sockaddr_in6 *sin6 = ptr;
     79 		memcpy(addr, &sin6->sin6_addr, sizeof(sin6->sin6_addr));
     80 		return true;
     81 	}
     82 	default:
     83 		yyerror("unknown address family %u", fam);
     84 		return false;
     85 	}
     86 }
     87 
     88 static bool
     89 npfctl_parse_fam_addr(const char *name, sa_family_t *fam, npf_addr_t *addr)
     90 {
     91 	static const struct addrinfo hint = {
     92 		.ai_family = AF_UNSPEC,
     93 		.ai_flags = AI_NUMERICHOST
     94 	};
     95 	struct addrinfo *ai;
     96 	int ret;
     97 
     98 	ret = getaddrinfo(name, NULL, &hint, &ai);
     99 	if (ret) {
    100 		yyerror("cannot parse '%s' (%s)", name, gai_strerror(ret));
    101 		return false;
    102 	}
    103 	if (fam) {
    104 		*fam = ai->ai_family;
    105 	}
    106 	if (!npfctl_copy_address(*fam, addr, ai->ai_addr)) {
    107 		return false;
    108 	}
    109 	freeaddrinfo(ai);
    110 	return true;
    111 }
    112 
    113 static bool
    114 npfctl_parse_mask(const char *s, sa_family_t fam, npf_netmask_t *mask)
    115 {
    116 	char *ep = NULL;
    117 	npf_addr_t addr;
    118 	uint8_t *ap;
    119 
    120 	if (s) {
    121 		errno = 0;
    122 		*mask = (npf_netmask_t)strtol(s, &ep, 0);
    123 		if (*ep == '\0' && s != ep && errno != ERANGE)
    124 			return true;
    125 		if (!npfctl_parse_fam_addr(s, &fam, &addr))
    126 			return false;
    127 	}
    128 
    129 	assert(fam == AF_INET || fam == AF_INET6);
    130 	*mask = NPF_NO_NETMASK;
    131 	if (ep == NULL) {
    132 		return true;
    133 	}
    134 
    135 	ap = addr.s6_addr + (*mask / 8) - 1;
    136 	while (ap >= addr.s6_addr) {
    137 		for (int j = 8; j > 0; j--) {
    138 			if (*ap & 1)
    139 				return true;
    140 			*ap >>= 1;
    141 			(*mask)--;
    142 			if (*mask == 0)
    143 				return true;
    144 		}
    145 		ap--;
    146 	}
    147 	return true;
    148 }
    149 
    150 /*
    151  * npfctl_parse_fam_addr_mask: return address family, address and mask.
    152  *
    153  * => Mask is optional and can be NULL.
    154  * => Returns true on success or false if unable to parse.
    155  */
    156 npfvar_t *
    157 npfctl_parse_fam_addr_mask(const char *addr, const char *mask,
    158     unsigned long *nummask)
    159 {
    160 	npfvar_t *vp = npfvar_create(".addr");
    161 	fam_addr_mask_t fam;
    162 
    163 	memset(&fam, 0, sizeof(fam));
    164 
    165 	if (!npfctl_parse_fam_addr(addr, &fam.fam_family, &fam.fam_addr))
    166 		goto out;
    167 
    168 	/*
    169 	 * Note: both mask and nummask may be NULL.  In such case,
    170 	 * npfctl_parse_mask() will handle and will set full mask.
    171 	 */
    172 	if (nummask) {
    173 		fam.fam_mask = *nummask;
    174 	} else if (!npfctl_parse_mask(mask, fam.fam_family, &fam.fam_mask)) {
    175 		goto out;
    176 	}
    177 
    178 	if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
    179 		goto out;
    180 
    181 	return vp;
    182 out:
    183 	npfvar_destroy(vp);
    184 	return NULL;
    185 }
    186 
    187 npfvar_t *
    188 npfctl_parse_table_id(const char *id)
    189 {
    190 	npfvar_t *vp;
    191 
    192 	if (!npfctl_table_exists_p(id)) {
    193 		yyerror("table '%s' is not defined", id);
    194 		return NULL;
    195 	}
    196 	vp = npfvar_create(".table");
    197 
    198 	if (!npfvar_add_element(vp, NPFVAR_TABLE, id, strlen(id) + 1))
    199 		goto out;
    200 
    201 	return vp;
    202 out:
    203 	npfvar_destroy(vp);
    204 	return NULL;
    205 }
    206 
    207 /*
    208  * npfctl_parse_port_range: create a port-range variable.  Note that the
    209  * passed port numbers should be in host byte order.
    210  */
    211 npfvar_t *
    212 npfctl_parse_port_range(in_port_t s, in_port_t e)
    213 {
    214 	npfvar_t *vp = npfvar_create(".port_range");
    215 	port_range_t pr;
    216 
    217 	pr.pr_start = htons(s);
    218 	pr.pr_end = htons(e);
    219 
    220 	if (!npfvar_add_element(vp, NPFVAR_PORT_RANGE, &pr, sizeof(pr)))
    221 		goto out;
    222 
    223 	return vp;
    224 out:
    225 	npfvar_destroy(vp);
    226 	return NULL;
    227 }
    228 
    229 npfvar_t *
    230 npfctl_parse_port_range_variable(const char *v)
    231 {
    232 	npfvar_t *vp = npfvar_lookup(v);
    233 	size_t count = npfvar_get_count(vp);
    234 	npfvar_t *pvp = npfvar_create(".port_range");
    235 	port_range_t *pr;
    236 	in_port_t p;
    237 
    238 	for (size_t i = 0; i < count; i++) {
    239 		int type = npfvar_get_type(vp, i);
    240 		void *data = npfvar_get_data(vp, type, i);
    241 
    242 		switch (type) {
    243 		case NPFVAR_IDENTIFIER:
    244 		case NPFVAR_STRING:
    245 			p = npfctl_portno(data);
    246 			npfvar_add_elements(pvp, npfctl_parse_port_range(p, p));
    247 			break;
    248 		case NPFVAR_PORT_RANGE:
    249 			pr = data;
    250 			npfvar_add_element(pvp, NPFVAR_PORT_RANGE, pr,
    251 			    sizeof(*pr));
    252 			break;
    253 		case NPFVAR_NUM:
    254 			p = *(unsigned long *)data;
    255 			npfvar_add_elements(pvp, npfctl_parse_port_range(p, p));
    256 			break;
    257 		default:
    258 			yyerror("wrong variable '%s' type '%s' for port range",
    259 			    v, npfvar_type(type));
    260 			npfvar_destroy(pvp);
    261 			return NULL;
    262 		}
    263 	}
    264 	return pvp;
    265 }
    266 
    267 npfvar_t *
    268 npfctl_parse_iface(const char *ifname)
    269 {
    270 	npfvar_t *vp = npfvar_create(".iface");
    271 	struct ifaddrs *ifa;
    272 	fam_addr_mask_t fam;
    273 	bool gotif = false;
    274 
    275 	if (ifs_list == NULL && getifaddrs(&ifs_list) == -1) {
    276 		err(EXIT_FAILURE, "getifaddrs");
    277 	}
    278 	memset(&fam, 0, sizeof(fam));
    279 
    280 	npfvar_t *ip = npfvar_create(".ifname");
    281 	if (!npfvar_add_element(ip, NPFVAR_STRING, ifname, strlen(ifname) + 1))
    282 		goto out;
    283 
    284 	for (ifa = ifs_list; ifa != NULL; ifa = ifa->ifa_next) {
    285 		struct sockaddr *sa;
    286 		sa_family_t family;
    287 
    288 		if (strcmp(ifa->ifa_name, ifname) != 0)
    289 			continue;
    290 
    291 		gotif = true;
    292 		if ((ifa->ifa_flags & IFF_UP) == 0)
    293 			warnx("interface '%s' is down", ifname);
    294 
    295 		sa = ifa->ifa_addr;
    296 		family = sa->sa_family;
    297 		if (family != AF_INET && family != AF_INET6)
    298 			continue;
    299 
    300 		fam.fam_family = family;
    301 		fam.fam_interface = ip;
    302 
    303 		if (!npfctl_copy_address(family, &fam.fam_addr, sa))
    304 			goto out;
    305 
    306 		if (!npfctl_parse_mask(NULL, fam.fam_family, &fam.fam_mask))
    307 			goto out;
    308 
    309 		if (!npfvar_add_element(vp, NPFVAR_FAM, &fam, sizeof(fam)))
    310 			goto out;
    311 	}
    312 	if (!gotif) {
    313 		yyerror("interface '%s' not found", ifname);
    314 		goto out;
    315 	}
    316 	if (npfvar_get_count(vp) == 0) {
    317 		yyerror("no addresses matched for interface '%s'", ifname);
    318 		goto out;
    319 	}
    320 	return vp;
    321 out:
    322 	npfvar_destroy(vp);
    323 	npfvar_destroy(ip);
    324 	return NULL;
    325 }
    326 
    327 bool
    328 npfctl_parse_cidr(char *cidr, fam_addr_mask_t *fam, int *alen)
    329 {
    330 	char *mask, *p;
    331 
    332 	p = strchr(cidr, '\n');
    333 	if (p) {
    334 		*p = '\0';
    335 	}
    336 	mask = strchr(cidr, '/');
    337 	if (mask) {
    338 		*mask++ = '\0';
    339 	}
    340 
    341 	memset(fam, 0, sizeof(*fam));
    342 	if (!npfctl_parse_fam_addr(cidr, &fam->fam_family, &fam->fam_addr)) {
    343 		return false;
    344 	}
    345 	if (!npfctl_parse_mask(mask, fam->fam_family, &fam->fam_mask)) {
    346 		return false;
    347 	}
    348 	switch (fam->fam_family) {
    349 	case AF_INET:
    350 		*alen = sizeof(struct in_addr);
    351 		break;
    352 	case AF_INET6:
    353 		*alen = sizeof(struct in6_addr);
    354 		break;
    355 	default:
    356 		return false;
    357 	}
    358 	return true;
    359 }
    360 
    361 int
    362 npfctl_protono(const char *proto)
    363 {
    364 	struct protoent *pe;
    365 
    366 	pe = getprotobyname(proto);
    367 	if (pe == NULL) {
    368 		yyerror("unknown protocol '%s'", proto);
    369 		return -1;
    370 	}
    371 	return pe->p_proto;
    372 }
    373 
    374 /*
    375  * npfctl_portno: convert port identifier (string) to a number.
    376  *
    377  * => Returns port number in host byte order.
    378  */
    379 in_port_t
    380 npfctl_portno(const char *port)
    381 {
    382 	struct addrinfo *ai, *rai;
    383 	in_port_t p = 0;
    384 	int e;
    385 
    386 	e = getaddrinfo(NULL, port, NULL, &rai);
    387 	if (e != 0) {
    388 		yyerror("invalid port name '%s' (%s)", port, gai_strerror(e));
    389 		return 0;
    390 	}
    391 
    392 	for (ai = rai; ai; ai = ai->ai_next) {
    393 		switch (ai->ai_family) {
    394 		case AF_INET: {
    395 			struct sockaddr_in *sin = (void *)ai->ai_addr;
    396 			p = sin->sin_port;
    397 			goto out;
    398 		}
    399 		case AF_INET6: {
    400 			struct sockaddr_in6 *sin6 = (void *)ai->ai_addr;
    401 			p = sin6->sin6_port;
    402 			goto out;
    403 		}
    404 		default:
    405 			break;
    406 		}
    407 	}
    408 out:
    409 	freeaddrinfo(rai);
    410 	return ntohs(p);
    411 }
    412 
    413 npfvar_t *
    414 npfctl_parse_tcpflag(const char *s)
    415 {
    416 	uint8_t tfl = 0;
    417 
    418 	while (*s) {
    419 		switch (*s) {
    420 		case 'F': tfl |= TH_FIN; break;
    421 		case 'S': tfl |= TH_SYN; break;
    422 		case 'R': tfl |= TH_RST; break;
    423 		case 'P': tfl |= TH_PUSH; break;
    424 		case 'A': tfl |= TH_ACK; break;
    425 		case 'U': tfl |= TH_URG; break;
    426 		case 'E': tfl |= TH_ECE; break;
    427 		case 'W': tfl |= TH_CWR; break;
    428 		default:
    429 			yyerror("invalid flag '%c'", *s);
    430 			return NULL;
    431 		}
    432 		s++;
    433 	}
    434 
    435 	npfvar_t *vp = npfvar_create(".tcp_flag");
    436 	if (!npfvar_add_element(vp, NPFVAR_TCPFLAG, &tfl, sizeof(tfl))) {
    437 		npfvar_destroy(vp);
    438 		return NULL;
    439 	}
    440 
    441 	return vp;
    442 }
    443 
    444 uint8_t
    445 npfctl_icmptype(int proto, const char *type)
    446 {
    447 	uint8_t ul;
    448 
    449 	switch (proto) {
    450 	case IPPROTO_ICMP:
    451 		for (ul = 0; icmp_type[ul]; ul++)
    452 			if (strcmp(icmp_type[ul], type) == 0)
    453 				return ul;
    454 		break;
    455 	case IPPROTO_ICMPV6:
    456 		for (ul = 0; icmp6_type_err[ul]; ul++)
    457 			if (strcmp(icmp6_type_err[ul], type) == 0)
    458 				return ul;
    459 		for (ul = 0; icmp6_type_info[ul]; ul++)
    460 			if (strcmp(icmp6_type_info[ul], type) == 0)
    461 				return (ul+128);
    462 		break;
    463 	default:
    464 		assert(false);
    465 	}
    466 
    467 	yyerror("unknown icmp-type %s", type);
    468 	return ~0;
    469 }
    470 
    471 uint8_t
    472 npfctl_icmpcode(int proto, uint8_t type, const char *code)
    473 {
    474 	const char * const *arr;
    475 
    476 	switch (proto) {
    477 	case IPPROTO_ICMP:
    478 		switch (type) {
    479 		case ICMP_ECHOREPLY:
    480 		case ICMP_SOURCEQUENCH:
    481 		case ICMP_ALTHOSTADDR:
    482 		case ICMP_ECHO:
    483 		case ICMP_ROUTERSOLICIT:
    484 		case ICMP_TSTAMP:
    485 		case ICMP_TSTAMPREPLY:
    486 		case ICMP_IREQ:
    487 		case ICMP_IREQREPLY:
    488 		case ICMP_MASKREQ:
    489 		case ICMP_MASKREPLY:
    490 			arr = icmp_code_none;
    491 			break;
    492 		case ICMP_ROUTERADVERT:
    493 			arr = icmp_code_routeradvert;
    494 			break;
    495 		case ICMP_UNREACH:
    496 			arr = icmp_code_unreach;
    497 			break;
    498 		case ICMP_REDIRECT:
    499 			arr = icmp_code_redirect;
    500 			break;
    501 		case ICMP_TIMXCEED:
    502 			arr = icmp_code_timxceed;
    503 			break;
    504 		case ICMP_PARAMPROB:
    505 			arr = icmp_code_paramprob;
    506 			break;
    507 		case ICMP_PHOTURIS:
    508 			arr = icmp_code_photuris;
    509 			break;
    510 		default:
    511 			yyerror("unknown icmp-type %d while parsing code %s",
    512 				type, code);
    513 			return ~0;
    514 		}
    515 		break;
    516 	case IPPROTO_ICMPV6:
    517 		switch (type) {
    518 		case ICMP6_DST_UNREACH:
    519 			arr = icmp6_code_unreach;
    520 			break;
    521 		case ICMP6_TIME_EXCEEDED:
    522 			arr = icmp6_code_timxceed;
    523 			break;
    524 		case ICMP6_PARAM_PROB:
    525 			arr = icmp6_code_paramprob;
    526 			break;
    527 		case ICMP6_PACKET_TOO_BIG:
    528 		/* code-less info ICMPs */
    529 		case ICMP6_ECHO_REQUEST:
    530 		case ICMP6_ECHO_REPLY:
    531 		case MLD_LISTENER_QUERY:
    532 		case MLD_LISTENER_REPORT:
    533 		case MLD_LISTENER_DONE:
    534 		case ND_ROUTER_SOLICIT:
    535 		case ND_ROUTER_ADVERT:
    536 		case ND_NEIGHBOR_SOLICIT:
    537 		case ND_NEIGHBOR_ADVERT:
    538 		case ND_REDIRECT:
    539 			arr = icmp6_code_none;
    540 			break;
    541 		/* XXX TODO: info ICMPs with code values */
    542 		default:
    543 			yyerror("unknown icmp-type %d while parsing code %s",
    544 				type, code);
    545 			return ~0;
    546 		}
    547 		break;
    548 	default:
    549 		assert(false);
    550 	}
    551 
    552 	for (uint8_t ul = 0; arr[ul]; ul++) {
    553 		if (strcmp(arr[ul], code) == 0)
    554 			return ul;
    555 	}
    556 	yyerror("unknown code %s for icmp-type %d", code, type);
    557 	return ~0;
    558 }
    559 
    560 npfvar_t *
    561 npfctl_parse_icmp(int proto, int type, int code)
    562 {
    563 	npfvar_t *vp=npfvar_create(".icmp");
    564 	int      varnum;
    565 
    566 	switch (proto) {
    567 	case IPPROTO_ICMP:
    568 		varnum = NPFVAR_ICMP;
    569 		break;
    570 	case IPPROTO_ICMPV6:
    571 		varnum = NPFVAR_ICMP6;
    572 		break;
    573 	default:
    574 		assert(false);
    575 	}
    576 
    577 	if (!npfvar_add_element(vp, varnum, &type, sizeof(type)))
    578 		goto out;
    579 
    580 	if (!npfvar_add_element(vp, varnum, &code, sizeof(code)))
    581 		goto out;
    582 
    583 	return vp;
    584 out:
    585 	npfvar_destroy(vp);
    586 	return NULL;
    587 }
    588 
    589