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