ipfw2.c revision 187770
198943Sluigi/* 2117328Sluigi * Copyright (c) 2002-2003 Luigi Rizzo 398943Sluigi * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 498943Sluigi * Copyright (c) 1994 Ugen J.S.Antsilevich 598943Sluigi * 698943Sluigi * Idea and grammar partially left from: 798943Sluigi * Copyright (c) 1993 Daniel Boulet 898943Sluigi * 998943Sluigi * Redistribution and use in source forms, with and without modification, 1098943Sluigi * are permitted provided that this entire comment appears intact. 1198943Sluigi * 1298943Sluigi * Redistribution in binary form may occur without any restrictions. 1398943Sluigi * Obviously, it would be nice if you gave credit where credit is due 1498943Sluigi * but requiring it would be too onerous. 1598943Sluigi * 1698943Sluigi * This software is provided ``AS IS'' without any warranties of any kind. 1798943Sluigi * 1898943Sluigi * NEW command line interface for IP firewall facility 1998943Sluigi * 2098943Sluigi * $FreeBSD: head/sbin/ipfw/ipfw2.c 187770 2009-01-27 12:01:30Z luigi $ 2198943Sluigi */ 2298943Sluigi 23187604Sluigi#include <sys/types.h> 2498943Sluigi#include <sys/socket.h> 2598943Sluigi#include <sys/sockio.h> 2698943Sluigi#include <sys/sysctl.h> 2798943Sluigi 28187767Sluigi#include "ipfw2.h" 29187767Sluigi 3098943Sluigi#include <ctype.h> 3198943Sluigi#include <err.h> 3298943Sluigi#include <errno.h> 3398943Sluigi#include <grp.h> 3498943Sluigi#include <netdb.h> 3598943Sluigi#include <pwd.h> 3698943Sluigi#include <stdio.h> 3798943Sluigi#include <stdlib.h> 3898943Sluigi#include <string.h> 3998943Sluigi#include <sysexits.h> 40187604Sluigi#include <timeconv.h> /* _long_to_time */ 41136071Sgreen#include <unistd.h> 42136071Sgreen#include <fcntl.h> 4398943Sluigi 44175659Srwatson#define IPFW_INTERNAL /* Access to protected structures in ip_fw.h. */ 45175659Srwatson 46169424Smaxim#include <net/ethernet.h> 4798943Sluigi#include <net/if.h> 48165648Spiso#include <net/if_dl.h> 49136071Sgreen#include <net/pfvar.h> 50145246Sbrooks#include <net/route.h> /* def. of struct route */ 5198943Sluigi#include <netinet/in.h> 5298943Sluigi#include <netinet/in_systm.h> 5398943Sluigi#include <netinet/ip.h> 5498943Sluigi#include <netinet/ip_icmp.h> 5598943Sluigi#include <netinet/ip_fw.h> 5698943Sluigi#include <netinet/tcp.h> 5798943Sluigi#include <arpa/inet.h> 5898943Sluigi 59187767Sluigistruct cmdline_opts co; /* global options */ 6098943Sluigi 61187767Sluigiint resvd_set_number = RESVD_SET; 62187764Sluigi 63159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ 64159636Soleg if (!ac) \ 65159636Soleg errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ 66159636Soleg if (_substrcmp(*av, "tablearg") == 0) { \ 67159636Soleg arg = IP_FW_TABLEARG; \ 68159636Soleg break; \ 69159636Soleg } \ 70159636Soleg \ 71159636Soleg { \ 72159636Soleg long val; \ 73159636Soleg char *end; \ 74159636Soleg \ 75159636Soleg val = strtol(*av, &end, 10); \ 76159636Soleg \ 77159636Soleg if (!isdigit(**av) || *end != '\0' || (val == 0 && errno == EINVAL)) \ 78159636Soleg errx(EX_DATAERR, "%s: invalid argument: %s", \ 79159636Soleg match_value(s_x, tok), *av); \ 80159636Soleg \ 81159636Soleg if (errno == ERANGE || val < min || val > max) \ 82159636Soleg errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \ 83159636Soleg match_value(s_x, tok), min, max, *av); \ 84159636Soleg \ 85159636Soleg if (val == IP_FW_TABLEARG) \ 86159636Soleg errx(EX_DATAERR, "%s: illegal argument value: %s", \ 87159636Soleg match_value(s_x, tok), *av); \ 88159636Soleg arg = val; \ 89159636Soleg } \ 90158879Soleg} while (0) 91158879Soleg 92187762Sluigistatic void 93187762SluigiPRINT_UINT_ARG(const char *str, uint32_t arg) 94187762Sluigi{ 95187762Sluigi if (str != NULL) 96187762Sluigi printf("%s",str); 97187762Sluigi if (arg == IP_FW_TABLEARG) 98187762Sluigi printf("tablearg"); 99187762Sluigi else 100187762Sluigi printf("%u", arg); 101187762Sluigi} 102159636Soleg 10398943Sluigistatic struct _s_x f_tcpflags[] = { 10498943Sluigi { "syn", TH_SYN }, 10598943Sluigi { "fin", TH_FIN }, 10698943Sluigi { "ack", TH_ACK }, 10798943Sluigi { "psh", TH_PUSH }, 10898943Sluigi { "rst", TH_RST }, 10998943Sluigi { "urg", TH_URG }, 11098943Sluigi { "tcp flag", 0 }, 11198943Sluigi { NULL, 0 } 11298943Sluigi}; 11398943Sluigi 11498943Sluigistatic struct _s_x f_tcpopts[] = { 11598943Sluigi { "mss", IP_FW_TCPOPT_MSS }, 11698943Sluigi { "maxseg", IP_FW_TCPOPT_MSS }, 11798943Sluigi { "window", IP_FW_TCPOPT_WINDOW }, 11898943Sluigi { "sack", IP_FW_TCPOPT_SACK }, 11998943Sluigi { "ts", IP_FW_TCPOPT_TS }, 12098943Sluigi { "timestamp", IP_FW_TCPOPT_TS }, 12198943Sluigi { "cc", IP_FW_TCPOPT_CC }, 12298943Sluigi { "tcp option", 0 }, 12398943Sluigi { NULL, 0 } 12498943Sluigi}; 12598943Sluigi 12698943Sluigi/* 12798943Sluigi * IP options span the range 0 to 255 so we need to remap them 12898943Sluigi * (though in fact only the low 5 bits are significant). 12998943Sluigi */ 13098943Sluigistatic struct _s_x f_ipopts[] = { 13198943Sluigi { "ssrr", IP_FW_IPOPT_SSRR}, 13298943Sluigi { "lsrr", IP_FW_IPOPT_LSRR}, 13398943Sluigi { "rr", IP_FW_IPOPT_RR}, 13498943Sluigi { "ts", IP_FW_IPOPT_TS}, 13598943Sluigi { "ip option", 0 }, 13698943Sluigi { NULL, 0 } 13798943Sluigi}; 13898943Sluigi 13998943Sluigistatic struct _s_x f_iptos[] = { 14098943Sluigi { "lowdelay", IPTOS_LOWDELAY}, 14198943Sluigi { "throughput", IPTOS_THROUGHPUT}, 14298943Sluigi { "reliability", IPTOS_RELIABILITY}, 14398943Sluigi { "mincost", IPTOS_MINCOST}, 144172801Srpaulo { "congestion", IPTOS_ECN_CE}, 145172801Srpaulo { "ecntransport", IPTOS_ECN_ECT0}, 14698943Sluigi { "ip tos option", 0}, 14798943Sluigi { NULL, 0 } 14898943Sluigi}; 14998943Sluigi 15098943Sluigistatic struct _s_x limit_masks[] = { 15198943Sluigi {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT}, 15298943Sluigi {"src-addr", DYN_SRC_ADDR}, 15398943Sluigi {"src-port", DYN_SRC_PORT}, 15498943Sluigi {"dst-addr", DYN_DST_ADDR}, 15598943Sluigi {"dst-port", DYN_DST_PORT}, 15698943Sluigi {NULL, 0} 15798943Sluigi}; 15898943Sluigi 15998943Sluigi/* 16098943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines 16198943Sluigi * This is only used in this code. 16298943Sluigi */ 16398943Sluigi#define IPPROTO_ETHERTYPE 0x1000 16498943Sluigistatic struct _s_x ether_types[] = { 16598943Sluigi /* 16698943Sluigi * Note, we cannot use "-:&/" in the names because they are field 16798943Sluigi * separators in the type specifications. Also, we use s = NULL as 16898943Sluigi * end-delimiter, because a type of 0 can be legal. 16998943Sluigi */ 17098943Sluigi { "ip", 0x0800 }, 17198943Sluigi { "ipv4", 0x0800 }, 17298943Sluigi { "ipv6", 0x86dd }, 17398943Sluigi { "arp", 0x0806 }, 17498943Sluigi { "rarp", 0x8035 }, 17598943Sluigi { "vlan", 0x8100 }, 17698943Sluigi { "loop", 0x9000 }, 17798943Sluigi { "trail", 0x1000 }, 17898943Sluigi { "at", 0x809b }, 17998943Sluigi { "atalk", 0x809b }, 18098943Sluigi { "aarp", 0x80f3 }, 18198943Sluigi { "pppoe_disc", 0x8863 }, 18298943Sluigi { "pppoe_sess", 0x8864 }, 18398943Sluigi { "ipx_8022", 0x00E0 }, 18498943Sluigi { "ipx_8023", 0x0000 }, 18598943Sluigi { "ipx_ii", 0x8137 }, 18698943Sluigi { "ipx_snap", 0x8137 }, 18798943Sluigi { "ipx", 0x8137 }, 18898943Sluigi { "ns", 0x0600 }, 18998943Sluigi { NULL, 0 } 19098943Sluigi}; 19198943Sluigi 19298943Sluigi 193187769Sluigistatic struct _s_x rule_actions[] = { 19498943Sluigi { "accept", TOK_ACCEPT }, 19598943Sluigi { "pass", TOK_ACCEPT }, 19698943Sluigi { "allow", TOK_ACCEPT }, 19798943Sluigi { "permit", TOK_ACCEPT }, 19898943Sluigi { "count", TOK_COUNT }, 19998943Sluigi { "pipe", TOK_PIPE }, 20098943Sluigi { "queue", TOK_QUEUE }, 20198943Sluigi { "divert", TOK_DIVERT }, 20298943Sluigi { "tee", TOK_TEE }, 203141351Sglebius { "netgraph", TOK_NETGRAPH }, 204141351Sglebius { "ngtee", TOK_NGTEE }, 20598943Sluigi { "fwd", TOK_FORWARD }, 20698943Sluigi { "forward", TOK_FORWARD }, 20798943Sluigi { "skipto", TOK_SKIPTO }, 20898943Sluigi { "deny", TOK_DENY }, 20998943Sluigi { "drop", TOK_DENY }, 21098943Sluigi { "reject", TOK_REJECT }, 211149020Sbz { "reset6", TOK_RESET6 }, 21298943Sluigi { "reset", TOK_RESET }, 213149020Sbz { "unreach6", TOK_UNREACH6 }, 21499475Sluigi { "unreach", TOK_UNREACH }, 21598943Sluigi { "check-state", TOK_CHECKSTATE }, 216117469Sluigi { "//", TOK_COMMENT }, 217165648Spiso { "nat", TOK_NAT }, 218178888Sjulian { "setfib", TOK_SETFIB }, 219117328Sluigi { NULL, 0 } /* terminator */ 22098943Sluigi}; 22198943Sluigi 222187769Sluigistatic struct _s_x rule_action_params[] = { 223136071Sgreen { "altq", TOK_ALTQ }, 224136071Sgreen { "log", TOK_LOG }, 225158879Soleg { "tag", TOK_TAG }, 226158879Soleg { "untag", TOK_UNTAG }, 227136071Sgreen { NULL, 0 } /* terminator */ 228136071Sgreen}; 229136071Sgreen 230187769Sluigistatic struct _s_x rule_options[] = { 231158879Soleg { "tagged", TOK_TAGGED }, 23298943Sluigi { "uid", TOK_UID }, 23398943Sluigi { "gid", TOK_GID }, 234133600Scsjp { "jail", TOK_JAIL }, 23598943Sluigi { "in", TOK_IN }, 23698943Sluigi { "limit", TOK_LIMIT }, 23798943Sluigi { "keep-state", TOK_KEEPSTATE }, 23898943Sluigi { "bridged", TOK_LAYER2 }, 23998943Sluigi { "layer2", TOK_LAYER2 }, 24098943Sluigi { "out", TOK_OUT }, 241136073Sgreen { "diverted", TOK_DIVERTED }, 242136073Sgreen { "diverted-loopback", TOK_DIVERTEDLOOPBACK }, 243136073Sgreen { "diverted-output", TOK_DIVERTEDOUTPUT }, 24498943Sluigi { "xmit", TOK_XMIT }, 24598943Sluigi { "recv", TOK_RECV }, 24698943Sluigi { "via", TOK_VIA }, 24798943Sluigi { "fragment", TOK_FRAG }, 24898943Sluigi { "frag", TOK_FRAG }, 249178888Sjulian { "fib", TOK_FIB }, 25098943Sluigi { "ipoptions", TOK_IPOPTS }, 25198943Sluigi { "ipopts", TOK_IPOPTS }, 25298943Sluigi { "iplen", TOK_IPLEN }, 25398943Sluigi { "ipid", TOK_IPID }, 25498943Sluigi { "ipprecedence", TOK_IPPRECEDENCE }, 25598943Sluigi { "iptos", TOK_IPTOS }, 25698943Sluigi { "ipttl", TOK_IPTTL }, 25798943Sluigi { "ipversion", TOK_IPVER }, 25898943Sluigi { "ipver", TOK_IPVER }, 25998943Sluigi { "estab", TOK_ESTAB }, 26098943Sluigi { "established", TOK_ESTAB }, 26198943Sluigi { "setup", TOK_SETUP }, 262136075Sgreen { "tcpdatalen", TOK_TCPDATALEN }, 26398943Sluigi { "tcpflags", TOK_TCPFLAGS }, 26498943Sluigi { "tcpflgs", TOK_TCPFLAGS }, 26598943Sluigi { "tcpoptions", TOK_TCPOPTS }, 26698943Sluigi { "tcpopts", TOK_TCPOPTS }, 26798943Sluigi { "tcpseq", TOK_TCPSEQ }, 26898943Sluigi { "tcpack", TOK_TCPACK }, 26998943Sluigi { "tcpwin", TOK_TCPWIN }, 27099909Sluigi { "icmptype", TOK_ICMPTYPES }, 27198943Sluigi { "icmptypes", TOK_ICMPTYPES }, 272102087Sluigi { "dst-ip", TOK_DSTIP }, 273102087Sluigi { "src-ip", TOK_SRCIP }, 274102087Sluigi { "dst-port", TOK_DSTPORT }, 275102087Sluigi { "src-port", TOK_SRCPORT }, 276102087Sluigi { "proto", TOK_PROTO }, 277102087Sluigi { "MAC", TOK_MAC }, 278102087Sluigi { "mac", TOK_MAC }, 279102087Sluigi { "mac-type", TOK_MACTYPE }, 280112250Scjc { "verrevpath", TOK_VERREVPATH }, 281128575Sandre { "versrcreach", TOK_VERSRCREACH }, 282133387Sandre { "antispoof", TOK_ANTISPOOF }, 283117241Sluigi { "ipsec", TOK_IPSEC }, 284145246Sbrooks { "icmp6type", TOK_ICMP6TYPES }, 285145246Sbrooks { "icmp6types", TOK_ICMP6TYPES }, 286145246Sbrooks { "ext6hdr", TOK_EXT6HDR}, 287145246Sbrooks { "flow-id", TOK_FLOWID}, 288145246Sbrooks { "ipv6", TOK_IPV6}, 289145246Sbrooks { "ip6", TOK_IPV6}, 290146894Smlaier { "ipv4", TOK_IPV4}, 291146894Smlaier { "ip4", TOK_IPV4}, 292145246Sbrooks { "dst-ipv6", TOK_DSTIP6}, 293145246Sbrooks { "dst-ip6", TOK_DSTIP6}, 294145246Sbrooks { "src-ipv6", TOK_SRCIP6}, 295145246Sbrooks { "src-ip6", TOK_SRCIP6}, 296117469Sluigi { "//", TOK_COMMENT }, 29798943Sluigi 29898943Sluigi { "not", TOK_NOT }, /* pseudo option */ 29998943Sluigi { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ 30098943Sluigi { "or", TOK_OR }, /* pseudo option */ 30198943Sluigi { "|", /* escape */ TOK_OR }, /* pseudo option */ 302101641Sluigi { "{", TOK_STARTBRACE }, /* pseudo option */ 303101641Sluigi { "(", TOK_STARTBRACE }, /* pseudo option */ 304101641Sluigi { "}", TOK_ENDBRACE }, /* pseudo option */ 305101641Sluigi { ")", TOK_ENDBRACE }, /* pseudo option */ 306117328Sluigi { NULL, 0 } /* terminator */ 30798943Sluigi}; 30898943Sluigi 309117328Sluigistatic __inline uint64_t 310117328Sluigialign_uint64(uint64_t *pll) { 311117328Sluigi uint64_t ret; 312115793Sticso 313115793Sticso bcopy (pll, &ret, sizeof(ret)); 314115793Sticso return ret; 315129389Sstefanf} 316115793Sticso 317187767Sluigivoid * 318187716Sluigisafe_calloc(size_t number, size_t size) 319187716Sluigi{ 320187716Sluigi void *ret = calloc(number, size); 321187716Sluigi 322187716Sluigi if (ret == NULL) 323187716Sluigi err(EX_OSERR, "calloc"); 324187716Sluigi return ret; 325187716Sluigi} 326187716Sluigi 327187767Sluigivoid * 328187716Sluigisafe_realloc(void *ptr, size_t size) 329187716Sluigi{ 330187716Sluigi void *ret = realloc(ptr, size); 331187716Sluigi 332187716Sluigi if (ret == NULL) 333187716Sluigi err(EX_OSERR, "realloc"); 334187716Sluigi return ret; 335187716Sluigi} 336187716Sluigi 337117328Sluigi/* 338117328Sluigi * conditionally runs the command. 339117328Sluigi */ 340187769Sluigiint 341119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen) 342117328Sluigi{ 343117328Sluigi static int s = -1; /* the socket */ 344117328Sluigi int i; 345117577Sluigi 346187764Sluigi if (co.test_only) 347117328Sluigi return 0; 348117328Sluigi 349117328Sluigi if (s == -1) 350117328Sluigi s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 351117328Sluigi if (s < 0) 352117328Sluigi err(EX_UNAVAILABLE, "socket"); 353117328Sluigi 354117328Sluigi if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET || 355130281Sru optname == IP_FW_ADD || optname == IP_FW_TABLE_LIST || 356165648Spiso optname == IP_FW_TABLE_GETSIZE || 357165648Spiso optname == IP_FW_NAT_GET_CONFIG || 358165648Spiso optname == IP_FW_NAT_GET_LOG) 359117328Sluigi i = getsockopt(s, IPPROTO_IP, optname, optval, 360117328Sluigi (socklen_t *)optlen); 361117328Sluigi else 362117328Sluigi i = setsockopt(s, IPPROTO_IP, optname, optval, optlen); 363117328Sluigi return i; 364117328Sluigi} 365117328Sluigi 36698943Sluigi/** 36798943Sluigi * match_token takes a table and a string, returns the value associated 368117328Sluigi * with the string (-1 in case of failure). 36998943Sluigi */ 370187769Sluigiint 37198943Sluigimatch_token(struct _s_x *table, char *string) 37298943Sluigi{ 37398943Sluigi struct _s_x *pt; 374117469Sluigi uint i = strlen(string); 37598943Sluigi 37698943Sluigi for (pt = table ; i && pt->s != NULL ; pt++) 37798943Sluigi if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) 37898943Sluigi return pt->x; 37998943Sluigi return -1; 380129389Sstefanf} 38198943Sluigi 382117328Sluigi/** 383117328Sluigi * match_value takes a table and a value, returns the string associated 384117328Sluigi * with the value (NULL in case of failure). 385117328Sluigi */ 386187770Sluigichar const * 387117469Sluigimatch_value(struct _s_x *p, int value) 38898943Sluigi{ 38998943Sluigi for (; p->s != NULL; p++) 39098943Sluigi if (p->x == value) 39198943Sluigi return p->s; 39298943Sluigi return NULL; 39398943Sluigi} 39498943Sluigi 39598943Sluigi/* 396140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match, 397140271Sbrooks * and 0 if they match exactly or the first string is a sub-string 398140271Sbrooks * of the second. A warning is printed to stderr in the case that the 399140271Sbrooks * first string is a sub-string of the second. 400140271Sbrooks * 401140271Sbrooks * This function will be removed in the future through the usual 402140271Sbrooks * deprecation process. 403140271Sbrooks */ 404187767Sluigiint 405140271Sbrooks_substrcmp(const char *str1, const char* str2) 406140271Sbrooks{ 407140271Sbrooks 408140271Sbrooks if (strncmp(str1, str2, strlen(str1)) != 0) 409140271Sbrooks return 1; 410140271Sbrooks 411140271Sbrooks if (strlen(str1) != strlen(str2)) 412140271Sbrooks warnx("DEPRECATED: '%s' matched '%s' as a sub-string", 413140271Sbrooks str1, str2); 414140271Sbrooks return 0; 415140271Sbrooks} 416140271Sbrooks 417140271Sbrooks/* 418140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match, 419140271Sbrooks * and 0 if they match exactly or the second string is a sub-string 420140271Sbrooks * of the first. A warning is printed to stderr in the case that the 421140271Sbrooks * first string does not match the third. 422140271Sbrooks * 423140271Sbrooks * This function exists to warn about the bizzare construction 424140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut 425140271Sbrooks * for "bytes". The problem is that in addition to accepting "by", 426140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any 427140271Sbrooks * other string beginning with "by". 428140271Sbrooks * 429140271Sbrooks * This function will be removed in the future through the usual 430140271Sbrooks * deprecation process. 431140271Sbrooks */ 432187769Sluigiint 433140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3) 434140271Sbrooks{ 435140271Sbrooks 436140271Sbrooks if (strncmp(str1, str2, strlen(str2)) != 0) 437140271Sbrooks return 1; 438140271Sbrooks 439140271Sbrooks if (strcmp(str1, str3) != 0) 440140271Sbrooks warnx("DEPRECATED: '%s' matched '%s'", 441140271Sbrooks str1, str3); 442140271Sbrooks return 0; 443140271Sbrooks} 444140271Sbrooks 445140271Sbrooks/* 44698943Sluigi * prints one port, symbolic or numeric 44798943Sluigi */ 44898943Sluigistatic void 449117328Sluigiprint_port(int proto, uint16_t port) 45098943Sluigi{ 45198943Sluigi 45298943Sluigi if (proto == IPPROTO_ETHERTYPE) { 453117469Sluigi char const *s; 45498943Sluigi 455187764Sluigi if (co.do_resolv && (s = match_value(ether_types, port)) ) 45698943Sluigi printf("%s", s); 45798943Sluigi else 45898943Sluigi printf("0x%04x", port); 45998943Sluigi } else { 46098943Sluigi struct servent *se = NULL; 461187764Sluigi if (co.do_resolv) { 46298943Sluigi struct protoent *pe = getprotobynumber(proto); 46398943Sluigi 46498943Sluigi se = getservbyport(htons(port), pe ? pe->p_name : NULL); 46598943Sluigi } 46698943Sluigi if (se) 46798943Sluigi printf("%s", se->s_name); 46898943Sluigi else 46998943Sluigi printf("%d", port); 47098943Sluigi } 47198943Sluigi} 47298943Sluigi 473187769Sluigistatic struct _s_x _port_name[] = { 474117328Sluigi {"dst-port", O_IP_DSTPORT}, 475117328Sluigi {"src-port", O_IP_SRCPORT}, 476117328Sluigi {"ipid", O_IPID}, 477117328Sluigi {"iplen", O_IPLEN}, 478117328Sluigi {"ipttl", O_IPTTL}, 479117328Sluigi {"mac-type", O_MAC_TYPE}, 480136075Sgreen {"tcpdatalen", O_TCPDATALEN}, 481158879Soleg {"tagged", O_TAGGED}, 482117328Sluigi {NULL, 0} 483117328Sluigi}; 484117328Sluigi 48598943Sluigi/* 486117328Sluigi * Print the values in a list 16-bit items of the types above. 48798943Sluigi * XXX todo: add support for mask. 48898943Sluigi */ 48998943Sluigistatic void 490102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode) 49198943Sluigi{ 492117328Sluigi uint16_t *p = cmd->ports; 49398943Sluigi int i; 494117469Sluigi char const *sep; 49598943Sluigi 496116690Sluigi if (opcode != 0) { 497117328Sluigi sep = match_value(_port_name, opcode); 498117328Sluigi if (sep == NULL) 499116690Sluigi sep = "???"; 500116690Sluigi printf (" %s", sep); 501116690Sluigi } 502116690Sluigi sep = " "; 50398943Sluigi for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { 50498943Sluigi printf(sep); 50598943Sluigi print_port(proto, p[0]); 50698943Sluigi if (p[0] != p[1]) { 50798943Sluigi printf("-"); 50898943Sluigi print_port(proto, p[1]); 50998943Sluigi } 51098943Sluigi sep = ","; 51198943Sluigi } 51298943Sluigi} 51398943Sluigi 51498943Sluigi/* 51598943Sluigi * Like strtol, but also translates service names into port numbers 51698943Sluigi * for some protocols. 51798943Sluigi * In particular: 51898943Sluigi * proto == -1 disables the protocol check; 51998943Sluigi * proto == IPPROTO_ETHERTYPE looks up an internal table 52098943Sluigi * proto == <some value in /etc/protocols> matches the values there. 521101628Sluigi * Returns *end == s in case the parameter is not found. 52298943Sluigi */ 52398943Sluigistatic int 52498943Sluigistrtoport(char *s, char **end, int base, int proto) 52598943Sluigi{ 526101628Sluigi char *p, *buf; 527101628Sluigi char *s1; 52898943Sluigi int i; 52998943Sluigi 530101628Sluigi *end = s; /* default - not found */ 531117577Sluigi if (*s == '\0') 532101628Sluigi return 0; /* not found */ 533106505Smaxim 53498943Sluigi if (isdigit(*s)) 53598943Sluigi return strtol(s, end, base); 53698943Sluigi 53798943Sluigi /* 538101628Sluigi * find separator. '\\' escapes the next char. 53998943Sluigi */ 540101628Sluigi for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++) 541101628Sluigi if (*s1 == '\\' && s1[1] != '\0') 542101628Sluigi s1++; 54398943Sluigi 544187716Sluigi buf = safe_calloc(s1 - s + 1, 1); 545101628Sluigi 546101628Sluigi /* 547101628Sluigi * copy into a buffer skipping backslashes 548101628Sluigi */ 549101628Sluigi for (p = s, i = 0; p != s1 ; p++) 550117577Sluigi if (*p != '\\') 551101628Sluigi buf[i++] = *p; 552101628Sluigi buf[i++] = '\0'; 553101628Sluigi 55498943Sluigi if (proto == IPPROTO_ETHERTYPE) { 555101628Sluigi i = match_token(ether_types, buf); 556101628Sluigi free(buf); 557101628Sluigi if (i != -1) { /* found */ 55898943Sluigi *end = s1; 55998943Sluigi return i; 56098943Sluigi } 56198943Sluigi } else { 56298943Sluigi struct protoent *pe = NULL; 56398943Sluigi struct servent *se; 56498943Sluigi 56598943Sluigi if (proto != 0) 56698943Sluigi pe = getprotobynumber(proto); 56798943Sluigi setservent(1); 568101628Sluigi se = getservbyname(buf, pe ? pe->p_name : NULL); 569101628Sluigi free(buf); 57098943Sluigi if (se != NULL) { 57198943Sluigi *end = s1; 57298943Sluigi return ntohs(se->s_port); 57398943Sluigi } 57498943Sluigi } 575101628Sluigi return 0; /* not found */ 57698943Sluigi} 57798943Sluigi 57898943Sluigi/* 579136071Sgreen * Map between current altq queue id numbers and names. 580136071Sgreen */ 581136071Sgreenstatic int altq_fetched = 0; 582136071Sgreenstatic TAILQ_HEAD(, pf_altq) altq_entries = 583136071Sgreen TAILQ_HEAD_INITIALIZER(altq_entries); 584136071Sgreen 585136071Sgreenstatic void 586136071Sgreenaltq_set_enabled(int enabled) 587136071Sgreen{ 588136071Sgreen int pffd; 589136071Sgreen 590136071Sgreen pffd = open("/dev/pf", O_RDWR); 591136071Sgreen if (pffd == -1) 592136071Sgreen err(EX_UNAVAILABLE, 593136071Sgreen "altq support opening pf(4) control device"); 594136071Sgreen if (enabled) { 595136071Sgreen if (ioctl(pffd, DIOCSTARTALTQ) != 0 && errno != EEXIST) 596136071Sgreen err(EX_UNAVAILABLE, "enabling altq"); 597136071Sgreen } else { 598136071Sgreen if (ioctl(pffd, DIOCSTOPALTQ) != 0 && errno != ENOENT) 599136071Sgreen err(EX_UNAVAILABLE, "disabling altq"); 600136071Sgreen } 601136071Sgreen close(pffd); 602136071Sgreen} 603136071Sgreen 604136071Sgreenstatic void 605187477Sluigialtq_fetch(void) 606136071Sgreen{ 607136071Sgreen struct pfioc_altq pfioc; 608136071Sgreen struct pf_altq *altq; 609187477Sluigi int pffd; 610187477Sluigi unsigned int mnr; 611136071Sgreen 612136071Sgreen if (altq_fetched) 613136071Sgreen return; 614136071Sgreen altq_fetched = 1; 615136071Sgreen pffd = open("/dev/pf", O_RDONLY); 616136071Sgreen if (pffd == -1) { 617136071Sgreen warn("altq support opening pf(4) control device"); 618136071Sgreen return; 619136071Sgreen } 620136071Sgreen bzero(&pfioc, sizeof(pfioc)); 621136071Sgreen if (ioctl(pffd, DIOCGETALTQS, &pfioc) != 0) { 622136071Sgreen warn("altq support getting queue list"); 623136071Sgreen close(pffd); 624136071Sgreen return; 625136071Sgreen } 626136071Sgreen mnr = pfioc.nr; 627136071Sgreen for (pfioc.nr = 0; pfioc.nr < mnr; pfioc.nr++) { 628136071Sgreen if (ioctl(pffd, DIOCGETALTQ, &pfioc) != 0) { 629136071Sgreen if (errno == EBUSY) 630136071Sgreen break; 631136071Sgreen warn("altq support getting queue list"); 632136071Sgreen close(pffd); 633136071Sgreen return; 634136071Sgreen } 635136071Sgreen if (pfioc.altq.qid == 0) 636136071Sgreen continue; 637187716Sluigi altq = safe_calloc(1, sizeof(*altq)); 638136071Sgreen *altq = pfioc.altq; 639136071Sgreen TAILQ_INSERT_TAIL(&altq_entries, altq, entries); 640136071Sgreen } 641136071Sgreen close(pffd); 642136071Sgreen} 643136071Sgreen 644136071Sgreenstatic u_int32_t 645136071Sgreenaltq_name_to_qid(const char *name) 646136071Sgreen{ 647136071Sgreen struct pf_altq *altq; 648136071Sgreen 649136071Sgreen altq_fetch(); 650136071Sgreen TAILQ_FOREACH(altq, &altq_entries, entries) 651136071Sgreen if (strcmp(name, altq->qname) == 0) 652136071Sgreen break; 653136071Sgreen if (altq == NULL) 654136071Sgreen errx(EX_DATAERR, "altq has no queue named `%s'", name); 655136071Sgreen return altq->qid; 656136071Sgreen} 657136071Sgreen 658136071Sgreenstatic const char * 659136071Sgreenaltq_qid_to_name(u_int32_t qid) 660136071Sgreen{ 661136071Sgreen struct pf_altq *altq; 662136071Sgreen 663136071Sgreen altq_fetch(); 664136071Sgreen TAILQ_FOREACH(altq, &altq_entries, entries) 665136071Sgreen if (qid == altq->qid) 666136071Sgreen break; 667136071Sgreen if (altq == NULL) 668136071Sgreen return NULL; 669136071Sgreen return altq->qname; 670136071Sgreen} 671136071Sgreen 672136071Sgreenstatic void 673136071Sgreenfill_altq_qid(u_int32_t *qid, const char *av) 674136071Sgreen{ 675136071Sgreen *qid = altq_name_to_qid(av); 676136071Sgreen} 677136071Sgreen 678136071Sgreen/* 679117328Sluigi * Fill the body of the command with the list of port ranges. 68098943Sluigi */ 68198943Sluigistatic int 68298943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto) 68398943Sluigi{ 684117328Sluigi uint16_t a, b, *p = cmd->ports; 68598943Sluigi int i = 0; 686102087Sluigi char *s = av; 68798943Sluigi 688102087Sluigi while (*s) { 68998943Sluigi a = strtoport(av, &s, 0, proto); 690159636Soleg if (s == av) /* empty or invalid argument */ 691159636Soleg return (0); 692159636Soleg 693159636Soleg switch (*s) { 694159636Soleg case '-': /* a range */ 695159636Soleg av = s + 1; 69698943Sluigi b = strtoport(av, &s, 0, proto); 697159636Soleg /* Reject expressions like '1-abc' or '1-2-3'. */ 698159636Soleg if (s == av || (*s != ',' && *s != '\0')) 699159636Soleg return (0); 70098943Sluigi p[0] = a; 70198943Sluigi p[1] = b; 702159636Soleg break; 703159636Soleg case ',': /* comma separated list */ 704159636Soleg case '\0': 70598943Sluigi p[0] = p[1] = a; 706159636Soleg break; 707159636Soleg default: 708159636Soleg warnx("port list: invalid separator <%c> in <%s>", 709101978Sluigi *s, av); 710159636Soleg return (0); 711159636Soleg } 712159636Soleg 713102087Sluigi i++; 714102087Sluigi p += 2; 715159636Soleg av = s + 1; 71698943Sluigi } 71798943Sluigi if (i > 0) { 718159636Soleg if (i + 1 > F_LEN_MASK) 719102087Sluigi errx(EX_DATAERR, "too many ports/ranges\n"); 720159636Soleg cmd->o.len |= i + 1; /* leave F_NOT and F_OR untouched */ 72198943Sluigi } 722159636Soleg return (i); 72398943Sluigi} 72498943Sluigi 72598943Sluigistatic struct _s_x icmpcodes[] = { 72698943Sluigi { "net", ICMP_UNREACH_NET }, 72798943Sluigi { "host", ICMP_UNREACH_HOST }, 72898943Sluigi { "protocol", ICMP_UNREACH_PROTOCOL }, 72998943Sluigi { "port", ICMP_UNREACH_PORT }, 73098943Sluigi { "needfrag", ICMP_UNREACH_NEEDFRAG }, 73198943Sluigi { "srcfail", ICMP_UNREACH_SRCFAIL }, 73298943Sluigi { "net-unknown", ICMP_UNREACH_NET_UNKNOWN }, 73398943Sluigi { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN }, 73498943Sluigi { "isolated", ICMP_UNREACH_ISOLATED }, 73598943Sluigi { "net-prohib", ICMP_UNREACH_NET_PROHIB }, 73698943Sluigi { "host-prohib", ICMP_UNREACH_HOST_PROHIB }, 73798943Sluigi { "tosnet", ICMP_UNREACH_TOSNET }, 73898943Sluigi { "toshost", ICMP_UNREACH_TOSHOST }, 73998943Sluigi { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB }, 74098943Sluigi { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE }, 74198943Sluigi { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF }, 74298943Sluigi { NULL, 0 } 74398943Sluigi}; 74498943Sluigi 74598943Sluigistatic void 74698943Sluigifill_reject_code(u_short *codep, char *str) 74798943Sluigi{ 74898943Sluigi int val; 74998943Sluigi char *s; 75098943Sluigi 75198943Sluigi val = strtoul(str, &s, 0); 75298943Sluigi if (s == str || *s != '\0' || val >= 0x100) 75398943Sluigi val = match_token(icmpcodes, str); 754102087Sluigi if (val < 0) 75598943Sluigi errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); 75698943Sluigi *codep = val; 75798943Sluigi return; 75898943Sluigi} 75998943Sluigi 76098943Sluigistatic void 761117328Sluigiprint_reject_code(uint16_t code) 76298943Sluigi{ 763117469Sluigi char const *s = match_value(icmpcodes, code); 76498943Sluigi 76598943Sluigi if (s != NULL) 76699475Sluigi printf("unreach %s", s); 76798943Sluigi else 76899475Sluigi printf("unreach %u", code); 76998943Sluigi} 77098943Sluigi 77198943Sluigi/* 77298943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask, 77398943Sluigi * or -1 if the mask is not contiguous. 77498943Sluigi * XXX this needs a proper fix. 77598943Sluigi * This effectively works on masks in big-endian (network) format. 77698943Sluigi * when compiled on little endian architectures. 77798943Sluigi * 77898943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses, 77998943Sluigi * the first bit on the wire is bit 0 of the first byte. 78098943Sluigi * len is the max length in bits. 78198943Sluigi */ 782187770Sluigiint 783117577Sluigicontigmask(uint8_t *p, int len) 78498943Sluigi{ 78598943Sluigi int i, n; 786117577Sluigi 78798943Sluigi for (i=0; i<len ; i++) 78898943Sluigi if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */ 78998943Sluigi break; 79098943Sluigi for (n=i+1; n < len; n++) 79198943Sluigi if ( (p[n/8] & (1 << (7 - (n%8)))) != 0) 79298943Sluigi return -1; /* mask not contiguous */ 79398943Sluigi return i; 79498943Sluigi} 79598943Sluigi 79698943Sluigi/* 79798943Sluigi * print flags set/clear in the two bitmasks passed as parameters. 79898943Sluigi * There is a specialized check for f_tcpflags. 79998943Sluigi */ 80098943Sluigistatic void 801117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list) 80298943Sluigi{ 803117469Sluigi char const *comma = ""; 80498943Sluigi int i; 805117577Sluigi uint8_t set = cmd->arg1 & 0xff; 806117577Sluigi uint8_t clear = (cmd->arg1 >> 8) & 0xff; 80798943Sluigi 80898943Sluigi if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { 80998943Sluigi printf(" setup"); 81098943Sluigi return; 81198943Sluigi } 81298943Sluigi 81398943Sluigi printf(" %s ", name); 81498943Sluigi for (i=0; list[i].x != 0; i++) { 81598943Sluigi if (set & list[i].x) { 81698943Sluigi set &= ~list[i].x; 81798943Sluigi printf("%s%s", comma, list[i].s); 81898943Sluigi comma = ","; 81998943Sluigi } 82098943Sluigi if (clear & list[i].x) { 82198943Sluigi clear &= ~list[i].x; 82298943Sluigi printf("%s!%s", comma, list[i].s); 82398943Sluigi comma = ","; 82498943Sluigi } 82598943Sluigi } 82698943Sluigi} 82798943Sluigi 82898943Sluigi/* 82998943Sluigi * Print the ip address contained in a command. 83098943Sluigi */ 83198943Sluigistatic void 832117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s) 83398943Sluigi{ 83498943Sluigi struct hostent *he = NULL; 835117328Sluigi int len = F_LEN((ipfw_insn *)cmd); 836117328Sluigi uint32_t *a = ((ipfw_insn_u32 *)cmd)->d; 83798943Sluigi 838102087Sluigi printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); 83998943Sluigi 84098943Sluigi if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { 84198943Sluigi printf("me"); 84298943Sluigi return; 84398943Sluigi } 844130281Sru if (cmd->o.opcode == O_IP_SRC_LOOKUP || 845130281Sru cmd->o.opcode == O_IP_DST_LOOKUP) { 846130281Sru printf("table(%u", ((ipfw_insn *)cmd)->arg1); 847130281Sru if (len == F_INSN_SIZE(ipfw_insn_u32)) 848130281Sru printf(",%u", *a); 849130281Sru printf(")"); 850130281Sru return; 851130281Sru } 85298943Sluigi if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { 853117328Sluigi uint32_t x, *map = (uint32_t *)&(cmd->mask); 854116716Sluigi int i, j; 85598943Sluigi char comma = '{'; 85698943Sluigi 85798943Sluigi x = cmd->o.arg1 - 1; 85898943Sluigi x = htonl( ~x ); 85998943Sluigi cmd->addr.s_addr = htonl(cmd->addr.s_addr); 86098943Sluigi printf("%s/%d", inet_ntoa(cmd->addr), 861117577Sluigi contigmask((uint8_t *)&x, 32)); 86298943Sluigi x = cmd->addr.s_addr = htonl(cmd->addr.s_addr); 86398943Sluigi x &= 0xff; /* base */ 864116716Sluigi /* 865116716Sluigi * Print bits and ranges. 866116716Sluigi * Locate first bit set (i), then locate first bit unset (j). 867116716Sluigi * If we have 3+ consecutive bits set, then print them as a 868116716Sluigi * range, otherwise only print the initial bit and rescan. 869116716Sluigi */ 87098943Sluigi for (i=0; i < cmd->o.arg1; i++) 871117328Sluigi if (map[i/32] & (1<<(i & 31))) { 872116716Sluigi for (j=i+1; j < cmd->o.arg1; j++) 873117328Sluigi if (!(map[ j/32] & (1<<(j & 31)))) 874116716Sluigi break; 87598943Sluigi printf("%c%d", comma, i+x); 876116716Sluigi if (j>i+2) { /* range has at least 3 elements */ 877116716Sluigi printf("-%d", j-1+x); 878116716Sluigi i = j-1; 879116716Sluigi } 88098943Sluigi comma = ','; 88198943Sluigi } 88298943Sluigi printf("}"); 88398943Sluigi return; 88498943Sluigi } 885117328Sluigi /* 886117328Sluigi * len == 2 indicates a single IP, whereas lists of 1 or more 887117328Sluigi * addr/mask pairs have len = (2n+1). We convert len to n so we 888117328Sluigi * use that to count the number of entries. 889117328Sluigi */ 890117328Sluigi for (len = len / 2; len > 0; len--, a += 2) { 891117328Sluigi int mb = /* mask length */ 892117328Sluigi (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ? 893117577Sluigi 32 : contigmask((uint8_t *)&(a[1]), 32); 894187764Sluigi if (mb == 32 && co.do_resolv) 895117328Sluigi he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET); 89698943Sluigi if (he != NULL) /* resolved to name */ 89798943Sluigi printf("%s", he->h_name); 89898943Sluigi else if (mb == 0) /* any */ 89998943Sluigi printf("any"); 90098943Sluigi else { /* numeric IP followed by some kind of mask */ 901117328Sluigi printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) ); 90298943Sluigi if (mb < 0) 903117328Sluigi printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) ); 90498943Sluigi else if (mb < 32) 90598943Sluigi printf("/%d", mb); 90698943Sluigi } 907117328Sluigi if (len > 1) 908117328Sluigi printf(","); 909117328Sluigi } 91098943Sluigi} 91198943Sluigi 91298943Sluigi/* 91398943Sluigi * prints a MAC address/mask pair 91498943Sluigi */ 91598943Sluigistatic void 916117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask) 91798943Sluigi{ 91898943Sluigi int l = contigmask(mask, 48); 91998943Sluigi 92098943Sluigi if (l == 0) 92198943Sluigi printf(" any"); 92298943Sluigi else { 92398943Sluigi printf(" %02x:%02x:%02x:%02x:%02x:%02x", 92498943Sluigi addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 92598943Sluigi if (l == -1) 92698943Sluigi printf("&%02x:%02x:%02x:%02x:%02x:%02x", 92798943Sluigi mask[0], mask[1], mask[2], 92898943Sluigi mask[3], mask[4], mask[5]); 92998943Sluigi else if (l < 48) 93098943Sluigi printf("/%d", l); 93198943Sluigi } 93298943Sluigi} 93398943Sluigi 93499475Sluigistatic void 93599475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av) 93699475Sluigi{ 937117328Sluigi uint8_t type; 93898943Sluigi 93999475Sluigi cmd->d[0] = 0; 94099475Sluigi while (*av) { 94199475Sluigi if (*av == ',') 94299475Sluigi av++; 94399475Sluigi 94499475Sluigi type = strtoul(av, &av, 0); 94599475Sluigi 94699475Sluigi if (*av != ',' && *av != '\0') 94799475Sluigi errx(EX_DATAERR, "invalid ICMP type"); 94899475Sluigi 94999475Sluigi if (type > 31) 95099475Sluigi errx(EX_DATAERR, "ICMP type out of range"); 95199475Sluigi 95299475Sluigi cmd->d[0] |= 1 << type; 95399475Sluigi } 95499475Sluigi cmd->o.opcode = O_ICMPTYPE; 95599475Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 95699475Sluigi} 95799475Sluigi 95899475Sluigistatic void 95999475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd) 96099475Sluigi{ 96199475Sluigi int i; 96299475Sluigi char sep= ' '; 96399475Sluigi 96499475Sluigi printf(" icmptypes"); 96599475Sluigi for (i = 0; i < 32; i++) { 96699475Sluigi if ( (cmd->d[0] & (1 << (i))) == 0) 96799475Sluigi continue; 96899475Sluigi printf("%c%d", sep, i); 96999475Sluigi sep = ','; 97099475Sluigi } 97199475Sluigi} 97299475Sluigi 97398943Sluigi/* 97498943Sluigi * show_ipfw() prints the body of an ipfw rule. 97598943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use 97698943Sluigi * a helper function to produce these entries if not provided explicitly. 977102087Sluigi * The first argument is the list of fields we have, the second is 978102087Sluigi * the list of fields we want to be printed. 979101978Sluigi * 980102087Sluigi * Special cases if we have provided a MAC header: 981102087Sluigi * + if the rule does not contain IP addresses/ports, do not print them; 982102087Sluigi * + if the rule does not contain an IP proto, print "all" instead of "ip"; 983102087Sluigi * 984102087Sluigi * Once we have 'have_options', IP header fields are printed as options. 98598943Sluigi */ 986101978Sluigi#define HAVE_PROTO 0x0001 987101978Sluigi#define HAVE_SRCIP 0x0002 988101978Sluigi#define HAVE_DSTIP 0x0004 989169139Smaxim#define HAVE_PROTO4 0x0008 990169139Smaxim#define HAVE_PROTO6 0x0010 991102087Sluigi#define HAVE_OPTIONS 0x8000 99298943Sluigi 993101978Sluigi#define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP) 99498943Sluigistatic void 995187477Sluigishow_prerequisites(int *flags, int want, int cmd __unused) 99698943Sluigi{ 997187764Sluigi if (co.comment_only) 998123495Sluigi return; 999102087Sluigi if ( (*flags & HAVE_IP) == HAVE_IP) 1000102087Sluigi *flags |= HAVE_OPTIONS; 1001102087Sluigi 1002102087Sluigi if ( !(*flags & HAVE_OPTIONS)) { 1003187477Sluigi if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) { 1004146894Smlaier if ( (*flags & HAVE_PROTO4)) 1005146894Smlaier printf(" ip4"); 1006146894Smlaier else if ( (*flags & HAVE_PROTO6)) 1007146894Smlaier printf(" ip6"); 1008146894Smlaier else 1009146894Smlaier printf(" ip"); 1010187477Sluigi } 1011102087Sluigi if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) 1012102087Sluigi printf(" from any"); 1013102087Sluigi if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP)) 1014102087Sluigi printf(" to any"); 1015102087Sluigi } 101698943Sluigi *flags |= want; 101798943Sluigi} 101898943Sluigi 101998943Sluigistatic void 1020112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) 102198943Sluigi{ 1022107291Skeramida static int twidth = 0; 102398943Sluigi int l; 1024158879Soleg ipfw_insn *cmd, *tagptr = NULL; 1025187477Sluigi const char *comment = NULL; /* ptr to comment if we have one */ 102698943Sluigi int proto = 0; /* default */ 102798943Sluigi int flags = 0; /* prerequisites */ 102898943Sluigi ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */ 1029136071Sgreen ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */ 103098943Sluigi int or_block = 0; /* we are in an or block */ 1031117328Sluigi uint32_t set_disable; 103298943Sluigi 1033115793Sticso bcopy(&rule->next_rule, &set_disable, sizeof(set_disable)); 1034101628Sluigi 1035101628Sluigi if (set_disable & (1 << rule->set)) { /* disabled */ 1036187764Sluigi if (!co.show_sets) 1037101628Sluigi return; 1038101628Sluigi else 1039101628Sluigi printf("# DISABLED "); 1040101628Sluigi } 104198943Sluigi printf("%05u ", rule->rulenum); 104298943Sluigi 1043117469Sluigi if (pcwidth>0 || bcwidth>0) 1044115793Sticso printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt), 1045115793Sticso bcwidth, align_uint64(&rule->bcnt)); 104698943Sluigi 1047187764Sluigi if (co.do_time == 2) 1048117472Sluigi printf("%10u ", rule->timestamp); 1049187764Sluigi else if (co.do_time == 1) { 1050107291Skeramida char timestr[30]; 1051107291Skeramida time_t t = (time_t)0; 1052107291Skeramida 1053107291Skeramida if (twidth == 0) { 1054107291Skeramida strcpy(timestr, ctime(&t)); 1055107291Skeramida *strchr(timestr, '\n') = '\0'; 1056107291Skeramida twidth = strlen(timestr); 1057107291Skeramida } 105898943Sluigi if (rule->timestamp) { 1059107291Skeramida t = _long_to_time(rule->timestamp); 106098943Sluigi 106198943Sluigi strcpy(timestr, ctime(&t)); 106298943Sluigi *strchr(timestr, '\n') = '\0'; 106398943Sluigi printf("%s ", timestr); 106498943Sluigi } else { 1065107291Skeramida printf("%*s", twidth, " "); 106698943Sluigi } 106798943Sluigi } 106898943Sluigi 1069187764Sluigi if (co.show_sets) 1070101628Sluigi printf("set %d ", rule->set); 1071101628Sluigi 107298943Sluigi /* 1073107289Sluigi * print the optional "match probability" 1074107289Sluigi */ 1075107289Sluigi if (rule->cmd_len > 0) { 1076107289Sluigi cmd = rule->cmd ; 1077107289Sluigi if (cmd->opcode == O_PROB) { 1078107289Sluigi ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd; 1079107289Sluigi double d = 1.0 * p->d[0]; 1080107289Sluigi 1081107289Sluigi d = (d / 0x7fffffff); 1082107289Sluigi printf("prob %f ", d); 1083107289Sluigi } 1084107289Sluigi } 1085107289Sluigi 1086107289Sluigi /* 108798943Sluigi * first print actions 108898943Sluigi */ 108998943Sluigi for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule); 109098943Sluigi l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 109198943Sluigi switch(cmd->opcode) { 109298943Sluigi case O_CHECK_STATE: 109398943Sluigi printf("check-state"); 1094102087Sluigi flags = HAVE_IP; /* avoid printing anything else */ 109598943Sluigi break; 109698943Sluigi 109798943Sluigi case O_ACCEPT: 109898943Sluigi printf("allow"); 109998943Sluigi break; 110098943Sluigi 110198943Sluigi case O_COUNT: 110298943Sluigi printf("count"); 110398943Sluigi break; 110498943Sluigi 110598943Sluigi case O_DENY: 110698943Sluigi printf("deny"); 110798943Sluigi break; 110898943Sluigi 110999475Sluigi case O_REJECT: 111099475Sluigi if (cmd->arg1 == ICMP_REJECT_RST) 111199475Sluigi printf("reset"); 111299475Sluigi else if (cmd->arg1 == ICMP_UNREACH_HOST) 111399475Sluigi printf("reject"); 111499475Sluigi else 111599475Sluigi print_reject_code(cmd->arg1); 111699475Sluigi break; 111799475Sluigi 1118149020Sbz case O_UNREACH6: 1119149020Sbz if (cmd->arg1 == ICMP6_UNREACH_RST) 1120149020Sbz printf("reset6"); 1121149020Sbz else 1122149020Sbz print_unreach6_code(cmd->arg1); 1123149020Sbz break; 1124149020Sbz 1125159636Soleg case O_SKIPTO: 1126159636Soleg PRINT_UINT_ARG("skipto ", cmd->arg1); 112798943Sluigi break; 112898943Sluigi 112998943Sluigi case O_PIPE: 1130159636Soleg PRINT_UINT_ARG("pipe ", cmd->arg1); 1131159636Soleg break; 1132159636Soleg 113398943Sluigi case O_QUEUE: 1134159636Soleg PRINT_UINT_ARG("queue ", cmd->arg1); 1135159636Soleg break; 1136159636Soleg 113798943Sluigi case O_DIVERT: 1138159636Soleg PRINT_UINT_ARG("divert ", cmd->arg1); 1139159636Soleg break; 1140159636Soleg 114198943Sluigi case O_TEE: 1142159636Soleg PRINT_UINT_ARG("tee ", cmd->arg1); 1143159636Soleg break; 1144159636Soleg 1145141351Sglebius case O_NETGRAPH: 1146159636Soleg PRINT_UINT_ARG("netgraph ", cmd->arg1); 1147159636Soleg break; 1148159636Soleg 1149141351Sglebius case O_NGTEE: 1150159636Soleg PRINT_UINT_ARG("ngtee ", cmd->arg1); 1151159636Soleg break; 1152141351Sglebius 115398943Sluigi case O_FORWARD_IP: 115498943Sluigi { 115598943Sluigi ipfw_insn_sa *s = (ipfw_insn_sa *)cmd; 115698943Sluigi 1157161424Sjulian if (s->sa.sin_addr.s_addr == INADDR_ANY) { 1158161424Sjulian printf("fwd tablearg"); 1159161424Sjulian } else { 1160161424Sjulian printf("fwd %s", inet_ntoa(s->sa.sin_addr)); 1161161424Sjulian } 116298943Sluigi if (s->sa.sin_port) 1163103241Sluigi printf(",%d", s->sa.sin_port); 116498943Sluigi } 116598943Sluigi break; 116698943Sluigi 116798943Sluigi case O_LOG: /* O_LOG is printed last */ 116898943Sluigi logptr = (ipfw_insn_log *)cmd; 116998943Sluigi break; 117098943Sluigi 1171136071Sgreen case O_ALTQ: /* O_ALTQ is printed after O_LOG */ 1172136071Sgreen altqptr = (ipfw_insn_altq *)cmd; 1173136071Sgreen break; 1174136071Sgreen 1175158879Soleg case O_TAG: 1176158879Soleg tagptr = cmd; 1177158879Soleg break; 1178158879Soleg 1179165648Spiso case O_NAT: 1180176517Spiso PRINT_UINT_ARG("nat ", cmd->arg1); 1181165648Spiso break; 1182165648Spiso 1183178888Sjulian case O_SETFIB: 1184178888Sjulian PRINT_UINT_ARG("setfib ", cmd->arg1); 1185178888Sjulian break; 1186178888Sjulian 118798943Sluigi default: 1188136071Sgreen printf("** unrecognized action %d len %d ", 118998943Sluigi cmd->opcode, cmd->len); 119098943Sluigi } 119198943Sluigi } 119298943Sluigi if (logptr) { 119398943Sluigi if (logptr->max_log > 0) 119499909Sluigi printf(" log logamount %d", logptr->max_log); 119598943Sluigi else 119699909Sluigi printf(" log"); 119798943Sluigi } 1198136071Sgreen if (altqptr) { 1199136071Sgreen const char *qname; 1200102087Sluigi 1201136071Sgreen qname = altq_qid_to_name(altqptr->qid); 1202136071Sgreen if (qname == NULL) 1203136071Sgreen printf(" altq ?<%u>", altqptr->qid); 1204136071Sgreen else 1205136071Sgreen printf(" altq %s", qname); 1206136071Sgreen } 1207158879Soleg if (tagptr) { 1208158879Soleg if (tagptr->len & F_NOT) 1209159636Soleg PRINT_UINT_ARG(" untag ", tagptr->arg1); 1210158879Soleg else 1211159636Soleg PRINT_UINT_ARG(" tag ", tagptr->arg1); 1212158879Soleg } 1213136071Sgreen 121498943Sluigi /* 1215102087Sluigi * then print the body. 121698943Sluigi */ 1217146894Smlaier for (l = rule->act_ofs, cmd = rule->cmd ; 1218146894Smlaier l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { 1219146894Smlaier if ((cmd->len & F_OR) || (cmd->len & F_NOT)) 1220146894Smlaier continue; 1221146894Smlaier if (cmd->opcode == O_IP4) { 1222146894Smlaier flags |= HAVE_PROTO4; 1223146894Smlaier break; 1224146894Smlaier } else if (cmd->opcode == O_IP6) { 1225146894Smlaier flags |= HAVE_PROTO6; 1226146894Smlaier break; 1227146894Smlaier } 1228146894Smlaier } 1229102087Sluigi if (rule->_pad & 1) { /* empty rules before options */ 1230187764Sluigi if (!co.do_compact) { 1231146894Smlaier show_prerequisites(&flags, HAVE_PROTO, 0); 1232146894Smlaier printf(" from any to any"); 1233146894Smlaier } 1234102087Sluigi flags |= HAVE_IP | HAVE_OPTIONS; 1235102087Sluigi } 1236102087Sluigi 1237187764Sluigi if (co.comment_only) 1238123495Sluigi comment = "..."; 1239123495Sluigi 124098943Sluigi for (l = rule->act_ofs, cmd = rule->cmd ; 124198943Sluigi l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { 124299475Sluigi /* useful alias */ 124399475Sluigi ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; 124498943Sluigi 1245187764Sluigi if (co.comment_only) { 1246123495Sluigi if (cmd->opcode != O_NOP) 1247123495Sluigi continue; 1248123495Sluigi printf(" // %s\n", (char *)(cmd + 1)); 1249123495Sluigi return; 1250123495Sluigi } 1251123495Sluigi 1252102087Sluigi show_prerequisites(&flags, 0, cmd->opcode); 1253102087Sluigi 125498943Sluigi switch(cmd->opcode) { 1255117577Sluigi case O_PROB: 1256107289Sluigi break; /* done already */ 1257107289Sluigi 125898943Sluigi case O_PROBE_STATE: 125998943Sluigi break; /* no need to print anything here */ 126098943Sluigi 126198943Sluigi case O_IP_SRC: 1262130281Sru case O_IP_SRC_LOOKUP: 126398943Sluigi case O_IP_SRC_MASK: 126498943Sluigi case O_IP_SRC_ME: 126598943Sluigi case O_IP_SRC_SET: 1266102087Sluigi show_prerequisites(&flags, HAVE_PROTO, 0); 126798943Sluigi if (!(flags & HAVE_SRCIP)) 126898943Sluigi printf(" from"); 126998943Sluigi if ((cmd->len & F_OR) && !or_block) 127098943Sluigi printf(" {"); 1271102087Sluigi print_ip((ipfw_insn_ip *)cmd, 1272102087Sluigi (flags & HAVE_OPTIONS) ? " src-ip" : ""); 127398943Sluigi flags |= HAVE_SRCIP; 127498943Sluigi break; 127598943Sluigi 127698943Sluigi case O_IP_DST: 1277130281Sru case O_IP_DST_LOOKUP: 127898943Sluigi case O_IP_DST_MASK: 127998943Sluigi case O_IP_DST_ME: 128098943Sluigi case O_IP_DST_SET: 1281102087Sluigi show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); 128298943Sluigi if (!(flags & HAVE_DSTIP)) 128398943Sluigi printf(" to"); 128498943Sluigi if ((cmd->len & F_OR) && !or_block) 128598943Sluigi printf(" {"); 1286102087Sluigi print_ip((ipfw_insn_ip *)cmd, 1287102087Sluigi (flags & HAVE_OPTIONS) ? " dst-ip" : ""); 128898943Sluigi flags |= HAVE_DSTIP; 128998943Sluigi break; 129098943Sluigi 1291145246Sbrooks case O_IP6_SRC: 1292145246Sbrooks case O_IP6_SRC_MASK: 1293145246Sbrooks case O_IP6_SRC_ME: 1294147105Smlaier show_prerequisites(&flags, HAVE_PROTO, 0); 1295145246Sbrooks if (!(flags & HAVE_SRCIP)) 1296145246Sbrooks printf(" from"); 1297145246Sbrooks if ((cmd->len & F_OR) && !or_block) 1298145246Sbrooks printf(" {"); 1299145246Sbrooks print_ip6((ipfw_insn_ip6 *)cmd, 1300145246Sbrooks (flags & HAVE_OPTIONS) ? " src-ip6" : ""); 1301145246Sbrooks flags |= HAVE_SRCIP | HAVE_PROTO; 1302145246Sbrooks break; 1303145246Sbrooks 1304145246Sbrooks case O_IP6_DST: 1305145246Sbrooks case O_IP6_DST_MASK: 1306145246Sbrooks case O_IP6_DST_ME: 1307145246Sbrooks show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); 1308145246Sbrooks if (!(flags & HAVE_DSTIP)) 1309145246Sbrooks printf(" to"); 1310145246Sbrooks if ((cmd->len & F_OR) && !or_block) 1311145246Sbrooks printf(" {"); 1312145246Sbrooks print_ip6((ipfw_insn_ip6 *)cmd, 1313145246Sbrooks (flags & HAVE_OPTIONS) ? " dst-ip6" : ""); 1314145246Sbrooks flags |= HAVE_DSTIP; 1315145246Sbrooks break; 1316145246Sbrooks 1317145246Sbrooks case O_FLOW6ID: 1318145246Sbrooks print_flow6id( (ipfw_insn_u32 *) cmd ); 1319145246Sbrooks flags |= HAVE_OPTIONS; 1320145246Sbrooks break; 1321145246Sbrooks 132298943Sluigi case O_IP_DSTPORT: 1323102087Sluigi show_prerequisites(&flags, HAVE_IP, 0); 132498943Sluigi case O_IP_SRCPORT: 1325102087Sluigi show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); 1326101641Sluigi if ((cmd->len & F_OR) && !or_block) 1327101641Sluigi printf(" {"); 1328172306Smaxim if (cmd->len & F_NOT) 1329172306Smaxim printf(" not"); 1330102087Sluigi print_newports((ipfw_insn_u16 *)cmd, proto, 1331102087Sluigi (flags & HAVE_OPTIONS) ? cmd->opcode : 0); 133298943Sluigi break; 133398943Sluigi 133498943Sluigi case O_PROTO: { 1335145246Sbrooks struct protoent *pe = NULL; 133698943Sluigi 133798943Sluigi if ((cmd->len & F_OR) && !or_block) 133898943Sluigi printf(" {"); 133998943Sluigi if (cmd->len & F_NOT) 134098943Sluigi printf(" not"); 134198943Sluigi proto = cmd->arg1; 1342145567Sbrooks pe = getprotobynumber(cmd->arg1); 1343146894Smlaier if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) && 1344146894Smlaier !(flags & HAVE_PROTO)) 1345146894Smlaier show_prerequisites(&flags, 1346146894Smlaier HAVE_IP | HAVE_OPTIONS, 0); 1347102087Sluigi if (flags & HAVE_OPTIONS) 1348102087Sluigi printf(" proto"); 134998943Sluigi if (pe) 135098943Sluigi printf(" %s", pe->p_name); 135198943Sluigi else 135298943Sluigi printf(" %u", cmd->arg1); 135398943Sluigi } 135498943Sluigi flags |= HAVE_PROTO; 135598943Sluigi break; 1356106505Smaxim 135798943Sluigi default: /*options ... */ 1358146894Smlaier if (!(cmd->len & (F_OR|F_NOT))) 1359146894Smlaier if (((cmd->opcode == O_IP6) && 1360146894Smlaier (flags & HAVE_PROTO6)) || 1361146894Smlaier ((cmd->opcode == O_IP4) && 1362146894Smlaier (flags & HAVE_PROTO4))) 1363146894Smlaier break; 1364102087Sluigi show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0); 136598943Sluigi if ((cmd->len & F_OR) && !or_block) 136698943Sluigi printf(" {"); 136798943Sluigi if (cmd->len & F_NOT && cmd->opcode != O_IN) 136898943Sluigi printf(" not"); 136998943Sluigi switch(cmd->opcode) { 1370169139Smaxim case O_MACADDR2: { 1371169139Smaxim ipfw_insn_mac *m = (ipfw_insn_mac *)cmd; 1372169139Smaxim 1373169139Smaxim printf(" MAC"); 1374169139Smaxim print_mac(m->addr, m->mask); 1375169139Smaxim print_mac(m->addr + 6, m->mask + 6); 1376169139Smaxim } 1377169139Smaxim break; 1378169139Smaxim 1379169139Smaxim case O_MAC_TYPE: 1380169139Smaxim print_newports((ipfw_insn_u16 *)cmd, 1381169139Smaxim IPPROTO_ETHERTYPE, cmd->opcode); 1382169139Smaxim break; 1383169139Smaxim 1384169139Smaxim 138598943Sluigi case O_FRAG: 138698943Sluigi printf(" frag"); 138798943Sluigi break; 138898943Sluigi 1389178888Sjulian case O_FIB: 1390178888Sjulian printf(" fib %u", cmd->arg1 ); 1391178888Sjulian break; 1392178888Sjulian 139398943Sluigi case O_IN: 139498943Sluigi printf(cmd->len & F_NOT ? " out" : " in"); 139598943Sluigi break; 139698943Sluigi 1397136073Sgreen case O_DIVERTED: 1398136073Sgreen switch (cmd->arg1) { 1399136073Sgreen case 3: 1400136073Sgreen printf(" diverted"); 1401136073Sgreen break; 1402136073Sgreen case 1: 1403136073Sgreen printf(" diverted-loopback"); 1404136073Sgreen break; 1405136073Sgreen case 2: 1406136073Sgreen printf(" diverted-output"); 1407136073Sgreen break; 1408136073Sgreen default: 1409136073Sgreen printf(" diverted-?<%u>", cmd->arg1); 1410136073Sgreen break; 1411136073Sgreen } 1412136073Sgreen break; 1413136073Sgreen 141498943Sluigi case O_LAYER2: 141598943Sluigi printf(" layer2"); 141698943Sluigi break; 141798943Sluigi case O_XMIT: 141898943Sluigi case O_RECV: 1419140423Sglebius case O_VIA: 1420140423Sglebius { 1421117469Sluigi char const *s; 142298943Sluigi ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd; 142398943Sluigi 142498943Sluigi if (cmd->opcode == O_XMIT) 142598943Sluigi s = "xmit"; 142698943Sluigi else if (cmd->opcode == O_RECV) 142798943Sluigi s = "recv"; 1428117469Sluigi else /* if (cmd->opcode == O_VIA) */ 142998943Sluigi s = "via"; 143098943Sluigi if (cmdif->name[0] == '\0') 143199475Sluigi printf(" %s %s", s, 143299475Sluigi inet_ntoa(cmdif->p.ip)); 1433140423Sglebius else 1434140423Sglebius printf(" %s %s", s, cmdif->name); 1435140423Sglebius 143698943Sluigi break; 1437140423Sglebius } 143898943Sluigi case O_IPID: 1439116690Sluigi if (F_LEN(cmd) == 1) 1440116690Sluigi printf(" ipid %u", cmd->arg1 ); 1441116690Sluigi else 1442116690Sluigi print_newports((ipfw_insn_u16 *)cmd, 0, 1443116690Sluigi O_IPID); 144498943Sluigi break; 144598943Sluigi 144698943Sluigi case O_IPTTL: 1447116690Sluigi if (F_LEN(cmd) == 1) 1448116690Sluigi printf(" ipttl %u", cmd->arg1 ); 1449116690Sluigi else 1450116690Sluigi print_newports((ipfw_insn_u16 *)cmd, 0, 1451116690Sluigi O_IPTTL); 145298943Sluigi break; 145398943Sluigi 145498943Sluigi case O_IPVER: 145598943Sluigi printf(" ipver %u", cmd->arg1 ); 145698943Sluigi break; 145798943Sluigi 145899475Sluigi case O_IPPRECEDENCE: 145999475Sluigi printf(" ipprecedence %u", (cmd->arg1) >> 5 ); 146099475Sluigi break; 146199475Sluigi 146298943Sluigi case O_IPLEN: 1463116690Sluigi if (F_LEN(cmd) == 1) 1464116690Sluigi printf(" iplen %u", cmd->arg1 ); 1465116690Sluigi else 1466116690Sluigi print_newports((ipfw_insn_u16 *)cmd, 0, 1467116690Sluigi O_IPLEN); 146898943Sluigi break; 146998943Sluigi 1470101116Sluigi case O_IPOPT: 147198943Sluigi print_flags("ipoptions", cmd, f_ipopts); 147298943Sluigi break; 147398943Sluigi 147499475Sluigi case O_IPTOS: 147599475Sluigi print_flags("iptos", cmd, f_iptos); 147699475Sluigi break; 147799475Sluigi 147899475Sluigi case O_ICMPTYPE: 147999475Sluigi print_icmptypes((ipfw_insn_u32 *)cmd); 148099475Sluigi break; 148199475Sluigi 148298943Sluigi case O_ESTAB: 148398943Sluigi printf(" established"); 148498943Sluigi break; 148598943Sluigi 1486136075Sgreen case O_TCPDATALEN: 1487136075Sgreen if (F_LEN(cmd) == 1) 1488136075Sgreen printf(" tcpdatalen %u", cmd->arg1 ); 1489136075Sgreen else 1490136075Sgreen print_newports((ipfw_insn_u16 *)cmd, 0, 1491136075Sgreen O_TCPDATALEN); 1492136075Sgreen break; 1493136075Sgreen 149498943Sluigi case O_TCPFLAGS: 149598943Sluigi print_flags("tcpflags", cmd, f_tcpflags); 149698943Sluigi break; 149798943Sluigi 149898943Sluigi case O_TCPOPTS: 149998943Sluigi print_flags("tcpoptions", cmd, f_tcpopts); 150098943Sluigi break; 150198943Sluigi 150298943Sluigi case O_TCPWIN: 150398943Sluigi printf(" tcpwin %d", ntohs(cmd->arg1)); 150498943Sluigi break; 150598943Sluigi 150698943Sluigi case O_TCPACK: 150798943Sluigi printf(" tcpack %d", ntohl(cmd32->d[0])); 150898943Sluigi break; 150998943Sluigi 151098943Sluigi case O_TCPSEQ: 151198943Sluigi printf(" tcpseq %d", ntohl(cmd32->d[0])); 151298943Sluigi break; 151398943Sluigi 151498943Sluigi case O_UID: 151598943Sluigi { 151698943Sluigi struct passwd *pwd = getpwuid(cmd32->d[0]); 151798943Sluigi 151898943Sluigi if (pwd) 151998943Sluigi printf(" uid %s", pwd->pw_name); 152098943Sluigi else 152198943Sluigi printf(" uid %u", cmd32->d[0]); 152298943Sluigi } 152398943Sluigi break; 152498943Sluigi 152598943Sluigi case O_GID: 152698943Sluigi { 152798943Sluigi struct group *grp = getgrgid(cmd32->d[0]); 152898943Sluigi 152998943Sluigi if (grp) 153098943Sluigi printf(" gid %s", grp->gr_name); 153198943Sluigi else 153298943Sluigi printf(" gid %u", cmd32->d[0]); 153398943Sluigi } 153498943Sluigi break; 153598943Sluigi 1536133600Scsjp case O_JAIL: 1537133600Scsjp printf(" jail %d", cmd32->d[0]); 1538133600Scsjp break; 1539133600Scsjp 1540112250Scjc case O_VERREVPATH: 1541112250Scjc printf(" verrevpath"); 1542112250Scjc break; 1543116919Sluigi 1544128575Sandre case O_VERSRCREACH: 1545128575Sandre printf(" versrcreach"); 1546128575Sandre break; 1547128575Sandre 1548133387Sandre case O_ANTISPOOF: 1549133387Sandre printf(" antispoof"); 1550133387Sandre break; 1551133387Sandre 1552117241Sluigi case O_IPSEC: 1553117241Sluigi printf(" ipsec"); 1554117241Sluigi break; 1555117241Sluigi 1556117469Sluigi case O_NOP: 1557117626Sluigi comment = (char *)(cmd + 1); 1558117469Sluigi break; 1559117469Sluigi 156098943Sluigi case O_KEEP_STATE: 156198943Sluigi printf(" keep-state"); 156298943Sluigi break; 156398943Sluigi 1564159636Soleg case O_LIMIT: { 156598943Sluigi struct _s_x *p = limit_masks; 156698943Sluigi ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; 1567117328Sluigi uint8_t x = c->limit_mask; 1568117469Sluigi char const *comma = " "; 156998943Sluigi 157098943Sluigi printf(" limit"); 1571117577Sluigi for (; p->x != 0 ; p++) 157299909Sluigi if ((x & p->x) == p->x) { 157398943Sluigi x &= ~p->x; 157498943Sluigi printf("%s%s", comma, p->s); 157598943Sluigi comma = ","; 157698943Sluigi } 1577159636Soleg PRINT_UINT_ARG(" ", c->conn_limit); 157898943Sluigi break; 1579159636Soleg } 158098943Sluigi 1581146894Smlaier case O_IP6: 1582152923Sume printf(" ip6"); 1583145246Sbrooks break; 1584145246Sbrooks 1585146894Smlaier case O_IP4: 1586152923Sume printf(" ip4"); 1587146894Smlaier break; 1588146894Smlaier 1589145246Sbrooks case O_ICMP6TYPE: 1590145246Sbrooks print_icmp6types((ipfw_insn_u32 *)cmd); 1591145246Sbrooks break; 1592145246Sbrooks 1593145246Sbrooks case O_EXT_HDR: 1594145246Sbrooks print_ext6hdr( (ipfw_insn *) cmd ); 1595145246Sbrooks break; 1596145246Sbrooks 1597158879Soleg case O_TAGGED: 1598158879Soleg if (F_LEN(cmd) == 1) 1599159636Soleg PRINT_UINT_ARG(" tagged ", cmd->arg1); 1600158879Soleg else 1601159636Soleg print_newports((ipfw_insn_u16 *)cmd, 0, 1602159636Soleg O_TAGGED); 1603158879Soleg break; 1604158879Soleg 160598943Sluigi default: 160698943Sluigi printf(" [opcode %d len %d]", 160798943Sluigi cmd->opcode, cmd->len); 160898943Sluigi } 160998943Sluigi } 161098943Sluigi if (cmd->len & F_OR) { 161198943Sluigi printf(" or"); 161298943Sluigi or_block = 1; 161398943Sluigi } else if (or_block) { 161498943Sluigi printf(" }"); 161598943Sluigi or_block = 0; 161698943Sluigi } 161798943Sluigi } 1618102087Sluigi show_prerequisites(&flags, HAVE_IP, 0); 1619117626Sluigi if (comment) 1620117626Sluigi printf(" // %s", comment); 162198943Sluigi printf("\n"); 162298943Sluigi} 162398943Sluigi 162498943Sluigistatic void 1625112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth) 162698943Sluigi{ 162798943Sluigi struct protoent *pe; 162898943Sluigi struct in_addr a; 1629115793Sticso uint16_t rulenum; 1630159160Smlaier char buf[INET6_ADDRSTRLEN]; 163198943Sluigi 1632187764Sluigi if (!co.do_expired) { 163398943Sluigi if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT)) 163498943Sluigi return; 163598943Sluigi } 1636115793Sticso bcopy(&d->rule, &rulenum, sizeof(rulenum)); 1637117328Sluigi printf("%05d", rulenum); 1638117469Sluigi if (pcwidth>0 || bcwidth>0) 1639117328Sluigi printf(" %*llu %*llu (%ds)", pcwidth, 1640117328Sluigi align_uint64(&d->pcnt), bcwidth, 1641117328Sluigi align_uint64(&d->bcnt), d->expire); 164298943Sluigi switch (d->dyn_type) { 164398943Sluigi case O_LIMIT_PARENT: 164498943Sluigi printf(" PARENT %d", d->count); 164598943Sluigi break; 164698943Sluigi case O_LIMIT: 164798943Sluigi printf(" LIMIT"); 164898943Sluigi break; 164998943Sluigi case O_KEEP_STATE: /* bidir, no mask */ 1650106505Smaxim printf(" STATE"); 165198943Sluigi break; 165298943Sluigi } 165398943Sluigi 165498943Sluigi if ((pe = getprotobynumber(d->id.proto)) != NULL) 165598943Sluigi printf(" %s", pe->p_name); 165698943Sluigi else 165798943Sluigi printf(" proto %u", d->id.proto); 165898943Sluigi 1659159160Smlaier if (d->id.addr_type == 4) { 1660159160Smlaier a.s_addr = htonl(d->id.src_ip); 1661159160Smlaier printf(" %s %d", inet_ntoa(a), d->id.src_port); 166298943Sluigi 1663159160Smlaier a.s_addr = htonl(d->id.dst_ip); 1664159160Smlaier printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port); 1665159160Smlaier } else if (d->id.addr_type == 6) { 1666159160Smlaier printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf, 1667159160Smlaier sizeof(buf)), d->id.src_port); 1668159160Smlaier printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf, 1669159160Smlaier sizeof(buf)), d->id.dst_port); 1670159160Smlaier } else 1671159160Smlaier printf(" UNKNOWN <-> UNKNOWN\n"); 1672159160Smlaier 167398943Sluigi printf("\n"); 167498943Sluigi} 167598943Sluigi 1676101978Sluigi/* 1677101978Sluigi * This one handles all set-related commands 1678101978Sluigi * ipfw set { show | enable | disable } 1679101978Sluigi * ipfw set swap X Y 1680101978Sluigi * ipfw set move X to Y 1681101978Sluigi * ipfw set move rule X to Y 1682101978Sluigi */ 1683187767Sluigivoid 1684187767Sluigiipfw_sets_handler(int ac, char *av[]) 1685101978Sluigi{ 1686117328Sluigi uint32_t set_disable, masks[2]; 1687101978Sluigi int i, nbytes; 1688117328Sluigi uint16_t rulenum; 1689117328Sluigi uint8_t cmd, new_set; 1690101978Sluigi 1691101978Sluigi ac--; 1692101978Sluigi av++; 1693101978Sluigi 1694101978Sluigi if (!ac) 1695101978Sluigi errx(EX_USAGE, "set needs command"); 1696140271Sbrooks if (_substrcmp(*av, "show") == 0) { 1697101978Sluigi void *data; 1698117469Sluigi char const *msg; 1699101978Sluigi 1700101978Sluigi nbytes = sizeof(struct ip_fw); 1701187716Sluigi data = safe_calloc(1, nbytes); 1702119740Stmm if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0) 1703101978Sluigi err(EX_OSERR, "getsockopt(IP_FW_GET)"); 1704115793Sticso bcopy(&((struct ip_fw *)data)->next_rule, 1705115793Sticso &set_disable, sizeof(set_disable)); 1706101978Sluigi 1707117655Sluigi for (i = 0, msg = "disable" ; i < RESVD_SET; i++) 1708117577Sluigi if ((set_disable & (1<<i))) { 1709101978Sluigi printf("%s %d", msg, i); 1710101978Sluigi msg = ""; 1711101978Sluigi } 1712101978Sluigi msg = (set_disable) ? " enable" : "enable"; 1713117655Sluigi for (i = 0; i < RESVD_SET; i++) 1714117577Sluigi if (!(set_disable & (1<<i))) { 1715101978Sluigi printf("%s %d", msg, i); 1716101978Sluigi msg = ""; 1717101978Sluigi } 1718101978Sluigi printf("\n"); 1719140271Sbrooks } else if (_substrcmp(*av, "swap") == 0) { 1720101978Sluigi ac--; av++; 1721101978Sluigi if (ac != 2) 1722101978Sluigi errx(EX_USAGE, "set swap needs 2 set numbers\n"); 1723101978Sluigi rulenum = atoi(av[0]); 1724101978Sluigi new_set = atoi(av[1]); 1725117655Sluigi if (!isdigit(*(av[0])) || rulenum > RESVD_SET) 1726101978Sluigi errx(EX_DATAERR, "invalid set number %s\n", av[0]); 1727117655Sluigi if (!isdigit(*(av[1])) || new_set > RESVD_SET) 1728101978Sluigi errx(EX_DATAERR, "invalid set number %s\n", av[1]); 1729101978Sluigi masks[0] = (4 << 24) | (new_set << 16) | (rulenum); 1730117328Sluigi i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); 1731140271Sbrooks } else if (_substrcmp(*av, "move") == 0) { 1732101978Sluigi ac--; av++; 1733140271Sbrooks if (ac && _substrcmp(*av, "rule") == 0) { 1734101978Sluigi cmd = 2; 1735101978Sluigi ac--; av++; 1736101978Sluigi } else 1737101978Sluigi cmd = 3; 1738140271Sbrooks if (ac != 3 || _substrcmp(av[1], "to") != 0) 1739101978Sluigi errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); 1740101978Sluigi rulenum = atoi(av[0]); 1741101978Sluigi new_set = atoi(av[2]); 1742117655Sluigi if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) || 1743182823Srik (cmd == 2 && rulenum == IPFW_DEFAULT_RULE) ) 1744101978Sluigi errx(EX_DATAERR, "invalid source number %s\n", av[0]); 1745117655Sluigi if (!isdigit(*(av[2])) || new_set > RESVD_SET) 1746101978Sluigi errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); 1747101978Sluigi masks[0] = (cmd << 24) | (new_set << 16) | (rulenum); 1748117328Sluigi i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); 1749140271Sbrooks } else if (_substrcmp(*av, "disable") == 0 || 1750140271Sbrooks _substrcmp(*av, "enable") == 0 ) { 1751140271Sbrooks int which = _substrcmp(*av, "enable") == 0 ? 1 : 0; 1752101978Sluigi 1753101978Sluigi ac--; av++; 1754101978Sluigi masks[0] = masks[1] = 0; 1755101978Sluigi 1756101978Sluigi while (ac) { 1757101978Sluigi if (isdigit(**av)) { 1758101978Sluigi i = atoi(*av); 1759117655Sluigi if (i < 0 || i > RESVD_SET) 1760101978Sluigi errx(EX_DATAERR, 1761101978Sluigi "invalid set number %d\n", i); 1762101978Sluigi masks[which] |= (1<<i); 1763140271Sbrooks } else if (_substrcmp(*av, "disable") == 0) 1764101978Sluigi which = 0; 1765140271Sbrooks else if (_substrcmp(*av, "enable") == 0) 1766101978Sluigi which = 1; 1767101978Sluigi else 1768101978Sluigi errx(EX_DATAERR, 1769101978Sluigi "invalid set command %s\n", *av); 1770101978Sluigi av++; ac--; 1771101978Sluigi } 1772101978Sluigi if ( (masks[0] & masks[1]) != 0 ) 1773101978Sluigi errx(EX_DATAERR, 1774101978Sluigi "cannot enable and disable the same set\n"); 1775101978Sluigi 1776117328Sluigi i = do_cmd(IP_FW_DEL, masks, sizeof(masks)); 1777101978Sluigi if (i) 1778101978Sluigi warn("set enable/disable: setsockopt(IP_FW_DEL)"); 1779101978Sluigi } else 1780101978Sluigi errx(EX_USAGE, "invalid set command %s\n", *av); 1781101978Sluigi} 1782101978Sluigi 1783187767Sluigivoid 1784187767Sluigiipfw_sysctl_handler(int ac, char *av[], int which) 1785109126Sdillon{ 1786109126Sdillon ac--; 1787109126Sdillon av++; 1788109126Sdillon 1789119668Smaxim if (ac == 0) { 1790109126Sdillon warnx("missing keyword to enable/disable\n"); 1791140271Sbrooks } else if (_substrcmp(*av, "firewall") == 0) { 1792116770Sluigi sysctlbyname("net.inet.ip.fw.enable", NULL, 0, 1793116770Sluigi &which, sizeof(which)); 1794140271Sbrooks } else if (_substrcmp(*av, "one_pass") == 0) { 1795116770Sluigi sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0, 1796116770Sluigi &which, sizeof(which)); 1797140271Sbrooks } else if (_substrcmp(*av, "debug") == 0) { 1798116770Sluigi sysctlbyname("net.inet.ip.fw.debug", NULL, 0, 1799116770Sluigi &which, sizeof(which)); 1800140271Sbrooks } else if (_substrcmp(*av, "verbose") == 0) { 1801116770Sluigi sysctlbyname("net.inet.ip.fw.verbose", NULL, 0, 1802116770Sluigi &which, sizeof(which)); 1803140271Sbrooks } else if (_substrcmp(*av, "dyn_keepalive") == 0) { 1804116770Sluigi sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0, 1805116770Sluigi &which, sizeof(which)); 1806140271Sbrooks } else if (_substrcmp(*av, "altq") == 0) { 1807136071Sgreen altq_set_enabled(which); 1808109126Sdillon } else { 1809109126Sdillon warnx("unrecognize enable/disable keyword: %s\n", *av); 1810109126Sdillon } 1811109126Sdillon} 1812109126Sdillon 1813187767Sluigivoid 1814187767Sluigiipfw_list(int ac, char *av[], int show_counters) 181598943Sluigi{ 181698943Sluigi struct ip_fw *r; 181798943Sluigi ipfw_dyn_rule *dynrules, *d; 181898943Sluigi 1819117469Sluigi#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r))) 1820117469Sluigi char *lim; 1821117469Sluigi void *data = NULL; 1822112189Smaxim int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width; 182398943Sluigi int exitval = EX_OK; 182498943Sluigi int lac; 182598943Sluigi char **lav; 1826117469Sluigi u_long rnum, last; 182798943Sluigi char *endptr; 182898943Sluigi int seen = 0; 1829170923Smaxim uint8_t set; 183098943Sluigi 1831187764Sluigi const int ocmd = co.do_pipe ? IP_DUMMYNET_GET : IP_FW_GET; 183298943Sluigi int nalloc = 1024; /* start somewhere... */ 183398943Sluigi 1834135036Smaxim last = 0; 1835135036Smaxim 1836187764Sluigi if (co.test_only) { 1837117328Sluigi fprintf(stderr, "Testing only, list disabled\n"); 1838117328Sluigi return; 1839117328Sluigi } 1840117328Sluigi 184198943Sluigi ac--; 184298943Sluigi av++; 184398943Sluigi 184498943Sluigi /* get rules or pipes from kernel, resizing array as necessary */ 184598943Sluigi nbytes = nalloc; 184698943Sluigi 184798943Sluigi while (nbytes >= nalloc) { 184898943Sluigi nalloc = nalloc * 2 + 200; 184998943Sluigi nbytes = nalloc; 1850187716Sluigi data = safe_realloc(data, nbytes); 1851119740Stmm if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0) 185298943Sluigi err(EX_OSERR, "getsockopt(IP_%s_GET)", 1853187764Sluigi co.do_pipe ? "DUMMYNET" : "FW"); 185498943Sluigi } 185598943Sluigi 1856187764Sluigi if (co.do_pipe) { 1857187769Sluigi ipfw_list_pipes(data, nbytes, ac, av); 185898943Sluigi goto done; 185998943Sluigi } 186098943Sluigi 186198943Sluigi /* 186298943Sluigi * Count static rules. They have variable size so we 186398943Sluigi * need to scan the list to count them. 186498943Sluigi */ 1865117469Sluigi for (nstat = 1, r = data, lim = (char *)data + nbytes; 1866182823Srik r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim; 1867117469Sluigi ++nstat, r = NEXT(r) ) 186898943Sluigi ; /* nothing */ 186998943Sluigi 187098943Sluigi /* 187198943Sluigi * Count dynamic rules. This is easier as they have 187298943Sluigi * fixed size. 187398943Sluigi */ 1874117469Sluigi r = NEXT(r); 187598943Sluigi dynrules = (ipfw_dyn_rule *)r ; 1876117469Sluigi n = (char *)r - (char *)data; 187798943Sluigi ndyn = (nbytes - n) / sizeof *dynrules; 187898943Sluigi 1879112189Smaxim /* if showing stats, figure out column widths ahead of time */ 1880112189Smaxim bcwidth = pcwidth = 0; 1881117469Sluigi if (show_counters) { 1882117469Sluigi for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) { 1883170923Smaxim /* skip rules from another set */ 1884187764Sluigi if (co.use_set && r->set != co.use_set - 1) 1885170923Smaxim continue; 1886170923Smaxim 1887112189Smaxim /* packet counter */ 1888115793Sticso width = snprintf(NULL, 0, "%llu", 1889115793Sticso align_uint64(&r->pcnt)); 1890112189Smaxim if (width > pcwidth) 1891112189Smaxim pcwidth = width; 1892112189Smaxim 1893112189Smaxim /* byte counter */ 1894115793Sticso width = snprintf(NULL, 0, "%llu", 1895115793Sticso align_uint64(&r->bcnt)); 1896112189Smaxim if (width > bcwidth) 1897112189Smaxim bcwidth = width; 1898112189Smaxim } 1899112189Smaxim } 1900187764Sluigi if (co.do_dynamic && ndyn) { 1901112189Smaxim for (n = 0, d = dynrules; n < ndyn; n++, d++) { 1902187764Sluigi if (co.use_set) { 1903170923Smaxim /* skip rules from another set */ 1904171989Smaxim bcopy((char *)&d->rule + sizeof(uint16_t), 1905170923Smaxim &set, sizeof(uint8_t)); 1906187764Sluigi if (set != co.use_set - 1) 1907170923Smaxim continue; 1908170923Smaxim } 1909115793Sticso width = snprintf(NULL, 0, "%llu", 1910115793Sticso align_uint64(&d->pcnt)); 1911112189Smaxim if (width > pcwidth) 1912112189Smaxim pcwidth = width; 1913112189Smaxim 1914115793Sticso width = snprintf(NULL, 0, "%llu", 1915115793Sticso align_uint64(&d->bcnt)); 1916112189Smaxim if (width > bcwidth) 1917112189Smaxim bcwidth = width; 1918112189Smaxim } 1919112189Smaxim } 192098943Sluigi /* if no rule numbers were specified, list all rules */ 192198943Sluigi if (ac == 0) { 1922170923Smaxim for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) { 1923187764Sluigi if (co.use_set && r->set != co.use_set - 1) 1924170923Smaxim continue; 1925112189Smaxim show_ipfw(r, pcwidth, bcwidth); 1926170923Smaxim } 192798943Sluigi 1928187764Sluigi if (co.do_dynamic && ndyn) { 192998943Sluigi printf("## Dynamic rules (%d):\n", ndyn); 1930170923Smaxim for (n = 0, d = dynrules; n < ndyn; n++, d++) { 1931187764Sluigi if (co.use_set) { 1932171989Smaxim bcopy((char *)&d->rule + sizeof(uint16_t), 1933170923Smaxim &set, sizeof(uint8_t)); 1934187764Sluigi if (set != co.use_set - 1) 1935170923Smaxim continue; 1936170923Smaxim } 1937112189Smaxim show_dyn_ipfw(d, pcwidth, bcwidth); 193898943Sluigi } 1939170923Smaxim } 194098943Sluigi goto done; 194198943Sluigi } 194298943Sluigi 194398943Sluigi /* display specific rules requested on command line */ 194498943Sluigi 194598943Sluigi for (lac = ac, lav = av; lac != 0; lac--) { 194698943Sluigi /* convert command line rule # */ 1947117469Sluigi last = rnum = strtoul(*lav++, &endptr, 10); 1948117469Sluigi if (*endptr == '-') 1949117469Sluigi last = strtoul(endptr+1, &endptr, 10); 195098943Sluigi if (*endptr) { 195198943Sluigi exitval = EX_USAGE; 195298943Sluigi warnx("invalid rule number: %s", *(lav - 1)); 195398943Sluigi continue; 195498943Sluigi } 1955117469Sluigi for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) { 1956117469Sluigi if (r->rulenum > last) 195798943Sluigi break; 1958187764Sluigi if (co.use_set && r->set != co.use_set - 1) 1959170923Smaxim continue; 1960117469Sluigi if (r->rulenum >= rnum && r->rulenum <= last) { 1961112189Smaxim show_ipfw(r, pcwidth, bcwidth); 196298943Sluigi seen = 1; 196398943Sluigi } 196498943Sluigi } 196598943Sluigi if (!seen) { 196698943Sluigi /* give precedence to other error(s) */ 196798943Sluigi if (exitval == EX_OK) 196898943Sluigi exitval = EX_UNAVAILABLE; 196998943Sluigi warnx("rule %lu does not exist", rnum); 197098943Sluigi } 197198943Sluigi } 197298943Sluigi 1973187764Sluigi if (co.do_dynamic && ndyn) { 197498943Sluigi printf("## Dynamic rules:\n"); 197598943Sluigi for (lac = ac, lav = av; lac != 0; lac--) { 1976145246Sbrooks last = rnum = strtoul(*lav++, &endptr, 10); 1977117469Sluigi if (*endptr == '-') 1978117469Sluigi last = strtoul(endptr+1, &endptr, 10); 197998943Sluigi if (*endptr) 198098943Sluigi /* already warned */ 198198943Sluigi continue; 198298943Sluigi for (n = 0, d = dynrules; n < ndyn; n++, d++) { 1983115793Sticso uint16_t rulenum; 1984115793Sticso 1985115793Sticso bcopy(&d->rule, &rulenum, sizeof(rulenum)); 1986115793Sticso if (rulenum > rnum) 198798943Sluigi break; 1988187764Sluigi if (co.use_set) { 1989171989Smaxim bcopy((char *)&d->rule + sizeof(uint16_t), 1990170923Smaxim &set, sizeof(uint8_t)); 1991187764Sluigi if (set != co.use_set - 1) 1992170923Smaxim continue; 1993170923Smaxim } 1994117469Sluigi if (r->rulenum >= rnum && r->rulenum <= last) 1995112189Smaxim show_dyn_ipfw(d, pcwidth, bcwidth); 199698943Sluigi } 199798943Sluigi } 199898943Sluigi } 199998943Sluigi 200098943Sluigi ac = 0; 200198943Sluigi 200298943Sluigidone: 200398943Sluigi free(data); 200498943Sluigi 200598943Sluigi if (exitval != EX_OK) 200698943Sluigi exit(exitval); 2007117469Sluigi#undef NEXT 200898943Sluigi} 200998943Sluigi 201098943Sluigistatic int 201198943Sluigilookup_host (char *host, struct in_addr *ipaddr) 201298943Sluigi{ 201398943Sluigi struct hostent *he; 201498943Sluigi 201598943Sluigi if (!inet_aton(host, ipaddr)) { 201698943Sluigi if ((he = gethostbyname(host)) == NULL) 201798943Sluigi return(-1); 201898943Sluigi *ipaddr = *(struct in_addr *)he->h_addr_list[0]; 201998943Sluigi } 202098943Sluigi return(0); 202198943Sluigi} 202298943Sluigi 202398943Sluigi/* 202498943Sluigi * fills the addr and mask fields in the instruction as appropriate from av. 202598943Sluigi * Update length as appropriate. 202698943Sluigi * The following formats are allowed: 202798943Sluigi * me returns O_IP_*_ME 202898943Sluigi * 1.2.3.4 single IP address 202998943Sluigi * 1.2.3.4:5.6.7.8 address:mask 203098943Sluigi * 1.2.3.4/24 address/mask 203198943Sluigi * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet 2032117328Sluigi * We can have multiple comma-separated address/mask entries. 203398943Sluigi */ 203498943Sluigistatic void 203598943Sluigifill_ip(ipfw_insn_ip *cmd, char *av) 203698943Sluigi{ 2037117328Sluigi int len = 0; 2038117328Sluigi uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; 203998943Sluigi 204098943Sluigi cmd->o.len &= ~F_LEN_MASK; /* zero len */ 204198943Sluigi 2042140271Sbrooks if (_substrcmp(av, "any") == 0) 204398943Sluigi return; 204498943Sluigi 2045140271Sbrooks if (_substrcmp(av, "me") == 0) { 204698943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn); 204798943Sluigi return; 204898943Sluigi } 204998943Sluigi 2050140271Sbrooks if (strncmp(av, "table(", 6) == 0) { 2051130281Sru char *p = strchr(av + 6, ','); 2052130281Sru 2053130281Sru if (p) 2054130281Sru *p++ = '\0'; 2055130281Sru cmd->o.opcode = O_IP_DST_LOOKUP; 2056130281Sru cmd->o.arg1 = strtoul(av + 6, NULL, 0); 2057130281Sru if (p) { 2058130281Sru cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 2059130281Sru d[0] = strtoul(p, NULL, 0); 2060130281Sru } else 2061130281Sru cmd->o.len |= F_INSN_SIZE(ipfw_insn); 2062130281Sru return; 2063130281Sru } 2064130281Sru 2065117328Sluigi while (av) { 2066117328Sluigi /* 2067117328Sluigi * After the address we can have '/' or ':' indicating a mask, 2068117328Sluigi * ',' indicating another address follows, '{' indicating a 2069117328Sluigi * set of addresses of unspecified size. 2070117328Sluigi */ 2071165851Smlaier char *t = NULL, *p = strpbrk(av, "/:,{"); 2072117328Sluigi int masklen; 2073187477Sluigi char md, nd = '\0'; 2074117328Sluigi 207598943Sluigi if (p) { 207698943Sluigi md = *p; 207798943Sluigi *p++ = '\0'; 2078165851Smlaier if ((t = strpbrk(p, ",{")) != NULL) { 2079165851Smlaier nd = *t; 2080165851Smlaier *t = '\0'; 2081165851Smlaier } 2082117328Sluigi } else 2083117328Sluigi md = '\0'; 208498943Sluigi 2085117328Sluigi if (lookup_host(av, (struct in_addr *)&d[0]) != 0) 208698943Sluigi errx(EX_NOHOST, "hostname ``%s'' unknown", av); 208798943Sluigi switch (md) { 208898943Sluigi case ':': 2089117328Sluigi if (!inet_aton(p, (struct in_addr *)&d[1])) 209098943Sluigi errx(EX_DATAERR, "bad netmask ``%s''", p); 209198943Sluigi break; 209298943Sluigi case '/': 2093117328Sluigi masklen = atoi(p); 2094117328Sluigi if (masklen == 0) 2095117328Sluigi d[1] = htonl(0); /* mask */ 2096117328Sluigi else if (masklen > 32) 209798943Sluigi errx(EX_DATAERR, "bad width ``%s''", p); 209898943Sluigi else 2099117328Sluigi d[1] = htonl(~0 << (32 - masklen)); 210098943Sluigi break; 2101117328Sluigi case '{': /* no mask, assume /24 and put back the '{' */ 2102117328Sluigi d[1] = htonl(~0 << (32 - 24)); 2103117328Sluigi *(--p) = md; 2104117328Sluigi break; 2105117328Sluigi 2106117328Sluigi case ',': /* single address plus continuation */ 2107117328Sluigi *(--p) = md; 2108117328Sluigi /* FALLTHROUGH */ 2109117328Sluigi case 0: /* initialization value */ 211098943Sluigi default: 2111117328Sluigi d[1] = htonl(~0); /* force /32 */ 211298943Sluigi break; 211398943Sluigi } 2114117328Sluigi d[0] &= d[1]; /* mask base address with mask */ 2115165851Smlaier if (t) 2116165851Smlaier *t = nd; 2117117328Sluigi /* find next separator */ 211898943Sluigi if (p) 2119117328Sluigi p = strpbrk(p, ",{"); 2120117328Sluigi if (p && *p == '{') { 2121117328Sluigi /* 2122117328Sluigi * We have a set of addresses. They are stored as follows: 2123117328Sluigi * arg1 is the set size (powers of 2, 2..256) 2124117328Sluigi * addr is the base address IN HOST FORMAT 2125117328Sluigi * mask.. is an array of arg1 bits (rounded up to 2126117328Sluigi * the next multiple of 32) with bits set 2127117328Sluigi * for each host in the map. 2128117328Sluigi */ 2129117328Sluigi uint32_t *map = (uint32_t *)&cmd->mask; 213098943Sluigi int low, high; 2131117577Sluigi int i = contigmask((uint8_t *)&(d[1]), 32); 213298943Sluigi 2133117328Sluigi if (len > 0) 2134117328Sluigi errx(EX_DATAERR, "address set cannot be in a list"); 2135117328Sluigi if (i < 24 || i > 31) 2136117328Sluigi errx(EX_DATAERR, "invalid set with mask %d\n", i); 2137117328Sluigi cmd->o.arg1 = 1<<(32-i); /* map length */ 2138117328Sluigi d[0] = ntohl(d[0]); /* base addr in host format */ 213998943Sluigi cmd->o.opcode = O_IP_DST_SET; /* default */ 214098943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32; 2141101117Sluigi for (i = 0; i < (cmd->o.arg1+31)/32 ; i++) 2142117328Sluigi map[i] = 0; /* clear map */ 214398943Sluigi 2144117328Sluigi av = p + 1; 2145117328Sluigi low = d[0] & 0xff; 214698943Sluigi high = low + cmd->o.arg1 - 1; 2147117328Sluigi /* 2148117328Sluigi * Here, i stores the previous value when we specify a range 2149117328Sluigi * of addresses within a mask, e.g. 45-63. i = -1 means we 2150117328Sluigi * have no previous value. 2151117328Sluigi */ 2152116716Sluigi i = -1; /* previous value in a range */ 215398943Sluigi while (isdigit(*av)) { 215498943Sluigi char *s; 2155117328Sluigi int a = strtol(av, &s, 0); 215698943Sluigi 2157117328Sluigi if (s == av) { /* no parameter */ 2158117328Sluigi if (*av != '}') 2159117328Sluigi errx(EX_DATAERR, "set not closed\n"); 2160117328Sluigi if (i != -1) 2161117328Sluigi errx(EX_DATAERR, "incomplete range %d-", i); 2162117328Sluigi break; 2163117328Sluigi } 2164117328Sluigi if (a < low || a > high) 2165117328Sluigi errx(EX_DATAERR, "addr %d out of range [%d-%d]\n", 216698943Sluigi a, low, high); 216798943Sluigi a -= low; 2168116716Sluigi if (i == -1) /* no previous in range */ 2169116716Sluigi i = a; 2170116716Sluigi else { /* check that range is valid */ 2171116716Sluigi if (i > a) 2172116716Sluigi errx(EX_DATAERR, "invalid range %d-%d", 2173116716Sluigi i+low, a+low); 2174116716Sluigi if (*s == '-') 2175116716Sluigi errx(EX_DATAERR, "double '-' in range"); 2176116716Sluigi } 2177116716Sluigi for (; i <= a; i++) 2178117328Sluigi map[i/32] |= 1<<(i & 31); 2179116716Sluigi i = -1; 2180116716Sluigi if (*s == '-') 2181116716Sluigi i = a; 2182117328Sluigi else if (*s == '}') 2183116716Sluigi break; 218498943Sluigi av = s+1; 218598943Sluigi } 218698943Sluigi return; 218798943Sluigi } 2188117328Sluigi av = p; 2189117328Sluigi if (av) /* then *av must be a ',' */ 2190117328Sluigi av++; 219198943Sluigi 2192117328Sluigi /* Check this entry */ 2193117328Sluigi if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */ 2194117328Sluigi /* 2195117328Sluigi * 'any' turns the entire list into a NOP. 2196117328Sluigi * 'not any' never matches, so it is removed from the 2197117328Sluigi * list unless it is the only item, in which case we 2198117328Sluigi * report an error. 2199117328Sluigi */ 2200117328Sluigi if (cmd->o.len & F_NOT) { /* "not any" never matches */ 2201117328Sluigi if (av == NULL && len == 0) /* only this entry */ 2202117328Sluigi errx(EX_DATAERR, "not any never matches"); 2203117328Sluigi } 2204117328Sluigi /* else do nothing and skip this entry */ 2205128067Smaxim return; 2206117328Sluigi } 2207117328Sluigi /* A single IP can be stored in an optimized format */ 2208187762Sluigi if (d[1] == ~0 && av == NULL && len == 0) { 220998943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 2210117328Sluigi return; 2211117328Sluigi } 2212117328Sluigi len += 2; /* two words... */ 2213117328Sluigi d += 2; 2214117328Sluigi } /* end while */ 2215162363Sjhay if (len + 1 > F_LEN_MASK) 2216162363Sjhay errx(EX_DATAERR, "address list too long"); 2217117328Sluigi cmd->o.len |= len+1; 221898943Sluigi} 221998943Sluigi 222098943Sluigi 2221145246Sbrooks/* n2mask sets n bits of the mask */ 2222187769Sluigivoid 2223145246Sbrooksn2mask(struct in6_addr *mask, int n) 2224145246Sbrooks{ 2225145246Sbrooks static int minimask[9] = 2226145246Sbrooks { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; 2227145246Sbrooks u_char *p; 2228145246Sbrooks 2229145246Sbrooks memset(mask, 0, sizeof(struct in6_addr)); 2230145246Sbrooks p = (u_char *) mask; 2231145246Sbrooks for (; n > 0; p++, n -= 8) { 2232145246Sbrooks if (n >= 8) 2233145246Sbrooks *p = 0xff; 2234145246Sbrooks else 2235145246Sbrooks *p = minimask[n]; 2236145246Sbrooks } 2237145246Sbrooks return; 2238145246Sbrooks} 2239145246Sbrooks 2240145246Sbrooks 224198943Sluigi/* 224298943Sluigi * helper function to process a set of flags and set bits in the 224398943Sluigi * appropriate masks. 224498943Sluigi */ 224598943Sluigistatic void 224698943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, 224798943Sluigi struct _s_x *flags, char *p) 224898943Sluigi{ 2249117328Sluigi uint8_t set=0, clear=0; 225098943Sluigi 225198943Sluigi while (p && *p) { 225298943Sluigi char *q; /* points to the separator */ 225398943Sluigi int val; 2254117328Sluigi uint8_t *which; /* mask we are working on */ 225598943Sluigi 225698943Sluigi if (*p == '!') { 225798943Sluigi p++; 225898943Sluigi which = &clear; 225998943Sluigi } else 226098943Sluigi which = &set; 226198943Sluigi q = strchr(p, ','); 226298943Sluigi if (q) 226398943Sluigi *q++ = '\0'; 226498943Sluigi val = match_token(flags, p); 226598943Sluigi if (val <= 0) 226698943Sluigi errx(EX_DATAERR, "invalid flag %s", p); 2267117328Sluigi *which |= (uint8_t)val; 226898943Sluigi p = q; 226998943Sluigi } 227098943Sluigi cmd->opcode = opcode; 227198943Sluigi cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; 227298943Sluigi cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); 227398943Sluigi} 227498943Sluigi 227598943Sluigi 2276187767Sluigivoid 2277187767Sluigiipfw_delete(int ac, char *av[]) 227898943Sluigi{ 2279117328Sluigi uint32_t rulenum; 228098943Sluigi int i; 228198943Sluigi int exitval = EX_OK; 2282101628Sluigi int do_set = 0; 228398943Sluigi 228498943Sluigi 228598943Sluigi av++; ac--; 2286130013Scsjp NEED1("missing rule specification"); 2287140271Sbrooks if (ac > 0 && _substrcmp(*av, "set") == 0) { 2288170923Smaxim /* Do not allow using the following syntax: 2289170923Smaxim * ipfw set N delete set M 2290170923Smaxim */ 2291187764Sluigi if (co.use_set) 2292170923Smaxim errx(EX_DATAERR, "invalid syntax"); 2293101978Sluigi do_set = 1; /* delete set */ 2294101628Sluigi ac--; av++; 2295101978Sluigi } 229698943Sluigi 229798943Sluigi /* Rule number */ 229898943Sluigi while (ac && isdigit(**av)) { 229998943Sluigi i = atoi(*av); av++; ac--; 2300187764Sluigi if (co.do_nat) { 2301165648Spiso exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i); 2302165648Spiso if (exitval) { 2303165648Spiso exitval = EX_UNAVAILABLE; 2304165648Spiso warn("rule %u not available", i); 2305165648Spiso } 2306187764Sluigi } else if (co.do_pipe) { 2307187769Sluigi exitval = ipfw_delete_pipe(co.do_pipe, i); 230898943Sluigi } else { 2309187764Sluigi if (co.use_set) 2310170923Smaxim rulenum = (i & 0xffff) | (5 << 24) | 2311187764Sluigi ((co.use_set - 1) << 16); 2312170923Smaxim else 2313101978Sluigi rulenum = (i & 0xffff) | (do_set << 24); 2314117328Sluigi i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum); 231598943Sluigi if (i) { 231698943Sluigi exitval = EX_UNAVAILABLE; 231798943Sluigi warn("rule %u: setsockopt(IP_FW_DEL)", 231898943Sluigi rulenum); 231998943Sluigi } 232098943Sluigi } 232198943Sluigi } 232298943Sluigi if (exitval != EX_OK) 232398943Sluigi exit(exitval); 232498943Sluigi} 232598943Sluigi 232698943Sluigi 232798943Sluigi/* 232898943Sluigi * fill the interface structure. We do not check the name as we can 232998943Sluigi * create interfaces dynamically, so checking them at insert time 233098943Sluigi * makes relatively little sense. 2331121816Sbrooks * Interface names containing '*', '?', or '[' are assumed to be shell 2332121816Sbrooks * patterns which match interfaces. 233398943Sluigi */ 233498943Sluigistatic void 233598943Sluigifill_iface(ipfw_insn_if *cmd, char *arg) 233698943Sluigi{ 233798943Sluigi cmd->name[0] = '\0'; 233898943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); 233998943Sluigi 234098943Sluigi /* Parse the interface or address */ 2341140271Sbrooks if (strcmp(arg, "any") == 0) 234298943Sluigi cmd->o.len = 0; /* effectively ignore this command */ 234398943Sluigi else if (!isdigit(*arg)) { 2344121816Sbrooks strlcpy(cmd->name, arg, sizeof(cmd->name)); 2345121816Sbrooks cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; 234698943Sluigi } else if (!inet_aton(arg, &cmd->p.ip)) 234798943Sluigi errx(EX_DATAERR, "bad ip address ``%s''", arg); 234898943Sluigi} 234998943Sluigi 235098943Sluigistatic void 2351169424Smaximget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask) 235298943Sluigi{ 235398943Sluigi int i, l; 2354169424Smaxim char *ap, *ptr, *optr; 2355169424Smaxim struct ether_addr *mac; 2356169424Smaxim const char *macset = "0123456789abcdefABCDEF:"; 235798943Sluigi 2358169424Smaxim if (strcmp(p, "any") == 0) { 2359169424Smaxim for (i = 0; i < ETHER_ADDR_LEN; i++) 2360169424Smaxim addr[i] = mask[i] = 0; 236198943Sluigi return; 2362169424Smaxim } 236398943Sluigi 2364169424Smaxim optr = ptr = strdup(p); 2365169424Smaxim if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) { 2366169424Smaxim l = strlen(ap); 2367169424Smaxim if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL) 2368169424Smaxim errx(EX_DATAERR, "Incorrect MAC address"); 2369169424Smaxim bcopy(mac, addr, ETHER_ADDR_LEN); 2370169424Smaxim } else 2371169424Smaxim errx(EX_DATAERR, "Incorrect MAC address"); 2372169424Smaxim 2373169424Smaxim if (ptr != NULL) { /* we have mask? */ 2374169424Smaxim if (p[ptr - optr - 1] == '/') { /* mask len */ 2375169424Smaxim l = strtol(ptr, &ap, 10); 2376169424Smaxim if (*ap != 0 || l > ETHER_ADDR_LEN * 8 || l < 0) 2377169424Smaxim errx(EX_DATAERR, "Incorrect mask length"); 2378169424Smaxim for (i = 0; l > 0 && i < ETHER_ADDR_LEN; l -= 8, i++) 2379169424Smaxim mask[i] = (l >= 8) ? 0xff: (~0) << (8 - l); 2380169424Smaxim } else { /* mask */ 2381169424Smaxim l = strlen(ptr); 2382169424Smaxim if (strspn(ptr, macset) != l || 2383169424Smaxim (mac = ether_aton(ptr)) == NULL) 2384169424Smaxim errx(EX_DATAERR, "Incorrect mask"); 2385169424Smaxim bcopy(mac, mask, ETHER_ADDR_LEN); 238698943Sluigi } 2387169424Smaxim } else { /* default mask: ff:ff:ff:ff:ff:ff */ 2388169424Smaxim for (i = 0; i < ETHER_ADDR_LEN; i++) 238998943Sluigi mask[i] = 0xff; 239098943Sluigi } 2391169424Smaxim for (i = 0; i < ETHER_ADDR_LEN; i++) 239298943Sluigi addr[i] &= mask[i]; 2393169424Smaxim 2394169424Smaxim free(optr); 239598943Sluigi} 239698943Sluigi 239798943Sluigi/* 239898943Sluigi * helper function, updates the pointer to cmd with the length 239998943Sluigi * of the current command, and also cleans up the first word of 240098943Sluigi * the new command in case it has been clobbered before. 240198943Sluigi */ 240298943Sluigistatic ipfw_insn * 240398943Sluiginext_cmd(ipfw_insn *cmd) 240498943Sluigi{ 240598943Sluigi cmd += F_LEN(cmd); 240698943Sluigi bzero(cmd, sizeof(*cmd)); 240798943Sluigi return cmd; 240898943Sluigi} 240998943Sluigi 241098943Sluigi/* 2411117469Sluigi * Takes arguments and copies them into a comment 2412117469Sluigi */ 2413117469Sluigistatic void 2414117469Sluigifill_comment(ipfw_insn *cmd, int ac, char **av) 2415117469Sluigi{ 2416117469Sluigi int i, l; 2417117469Sluigi char *p = (char *)(cmd + 1); 2418117577Sluigi 2419117469Sluigi cmd->opcode = O_NOP; 2420117469Sluigi cmd->len = (cmd->len & (F_NOT | F_OR)); 2421117469Sluigi 2422117469Sluigi /* Compute length of comment string. */ 2423117469Sluigi for (i = 0, l = 0; i < ac; i++) 2424117469Sluigi l += strlen(av[i]) + 1; 2425117469Sluigi if (l == 0) 2426117469Sluigi return; 2427117469Sluigi if (l > 84) 2428117469Sluigi errx(EX_DATAERR, 2429117469Sluigi "comment too long (max 80 chars)"); 2430117469Sluigi l = 1 + (l+3)/4; 2431117469Sluigi cmd->len = (cmd->len & (F_NOT | F_OR)) | l; 2432117469Sluigi for (i = 0; i < ac; i++) { 2433117469Sluigi strcpy(p, av[i]); 2434117469Sluigi p += strlen(av[i]); 2435117469Sluigi *p++ = ' '; 2436117469Sluigi } 2437117469Sluigi *(--p) = '\0'; 2438117469Sluigi} 2439117577Sluigi 2440117469Sluigi/* 244198943Sluigi * A function to fill simple commands of size 1. 244298943Sluigi * Existing flags are preserved. 244398943Sluigi */ 244498943Sluigistatic void 2445117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg) 244698943Sluigi{ 244798943Sluigi cmd->opcode = opcode; 244898943Sluigi cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; 244998943Sluigi cmd->arg1 = arg; 245098943Sluigi} 245198943Sluigi 245298943Sluigi/* 245398943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or 245498943Sluigi * two microinstructions, and returns the pointer to the last one. 245598943Sluigi */ 245698943Sluigistatic ipfw_insn * 245798943Sluigiadd_mac(ipfw_insn *cmd, int ac, char *av[]) 245898943Sluigi{ 2459102087Sluigi ipfw_insn_mac *mac; 246098943Sluigi 2461102087Sluigi if (ac < 2) 2462102098Sluigi errx(EX_DATAERR, "MAC dst src"); 246398943Sluigi 246498943Sluigi cmd->opcode = O_MACADDR2; 246598943Sluigi cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); 246698943Sluigi 246798943Sluigi mac = (ipfw_insn_mac *)cmd; 2468101978Sluigi get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ 2469169424Smaxim get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]), 2470169424Smaxim &(mac->mask[ETHER_ADDR_LEN])); /* src */ 2471102087Sluigi return cmd; 2472102087Sluigi} 247398943Sluigi 2474102087Sluigistatic ipfw_insn * 2475102087Sluigiadd_mactype(ipfw_insn *cmd, int ac, char *av) 2476102087Sluigi{ 2477102087Sluigi if (ac < 1) 2478102087Sluigi errx(EX_DATAERR, "missing MAC type"); 2479102087Sluigi if (strcmp(av, "any") != 0) { /* we have a non-null type */ 2480102087Sluigi fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE); 248198943Sluigi cmd->opcode = O_MAC_TYPE; 2482102087Sluigi return cmd; 2483102087Sluigi } else 2484102087Sluigi return NULL; 2485102087Sluigi} 248698943Sluigi 2487102087Sluigistatic ipfw_insn * 2488152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop) 2489102087Sluigi{ 2490102087Sluigi struct protoent *pe; 2491152923Sume char *ep; 2492152923Sume int proto; 2493102087Sluigi 2494156315Sume proto = strtol(av, &ep, 10); 2495156315Sume if (*ep != '\0' || proto <= 0) { 2496152923Sume if ((pe = getprotobyname(av)) == NULL) 2497152923Sume return NULL; 2498152923Sume proto = pe->p_proto; 2499152923Sume } 2500145246Sbrooks 2501152923Sume fill_cmd(cmd, O_PROTO, 0, proto); 2502152923Sume *protop = proto; 2503152923Sume return cmd; 2504152923Sume} 2505152923Sume 2506152923Sumestatic ipfw_insn * 2507152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop) 2508152923Sume{ 2509152923Sume u_char proto = IPPROTO_IP; 2510152923Sume 2511156315Sume if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) 2512146894Smlaier ; /* do not set O_IP4 nor O_IP6 */ 2513152923Sume else if (strcmp(av, "ip4") == 0) 2514152923Sume /* explicit "just IPv4" rule */ 2515152923Sume fill_cmd(cmd, O_IP4, 0, 0); 2516152923Sume else if (strcmp(av, "ip6") == 0) { 2517152923Sume /* explicit "just IPv6" rule */ 2518152923Sume proto = IPPROTO_IPV6; 2519152923Sume fill_cmd(cmd, O_IP6, 0, 0); 2520152923Sume } else 2521152923Sume return add_proto0(cmd, av, protop); 2522152923Sume 2523152923Sume *protop = proto; 2524152923Sume return cmd; 2525152923Sume} 2526152923Sume 2527152923Sumestatic ipfw_insn * 2528152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop) 2529152923Sume{ 2530152923Sume u_char proto = IPPROTO_IP; 2531152923Sume 2532152923Sume if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) 2533152923Sume ; /* do not set O_IP4 nor O_IP6 */ 2534146894Smlaier else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0) 2535146894Smlaier /* explicit "just IPv4" rule */ 2536146894Smlaier fill_cmd(cmd, O_IP4, 0, 0); 2537146894Smlaier else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) { 2538146894Smlaier /* explicit "just IPv6" rule */ 2539152923Sume proto = IPPROTO_IPV6; 2540146894Smlaier fill_cmd(cmd, O_IP6, 0, 0); 2541152923Sume } else 2542152923Sume return add_proto0(cmd, av, protop); 2543145246Sbrooks 2544152923Sume *protop = proto; 254598943Sluigi return cmd; 254698943Sluigi} 254798943Sluigi 2548102087Sluigistatic ipfw_insn * 2549102087Sluigiadd_srcip(ipfw_insn *cmd, char *av) 2550102087Sluigi{ 2551102087Sluigi fill_ip((ipfw_insn_ip *)cmd, av); 2552102087Sluigi if (cmd->opcode == O_IP_DST_SET) /* set */ 2553102087Sluigi cmd->opcode = O_IP_SRC_SET; 2554130281Sru else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 2555130281Sru cmd->opcode = O_IP_SRC_LOOKUP; 2556102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 2557102087Sluigi cmd->opcode = O_IP_SRC_ME; 2558102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 2559102087Sluigi cmd->opcode = O_IP_SRC; 2560117328Sluigi else /* addr/mask */ 2561102087Sluigi cmd->opcode = O_IP_SRC_MASK; 2562102087Sluigi return cmd; 2563102087Sluigi} 2564102087Sluigi 2565102087Sluigistatic ipfw_insn * 2566102087Sluigiadd_dstip(ipfw_insn *cmd, char *av) 2567102087Sluigi{ 2568102087Sluigi fill_ip((ipfw_insn_ip *)cmd, av); 2569102087Sluigi if (cmd->opcode == O_IP_DST_SET) /* set */ 2570102087Sluigi ; 2571130281Sru else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 2572130281Sru ; 2573102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 2574102087Sluigi cmd->opcode = O_IP_DST_ME; 2575102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 2576102087Sluigi cmd->opcode = O_IP_DST; 2577117328Sluigi else /* addr/mask */ 2578102087Sluigi cmd->opcode = O_IP_DST_MASK; 2579102087Sluigi return cmd; 2580102087Sluigi} 2581102087Sluigi 2582102087Sluigistatic ipfw_insn * 2583102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode) 2584102087Sluigi{ 2585140271Sbrooks if (_substrcmp(av, "any") == 0) { 2586102087Sluigi return NULL; 2587102087Sluigi } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) { 2588102087Sluigi /* XXX todo: check that we have a protocol with ports */ 2589102087Sluigi cmd->opcode = opcode; 2590102087Sluigi return cmd; 2591102087Sluigi } 2592102087Sluigi return NULL; 2593102087Sluigi} 2594102087Sluigi 2595145246Sbrooksstatic ipfw_insn * 2596145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto) 2597145246Sbrooks{ 2598145246Sbrooks struct in6_addr a; 2599158553Smlaier char *host, *ch; 2600158553Smlaier ipfw_insn *ret = NULL; 2601145246Sbrooks 2602158553Smlaier if ((host = strdup(av)) == NULL) 2603158553Smlaier return NULL; 2604158553Smlaier if ((ch = strrchr(host, '/')) != NULL) 2605158553Smlaier *ch = '\0'; 2606158553Smlaier 2607145246Sbrooks if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || 2608158553Smlaier inet_pton(AF_INET6, host, &a)) 2609158553Smlaier ret = add_srcip6(cmd, av); 2610145246Sbrooks /* XXX: should check for IPv4, not !IPv6 */ 2611161483Sdwmalone if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || 2612161483Sdwmalone !inet_pton(AF_INET6, host, &a))) 2613158553Smlaier ret = add_srcip(cmd, av); 2614161483Sdwmalone if (ret == NULL && strcmp(av, "any") != 0) 2615158553Smlaier ret = cmd; 2616145246Sbrooks 2617158553Smlaier free(host); 2618158553Smlaier return ret; 2619145246Sbrooks} 2620145246Sbrooks 2621145246Sbrooksstatic ipfw_insn * 2622145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto) 2623145246Sbrooks{ 2624145246Sbrooks struct in6_addr a; 2625158553Smlaier char *host, *ch; 2626158553Smlaier ipfw_insn *ret = NULL; 2627145246Sbrooks 2628158553Smlaier if ((host = strdup(av)) == NULL) 2629158553Smlaier return NULL; 2630158553Smlaier if ((ch = strrchr(host, '/')) != NULL) 2631158553Smlaier *ch = '\0'; 2632158553Smlaier 2633145246Sbrooks if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || 2634158553Smlaier inet_pton(AF_INET6, host, &a)) 2635158553Smlaier ret = add_dstip6(cmd, av); 2636145246Sbrooks /* XXX: should check for IPv4, not !IPv6 */ 2637161483Sdwmalone if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || 2638161483Sdwmalone !inet_pton(AF_INET6, host, &a))) 2639158553Smlaier ret = add_dstip(cmd, av); 2640161483Sdwmalone if (ret == NULL && strcmp(av, "any") != 0) 2641158553Smlaier ret = cmd; 2642145246Sbrooks 2643158553Smlaier free(host); 2644158553Smlaier return ret; 2645145246Sbrooks} 2646145246Sbrooks 264798943Sluigi/* 264898943Sluigi * Parse arguments and assemble the microinstructions which make up a rule. 264998943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order 265098943Sluigi * into the actual rule. 265198943Sluigi * 2652136071Sgreen * The syntax for a rule starts with the action, followed by 2653136071Sgreen * optional action parameters, and the various match patterns. 2654108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE 265598943Sluigi * (generated if the rule includes a keep-state option), then the 2656136071Sgreen * various match patterns, log/altq actions, and the actual action. 2657106505Smaxim * 265898943Sluigi */ 2659187767Sluigivoid 2660187767Sluigiipfw_add(int ac, char *av[]) 266198943Sluigi{ 266298943Sluigi /* 266398943Sluigi * rules are added into the 'rulebuf' and then copied in 266498943Sluigi * the correct order into the actual rule. 266598943Sluigi * Some things that need to go out of order (prob, action etc.) 266698943Sluigi * go into actbuf[]. 266798943Sluigi */ 2668117328Sluigi static uint32_t rulebuf[255], actbuf[255], cmdbuf[255]; 266998943Sluigi 2670117469Sluigi ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; 2671102087Sluigi ipfw_insn *first_cmd; /* first match pattern */ 267298943Sluigi 267398943Sluigi struct ip_fw *rule; 267498943Sluigi 267598943Sluigi /* 267698943Sluigi * various flags used to record that we entered some fields. 267798943Sluigi */ 2678101116Sluigi ipfw_insn *have_state = NULL; /* check-state or keep-state */ 2679158879Soleg ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL; 2680134475Smaxim size_t len; 268198943Sluigi 268298943Sluigi int i; 268398943Sluigi 268498943Sluigi int open_par = 0; /* open parenthesis ( */ 268598943Sluigi 268698943Sluigi /* proto is here because it is used to fetch ports */ 268798943Sluigi u_char proto = IPPROTO_IP; /* default protocol */ 268898943Sluigi 2689107289Sluigi double match_prob = 1; /* match probability, default is always match */ 2690107289Sluigi 269198943Sluigi bzero(actbuf, sizeof(actbuf)); /* actions go here */ 269298943Sluigi bzero(cmdbuf, sizeof(cmdbuf)); 269398943Sluigi bzero(rulebuf, sizeof(rulebuf)); 269498943Sluigi 269598943Sluigi rule = (struct ip_fw *)rulebuf; 269698943Sluigi cmd = (ipfw_insn *)cmdbuf; 269798943Sluigi action = (ipfw_insn *)actbuf; 269898943Sluigi 269998943Sluigi av++; ac--; 270098943Sluigi 270198943Sluigi /* [rule N] -- Rule number optional */ 270298943Sluigi if (ac && isdigit(**av)) { 270398943Sluigi rule->rulenum = atoi(*av); 270498943Sluigi av++; 270598943Sluigi ac--; 270698943Sluigi } 270798943Sluigi 2708117655Sluigi /* [set N] -- set number (0..RESVD_SET), optional */ 2709140271Sbrooks if (ac > 1 && _substrcmp(*av, "set") == 0) { 2710101628Sluigi int set = strtoul(av[1], NULL, 10); 2711117655Sluigi if (set < 0 || set > RESVD_SET) 2712101628Sluigi errx(EX_DATAERR, "illegal set %s", av[1]); 2713101628Sluigi rule->set = set; 2714101628Sluigi av += 2; ac -= 2; 2715101628Sluigi } 2716101628Sluigi 271798943Sluigi /* [prob D] -- match probability, optional */ 2718140271Sbrooks if (ac > 1 && _substrcmp(*av, "prob") == 0) { 2719107289Sluigi match_prob = strtod(av[1], NULL); 272098943Sluigi 2721107289Sluigi if (match_prob <= 0 || match_prob > 1) 272298943Sluigi errx(EX_DATAERR, "illegal match prob. %s", av[1]); 272398943Sluigi av += 2; ac -= 2; 272498943Sluigi } 272598943Sluigi 272698943Sluigi /* action -- mandatory */ 272798943Sluigi NEED1("missing action"); 272898943Sluigi i = match_token(rule_actions, *av); 272998943Sluigi ac--; av++; 273098943Sluigi action->len = 1; /* default */ 273198943Sluigi switch(i) { 273298943Sluigi case TOK_CHECKSTATE: 2733101116Sluigi have_state = action; 273498943Sluigi action->opcode = O_CHECK_STATE; 273598943Sluigi break; 273698943Sluigi 273798943Sluigi case TOK_ACCEPT: 273898943Sluigi action->opcode = O_ACCEPT; 273998943Sluigi break; 274098943Sluigi 274198943Sluigi case TOK_DENY: 274298943Sluigi action->opcode = O_DENY; 274399475Sluigi action->arg1 = 0; 274498943Sluigi break; 274598943Sluigi 274699475Sluigi case TOK_REJECT: 274799475Sluigi action->opcode = O_REJECT; 274899475Sluigi action->arg1 = ICMP_UNREACH_HOST; 274999475Sluigi break; 275099475Sluigi 275199475Sluigi case TOK_RESET: 275299475Sluigi action->opcode = O_REJECT; 275399475Sluigi action->arg1 = ICMP_REJECT_RST; 275499475Sluigi break; 275599475Sluigi 2756149020Sbz case TOK_RESET6: 2757149020Sbz action->opcode = O_UNREACH6; 2758149020Sbz action->arg1 = ICMP6_UNREACH_RST; 2759149020Sbz break; 2760149020Sbz 276199475Sluigi case TOK_UNREACH: 276299475Sluigi action->opcode = O_REJECT; 276399475Sluigi NEED1("missing reject code"); 276499475Sluigi fill_reject_code(&action->arg1, *av); 276599475Sluigi ac--; av++; 276699475Sluigi break; 276799475Sluigi 2768149020Sbz case TOK_UNREACH6: 2769149020Sbz action->opcode = O_UNREACH6; 2770149020Sbz NEED1("missing unreach code"); 2771149020Sbz fill_unreach6_code(&action->arg1, *av); 2772149020Sbz ac--; av++; 2773149020Sbz break; 2774149020Sbz 277598943Sluigi case TOK_COUNT: 277698943Sluigi action->opcode = O_COUNT; 277798943Sluigi break; 277898943Sluigi 2779176517Spiso case TOK_NAT: 2780176517Spiso action->opcode = O_NAT; 2781176517Spiso action->len = F_INSN_SIZE(ipfw_insn_nat); 2782176517Spiso goto chkarg; 2783178888Sjulian 278498943Sluigi case TOK_QUEUE: 2785153374Sglebius action->opcode = O_QUEUE; 2786153374Sglebius goto chkarg; 278798943Sluigi case TOK_PIPE: 2788153374Sglebius action->opcode = O_PIPE; 2789153374Sglebius goto chkarg; 279098943Sluigi case TOK_SKIPTO: 2791153374Sglebius action->opcode = O_SKIPTO; 2792153374Sglebius goto chkarg; 2793153374Sglebius case TOK_NETGRAPH: 2794153374Sglebius action->opcode = O_NETGRAPH; 2795153374Sglebius goto chkarg; 2796153374Sglebius case TOK_NGTEE: 2797153374Sglebius action->opcode = O_NGTEE; 2798153374Sglebius goto chkarg; 279998943Sluigi case TOK_DIVERT: 2800153374Sglebius action->opcode = O_DIVERT; 2801153374Sglebius goto chkarg; 280298943Sluigi case TOK_TEE: 2803153374Sglebius action->opcode = O_TEE; 2804153374Sglebiuschkarg: 2805153374Sglebius if (!ac) 2806153374Sglebius errx(EX_USAGE, "missing argument for %s", *(av - 1)); 2807153374Sglebius if (isdigit(**av)) { 2808153374Sglebius action->arg1 = strtoul(*av, NULL, 10); 2809153374Sglebius if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG) 2810153374Sglebius errx(EX_DATAERR, "illegal argument for %s", 2811153374Sglebius *(av - 1)); 2812187762Sluigi } else if (_substrcmp(*av, "tablearg") == 0) { 2813153374Sglebius action->arg1 = IP_FW_TABLEARG; 2814153374Sglebius } else if (i == TOK_DIVERT || i == TOK_TEE) { 281598943Sluigi struct servent *s; 281698943Sluigi setservent(1); 281798943Sluigi s = getservbyname(av[0], "divert"); 281898943Sluigi if (s != NULL) 281998943Sluigi action->arg1 = ntohs(s->s_port); 282098943Sluigi else 282198943Sluigi errx(EX_DATAERR, "illegal divert/tee port"); 2822153374Sglebius } else 2823153374Sglebius errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); 282498943Sluigi ac--; av++; 282598943Sluigi break; 282698943Sluigi 282798943Sluigi case TOK_FORWARD: { 282898943Sluigi ipfw_insn_sa *p = (ipfw_insn_sa *)action; 282998943Sluigi char *s, *end; 283098943Sluigi 283198943Sluigi NEED1("missing forward address[:port]"); 283298943Sluigi 283398943Sluigi action->opcode = O_FORWARD_IP; 283498943Sluigi action->len = F_INSN_SIZE(ipfw_insn_sa); 283598943Sluigi 283698943Sluigi p->sa.sin_len = sizeof(struct sockaddr_in); 283798943Sluigi p->sa.sin_family = AF_INET; 283898943Sluigi p->sa.sin_port = 0; 283998943Sluigi /* 284098943Sluigi * locate the address-port separator (':' or ',') 284198943Sluigi */ 284298943Sluigi s = strchr(*av, ':'); 284398943Sluigi if (s == NULL) 284498943Sluigi s = strchr(*av, ','); 284598943Sluigi if (s != NULL) { 284698943Sluigi *(s++) = '\0'; 284798943Sluigi i = strtoport(s, &end, 0 /* base */, 0 /* proto */); 284898943Sluigi if (s == end) 284998943Sluigi errx(EX_DATAERR, 285098943Sluigi "illegal forwarding port ``%s''", s); 2851103241Sluigi p->sa.sin_port = (u_short)i; 285298943Sluigi } 2853161456Sjulian if (_substrcmp(*av, "tablearg") == 0) 2854161456Sjulian p->sa.sin_addr.s_addr = INADDR_ANY; 2855161456Sjulian else 2856161424Sjulian lookup_host(*av, &(p->sa.sin_addr)); 285798943Sluigi ac--; av++; 285898943Sluigi break; 2859161424Sjulian } 2860117469Sluigi case TOK_COMMENT: 2861117469Sluigi /* pretend it is a 'count' rule followed by the comment */ 2862117469Sluigi action->opcode = O_COUNT; 2863117469Sluigi ac++; av--; /* go back... */ 2864117469Sluigi break; 2865178888Sjulian 2866178888Sjulian case TOK_SETFIB: 2867178888Sjulian { 2868178888Sjulian int numfibs; 2869178916Sjulian size_t intsize = sizeof(int); 2870178888Sjulian 2871178888Sjulian action->opcode = O_SETFIB; 2872178888Sjulian NEED1("missing fib number"); 2873178888Sjulian action->arg1 = strtoul(*av, NULL, 10); 2874178916Sjulian if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1) 2875178888Sjulian errx(EX_DATAERR, "fibs not suported.\n"); 2876178888Sjulian if (action->arg1 >= numfibs) /* Temporary */ 2877178888Sjulian errx(EX_DATAERR, "fib too large.\n"); 2878178888Sjulian ac--; av++; 2879178888Sjulian break; 2880178888Sjulian } 2881165648Spiso 288298943Sluigi default: 2883102087Sluigi errx(EX_DATAERR, "invalid action %s\n", av[-1]); 288498943Sluigi } 288598943Sluigi action = next_cmd(action); 288698943Sluigi 288798943Sluigi /* 2888136071Sgreen * [altq queuename] -- altq tag, optional 288998943Sluigi * [log [logamount N]] -- log, optional 289098943Sluigi * 2891136071Sgreen * If they exist, it go first in the cmdbuf, but then it is 289298943Sluigi * skipped in the copy section to the end of the buffer. 289398943Sluigi */ 2894136071Sgreen while (ac != 0 && (i = match_token(rule_action_params, *av)) != -1) { 2895136071Sgreen ac--; av++; 2896136071Sgreen switch (i) { 2897136071Sgreen case TOK_LOG: 2898136071Sgreen { 2899136071Sgreen ipfw_insn_log *c = (ipfw_insn_log *)cmd; 2900136071Sgreen int l; 290198943Sluigi 2902136071Sgreen if (have_log) 2903136071Sgreen errx(EX_DATAERR, 2904136071Sgreen "log cannot be specified more than once"); 2905136071Sgreen have_log = (ipfw_insn *)c; 2906136071Sgreen cmd->len = F_INSN_SIZE(ipfw_insn_log); 2907136071Sgreen cmd->opcode = O_LOG; 2908140271Sbrooks if (ac && _substrcmp(*av, "logamount") == 0) { 2909136071Sgreen ac--; av++; 2910136071Sgreen NEED1("logamount requires argument"); 2911136071Sgreen l = atoi(*av); 2912136071Sgreen if (l < 0) 2913136071Sgreen errx(EX_DATAERR, 2914136071Sgreen "logamount must be positive"); 2915136071Sgreen c->max_log = l; 2916136071Sgreen ac--; av++; 2917136071Sgreen } else { 2918136071Sgreen len = sizeof(c->max_log); 2919136071Sgreen if (sysctlbyname("net.inet.ip.fw.verbose_limit", 2920136071Sgreen &c->max_log, &len, NULL, 0) == -1) 2921136071Sgreen errx(1, "sysctlbyname(\"%s\")", 2922136071Sgreen "net.inet.ip.fw.verbose_limit"); 2923136071Sgreen } 2924136071Sgreen } 2925136071Sgreen break; 2926136071Sgreen 2927136071Sgreen case TOK_ALTQ: 2928136071Sgreen { 2929136071Sgreen ipfw_insn_altq *a = (ipfw_insn_altq *)cmd; 2930136071Sgreen 2931136071Sgreen NEED1("missing altq queue name"); 2932136071Sgreen if (have_altq) 2933136071Sgreen errx(EX_DATAERR, 2934136071Sgreen "altq cannot be specified more than once"); 2935136071Sgreen have_altq = (ipfw_insn *)a; 2936136071Sgreen cmd->len = F_INSN_SIZE(ipfw_insn_altq); 2937136071Sgreen cmd->opcode = O_ALTQ; 2938136071Sgreen fill_altq_qid(&a->qid, *av); 293998943Sluigi ac--; av++; 2940136071Sgreen } 2941136071Sgreen break; 2942136071Sgreen 2943158879Soleg case TOK_TAG: 2944159636Soleg case TOK_UNTAG: { 2945159636Soleg uint16_t tag; 2946159636Soleg 2947158879Soleg if (have_tag) 2948159636Soleg errx(EX_USAGE, "tag and untag cannot be " 2949159636Soleg "specified more than once"); 2950182823Srik GET_UINT_ARG(tag, 1, IPFW_DEFAULT_RULE - 1, i, 2951182823Srik rule_action_params); 2952158879Soleg have_tag = cmd; 2953159636Soleg fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag); 2954158879Soleg ac--; av++; 2955158879Soleg break; 2956159636Soleg } 2957158879Soleg 2958136071Sgreen default: 2959136071Sgreen abort(); 296098943Sluigi } 296198943Sluigi cmd = next_cmd(cmd); 296298943Sluigi } 296398943Sluigi 2964101116Sluigi if (have_state) /* must be a check-state, we are done */ 296598943Sluigi goto done; 296698943Sluigi 296798943Sluigi#define OR_START(target) \ 296898943Sluigi if (ac && (*av[0] == '(' || *av[0] == '{')) { \ 296998943Sluigi if (open_par) \ 297098943Sluigi errx(EX_USAGE, "nested \"(\" not allowed\n"); \ 2971101641Sluigi prev = NULL; \ 297298943Sluigi open_par = 1; \ 297398943Sluigi if ( (av[0])[1] == '\0') { \ 297498943Sluigi ac--; av++; \ 297598943Sluigi } else \ 297698943Sluigi (*av)++; \ 297798943Sluigi } \ 297898943Sluigi target: \ 297998943Sluigi 298098943Sluigi 298198943Sluigi#define CLOSE_PAR \ 298298943Sluigi if (open_par) { \ 298398943Sluigi if (ac && ( \ 2984140271Sbrooks strcmp(*av, ")") == 0 || \ 2985140271Sbrooks strcmp(*av, "}") == 0)) { \ 2986101641Sluigi prev = NULL; \ 298798943Sluigi open_par = 0; \ 298898943Sluigi ac--; av++; \ 298998943Sluigi } else \ 299098943Sluigi errx(EX_USAGE, "missing \")\"\n"); \ 299198943Sluigi } 2992106505Smaxim 299398943Sluigi#define NOT_BLOCK \ 2994140271Sbrooks if (ac && _substrcmp(*av, "not") == 0) { \ 299598943Sluigi if (cmd->len & F_NOT) \ 299698943Sluigi errx(EX_USAGE, "double \"not\" not allowed\n"); \ 299798943Sluigi cmd->len |= F_NOT; \ 299898943Sluigi ac--; av++; \ 299998943Sluigi } 300098943Sluigi 300198943Sluigi#define OR_BLOCK(target) \ 3002140271Sbrooks if (ac && _substrcmp(*av, "or") == 0) { \ 300398943Sluigi if (prev == NULL || open_par == 0) \ 300498943Sluigi errx(EX_DATAERR, "invalid OR block"); \ 300598943Sluigi prev->len |= F_OR; \ 300698943Sluigi ac--; av++; \ 300798943Sluigi goto target; \ 300898943Sluigi } \ 300998943Sluigi CLOSE_PAR; 301098943Sluigi 3011102087Sluigi first_cmd = cmd; 3012102098Sluigi 3013102098Sluigi#if 0 301498943Sluigi /* 3015102087Sluigi * MAC addresses, optional. 3016102087Sluigi * If we have this, we skip the part "proto from src to dst" 3017102087Sluigi * and jump straight to the option parsing. 3018102087Sluigi */ 3019102087Sluigi NOT_BLOCK; 3020102087Sluigi NEED1("missing protocol"); 3021140271Sbrooks if (_substrcmp(*av, "MAC") == 0 || 3022140271Sbrooks _substrcmp(*av, "mac") == 0) { 3023102087Sluigi ac--; av++; /* the "MAC" keyword */ 3024102087Sluigi add_mac(cmd, ac, av); /* exits in case of errors */ 3025102087Sluigi cmd = next_cmd(cmd); 3026102087Sluigi ac -= 2; av += 2; /* dst-mac and src-mac */ 3027102087Sluigi NOT_BLOCK; 3028102087Sluigi NEED1("missing mac type"); 3029102087Sluigi if (add_mactype(cmd, ac, av[0])) 3030102087Sluigi cmd = next_cmd(cmd); 3031102087Sluigi ac--; av++; /* any or mac-type */ 3032102087Sluigi goto read_options; 3033102087Sluigi } 3034102098Sluigi#endif 3035102087Sluigi 3036102087Sluigi /* 303798943Sluigi * protocol, mandatory 303898943Sluigi */ 303998943Sluigi OR_START(get_proto); 304098943Sluigi NOT_BLOCK; 304198943Sluigi NEED1("missing protocol"); 3042152923Sume if (add_proto_compat(cmd, *av, &proto)) { 3043102087Sluigi av++; ac--; 3044147105Smlaier if (F_LEN(cmd) != 0) { 3045102087Sluigi prev = cmd; 3046102087Sluigi cmd = next_cmd(cmd); 3047102087Sluigi } 3048102098Sluigi } else if (first_cmd != cmd) { 3049116438Smaxim errx(EX_DATAERR, "invalid protocol ``%s''", *av); 3050102098Sluigi } else 3051102098Sluigi goto read_options; 305298943Sluigi OR_BLOCK(get_proto); 305398943Sluigi 305498943Sluigi /* 3055102087Sluigi * "from", mandatory 305698943Sluigi */ 3057140271Sbrooks if (!ac || _substrcmp(*av, "from") != 0) 305898943Sluigi errx(EX_USAGE, "missing ``from''"); 305998943Sluigi ac--; av++; 306098943Sluigi 306198943Sluigi /* 306298943Sluigi * source IP, mandatory 306398943Sluigi */ 306498943Sluigi OR_START(source_ip); 306598943Sluigi NOT_BLOCK; /* optional "not" */ 306698943Sluigi NEED1("missing source address"); 3067145246Sbrooks if (add_src(cmd, *av, proto)) { 3068102087Sluigi ac--; av++; 3069102087Sluigi if (F_LEN(cmd) != 0) { /* ! any */ 3070102087Sluigi prev = cmd; 3071102087Sluigi cmd = next_cmd(cmd); 3072102087Sluigi } 3073145246Sbrooks } else 3074145246Sbrooks errx(EX_USAGE, "bad source address %s", *av); 307598943Sluigi OR_BLOCK(source_ip); 307698943Sluigi 307798943Sluigi /* 307898943Sluigi * source ports, optional 307998943Sluigi */ 308098943Sluigi NOT_BLOCK; /* optional "not" */ 3081101641Sluigi if (ac) { 3082140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3083102087Sluigi add_ports(cmd, *av, proto, O_IP_SRCPORT)) { 3084102087Sluigi ac--; av++; 3085102087Sluigi if (F_LEN(cmd) != 0) 3086102087Sluigi cmd = next_cmd(cmd); 3087101641Sluigi } 308898943Sluigi } 308998943Sluigi 309098943Sluigi /* 3091102087Sluigi * "to", mandatory 309298943Sluigi */ 3093140271Sbrooks if (!ac || _substrcmp(*av, "to") != 0) 309498943Sluigi errx(EX_USAGE, "missing ``to''"); 309598943Sluigi av++; ac--; 309698943Sluigi 309798943Sluigi /* 309898943Sluigi * destination, mandatory 309998943Sluigi */ 310098943Sluigi OR_START(dest_ip); 310198943Sluigi NOT_BLOCK; /* optional "not" */ 310298943Sluigi NEED1("missing dst address"); 3103145246Sbrooks if (add_dst(cmd, *av, proto)) { 3104102087Sluigi ac--; av++; 3105102087Sluigi if (F_LEN(cmd) != 0) { /* ! any */ 3106102087Sluigi prev = cmd; 3107102087Sluigi cmd = next_cmd(cmd); 3108102087Sluigi } 3109145246Sbrooks } else 3110145246Sbrooks errx( EX_USAGE, "bad destination address %s", *av); 311198943Sluigi OR_BLOCK(dest_ip); 311298943Sluigi 311398943Sluigi /* 311498943Sluigi * dest. ports, optional 311598943Sluigi */ 311698943Sluigi NOT_BLOCK; /* optional "not" */ 3117101641Sluigi if (ac) { 3118140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3119102087Sluigi add_ports(cmd, *av, proto, O_IP_DSTPORT)) { 3120102087Sluigi ac--; av++; 3121102087Sluigi if (F_LEN(cmd) != 0) 3122102087Sluigi cmd = next_cmd(cmd); 3123101641Sluigi } 312498943Sluigi } 312598943Sluigi 312698943Sluigiread_options: 3127102087Sluigi if (ac && first_cmd == cmd) { 3128102087Sluigi /* 3129102087Sluigi * nothing specified so far, store in the rule to ease 3130102087Sluigi * printout later. 3131102087Sluigi */ 3132102087Sluigi rule->_pad = 1; 3133102087Sluigi } 313498943Sluigi prev = NULL; 313598943Sluigi while (ac) { 3136101641Sluigi char *s; 3137101641Sluigi ipfw_insn_u32 *cmd32; /* alias for cmd */ 313898943Sluigi 3139101641Sluigi s = *av; 3140101641Sluigi cmd32 = (ipfw_insn_u32 *)cmd; 3141101641Sluigi 314298943Sluigi if (*s == '!') { /* alternate syntax for NOT */ 314398943Sluigi if (cmd->len & F_NOT) 314498943Sluigi errx(EX_USAGE, "double \"not\" not allowed\n"); 314598943Sluigi cmd->len = F_NOT; 314698943Sluigi s++; 314798943Sluigi } 314898943Sluigi i = match_token(rule_options, s); 314998943Sluigi ac--; av++; 315098943Sluigi switch(i) { 315198943Sluigi case TOK_NOT: 315298943Sluigi if (cmd->len & F_NOT) 315398943Sluigi errx(EX_USAGE, "double \"not\" not allowed\n"); 315498943Sluigi cmd->len = F_NOT; 315598943Sluigi break; 315698943Sluigi 315798943Sluigi case TOK_OR: 3158101641Sluigi if (open_par == 0 || prev == NULL) 315998943Sluigi errx(EX_USAGE, "invalid \"or\" block\n"); 316098943Sluigi prev->len |= F_OR; 316198943Sluigi break; 3162101641Sluigi 3163101641Sluigi case TOK_STARTBRACE: 3164101641Sluigi if (open_par) 3165101641Sluigi errx(EX_USAGE, "+nested \"(\" not allowed\n"); 3166101641Sluigi open_par = 1; 3167101641Sluigi break; 3168101641Sluigi 3169101641Sluigi case TOK_ENDBRACE: 3170101641Sluigi if (!open_par) 3171101641Sluigi errx(EX_USAGE, "+missing \")\"\n"); 3172101641Sluigi open_par = 0; 3173102087Sluigi prev = NULL; 3174101641Sluigi break; 3175101641Sluigi 317698943Sluigi case TOK_IN: 317798943Sluigi fill_cmd(cmd, O_IN, 0, 0); 317898943Sluigi break; 317998943Sluigi 318098943Sluigi case TOK_OUT: 318198943Sluigi cmd->len ^= F_NOT; /* toggle F_NOT */ 318298943Sluigi fill_cmd(cmd, O_IN, 0, 0); 318398943Sluigi break; 318498943Sluigi 3185136073Sgreen case TOK_DIVERTED: 3186136073Sgreen fill_cmd(cmd, O_DIVERTED, 0, 3); 3187136073Sgreen break; 3188136073Sgreen 3189136073Sgreen case TOK_DIVERTEDLOOPBACK: 3190136073Sgreen fill_cmd(cmd, O_DIVERTED, 0, 1); 3191136073Sgreen break; 3192136073Sgreen 3193136073Sgreen case TOK_DIVERTEDOUTPUT: 3194136073Sgreen fill_cmd(cmd, O_DIVERTED, 0, 2); 3195136073Sgreen break; 3196136073Sgreen 319798943Sluigi case TOK_FRAG: 319898943Sluigi fill_cmd(cmd, O_FRAG, 0, 0); 319998943Sluigi break; 320098943Sluigi 320198943Sluigi case TOK_LAYER2: 320298943Sluigi fill_cmd(cmd, O_LAYER2, 0, 0); 320398943Sluigi break; 320498943Sluigi 320598943Sluigi case TOK_XMIT: 320698943Sluigi case TOK_RECV: 320798943Sluigi case TOK_VIA: 320898943Sluigi NEED1("recv, xmit, via require interface name" 320998943Sluigi " or address"); 321098943Sluigi fill_iface((ipfw_insn_if *)cmd, av[0]); 321198943Sluigi ac--; av++; 321298943Sluigi if (F_LEN(cmd) == 0) /* not a valid address */ 321398943Sluigi break; 321498943Sluigi if (i == TOK_XMIT) 321598943Sluigi cmd->opcode = O_XMIT; 321698943Sluigi else if (i == TOK_RECV) 321798943Sluigi cmd->opcode = O_RECV; 321898943Sluigi else if (i == TOK_VIA) 321998943Sluigi cmd->opcode = O_VIA; 322098943Sluigi break; 322198943Sluigi 322299475Sluigi case TOK_ICMPTYPES: 322399475Sluigi NEED1("icmptypes requires list of types"); 322499475Sluigi fill_icmptypes((ipfw_insn_u32 *)cmd, *av); 322599475Sluigi av++; ac--; 322699475Sluigi break; 3227145246Sbrooks 3228145246Sbrooks case TOK_ICMP6TYPES: 3229145246Sbrooks NEED1("icmptypes requires list of types"); 3230145246Sbrooks fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av); 3231145246Sbrooks av++; ac--; 3232145246Sbrooks break; 323399475Sluigi 323498943Sluigi case TOK_IPTTL: 323598943Sluigi NEED1("ipttl requires TTL"); 3236116690Sluigi if (strpbrk(*av, "-,")) { 3237116690Sluigi if (!add_ports(cmd, *av, 0, O_IPTTL)) 3238116690Sluigi errx(EX_DATAERR, "invalid ipttl %s", *av); 3239116690Sluigi } else 3240116690Sluigi fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); 324198943Sluigi ac--; av++; 324298943Sluigi break; 324398943Sluigi 324498943Sluigi case TOK_IPID: 3245116690Sluigi NEED1("ipid requires id"); 3246116690Sluigi if (strpbrk(*av, "-,")) { 3247116690Sluigi if (!add_ports(cmd, *av, 0, O_IPID)) 3248116690Sluigi errx(EX_DATAERR, "invalid ipid %s", *av); 3249116690Sluigi } else 3250116690Sluigi fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); 325198943Sluigi ac--; av++; 325298943Sluigi break; 325398943Sluigi 325498943Sluigi case TOK_IPLEN: 325598943Sluigi NEED1("iplen requires length"); 3256116690Sluigi if (strpbrk(*av, "-,")) { 3257116690Sluigi if (!add_ports(cmd, *av, 0, O_IPLEN)) 3258116690Sluigi errx(EX_DATAERR, "invalid ip len %s", *av); 3259116690Sluigi } else 3260116690Sluigi fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); 326198943Sluigi ac--; av++; 326298943Sluigi break; 326398943Sluigi 326498943Sluigi case TOK_IPVER: 326598943Sluigi NEED1("ipver requires version"); 326698943Sluigi fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0)); 326798943Sluigi ac--; av++; 326898943Sluigi break; 326998943Sluigi 327099475Sluigi case TOK_IPPRECEDENCE: 327199475Sluigi NEED1("ipprecedence requires value"); 327299475Sluigi fill_cmd(cmd, O_IPPRECEDENCE, 0, 327399475Sluigi (strtoul(*av, NULL, 0) & 7) << 5); 327499475Sluigi ac--; av++; 327599475Sluigi break; 327699475Sluigi 327798943Sluigi case TOK_IPOPTS: 327898943Sluigi NEED1("missing argument for ipoptions"); 3279101116Sluigi fill_flags(cmd, O_IPOPT, f_ipopts, *av); 328098943Sluigi ac--; av++; 328198943Sluigi break; 328298943Sluigi 328399475Sluigi case TOK_IPTOS: 328499475Sluigi NEED1("missing argument for iptos"); 3285101116Sluigi fill_flags(cmd, O_IPTOS, f_iptos, *av); 328699475Sluigi ac--; av++; 328799475Sluigi break; 328899475Sluigi 328998943Sluigi case TOK_UID: 329098943Sluigi NEED1("uid requires argument"); 329198943Sluigi { 329298943Sluigi char *end; 329398943Sluigi uid_t uid; 329498943Sluigi struct passwd *pwd; 329598943Sluigi 329698943Sluigi cmd->opcode = O_UID; 329798943Sluigi uid = strtoul(*av, &end, 0); 329898943Sluigi pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av); 329998943Sluigi if (pwd == NULL) 330098943Sluigi errx(EX_DATAERR, "uid \"%s\" nonexistent", *av); 3301106504Smaxim cmd32->d[0] = pwd->pw_uid; 3302135089Scsjp cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 330398943Sluigi ac--; av++; 330498943Sluigi } 330598943Sluigi break; 330698943Sluigi 330798943Sluigi case TOK_GID: 330898943Sluigi NEED1("gid requires argument"); 330998943Sluigi { 331098943Sluigi char *end; 331198943Sluigi gid_t gid; 331298943Sluigi struct group *grp; 331398943Sluigi 331498943Sluigi cmd->opcode = O_GID; 331598943Sluigi gid = strtoul(*av, &end, 0); 331698943Sluigi grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av); 331798943Sluigi if (grp == NULL) 331898943Sluigi errx(EX_DATAERR, "gid \"%s\" nonexistent", *av); 3319106504Smaxim cmd32->d[0] = grp->gr_gid; 3320135089Scsjp cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 332198943Sluigi ac--; av++; 332298943Sluigi } 332398943Sluigi break; 332498943Sluigi 3325133600Scsjp case TOK_JAIL: 3326133600Scsjp NEED1("jail requires argument"); 3327133600Scsjp { 3328133600Scsjp char *end; 3329133600Scsjp int jid; 3330133600Scsjp 3331133600Scsjp cmd->opcode = O_JAIL; 3332133600Scsjp jid = (int)strtol(*av, &end, 0); 3333133600Scsjp if (jid < 0 || *end != '\0') 3334133600Scsjp errx(EX_DATAERR, "jail requires prison ID"); 3335135554Scsjp cmd32->d[0] = (uint32_t)jid; 3336135089Scsjp cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 3337133600Scsjp ac--; av++; 3338133600Scsjp } 3339133600Scsjp break; 3340133600Scsjp 334198943Sluigi case TOK_ESTAB: 334298943Sluigi fill_cmd(cmd, O_ESTAB, 0, 0); 334398943Sluigi break; 334498943Sluigi 334598943Sluigi case TOK_SETUP: 334698943Sluigi fill_cmd(cmd, O_TCPFLAGS, 0, 334798943Sluigi (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); 334898943Sluigi break; 334998943Sluigi 3350136075Sgreen case TOK_TCPDATALEN: 3351136075Sgreen NEED1("tcpdatalen requires length"); 3352136075Sgreen if (strpbrk(*av, "-,")) { 3353136075Sgreen if (!add_ports(cmd, *av, 0, O_TCPDATALEN)) 3354136075Sgreen errx(EX_DATAERR, "invalid tcpdata len %s", *av); 3355136075Sgreen } else 3356136075Sgreen fill_cmd(cmd, O_TCPDATALEN, 0, 3357136075Sgreen strtoul(*av, NULL, 0)); 3358136075Sgreen ac--; av++; 3359136075Sgreen break; 3360136075Sgreen 336198943Sluigi case TOK_TCPOPTS: 336298943Sluigi NEED1("missing argument for tcpoptions"); 336398943Sluigi fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av); 336498943Sluigi ac--; av++; 336598943Sluigi break; 336698943Sluigi 336798943Sluigi case TOK_TCPSEQ: 336898943Sluigi case TOK_TCPACK: 336998943Sluigi NEED1("tcpseq/tcpack requires argument"); 337098943Sluigi cmd->len = F_INSN_SIZE(ipfw_insn_u32); 337198943Sluigi cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK; 337298943Sluigi cmd32->d[0] = htonl(strtoul(*av, NULL, 0)); 337398943Sluigi ac--; av++; 337498943Sluigi break; 337598943Sluigi 337698943Sluigi case TOK_TCPWIN: 337798943Sluigi NEED1("tcpwin requires length"); 337898943Sluigi fill_cmd(cmd, O_TCPWIN, 0, 337998943Sluigi htons(strtoul(*av, NULL, 0))); 338098943Sluigi ac--; av++; 338198943Sluigi break; 338298943Sluigi 338398943Sluigi case TOK_TCPFLAGS: 338498943Sluigi NEED1("missing argument for tcpflags"); 338598943Sluigi cmd->opcode = O_TCPFLAGS; 338698943Sluigi fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av); 338798943Sluigi ac--; av++; 338898943Sluigi break; 338998943Sluigi 339098943Sluigi case TOK_KEEPSTATE: 3391101641Sluigi if (open_par) 3392101641Sluigi errx(EX_USAGE, "keep-state cannot be part " 3393101641Sluigi "of an or block"); 339499909Sluigi if (have_state) 3395101116Sluigi errx(EX_USAGE, "only one of keep-state " 339699909Sluigi "and limit is allowed"); 3397101116Sluigi have_state = cmd; 339898943Sluigi fill_cmd(cmd, O_KEEP_STATE, 0, 0); 339998943Sluigi break; 340098943Sluigi 3401159636Soleg case TOK_LIMIT: { 3402159636Soleg ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; 3403159636Soleg int val; 3404159636Soleg 3405101641Sluigi if (open_par) 3406159636Soleg errx(EX_USAGE, 3407159636Soleg "limit cannot be part of an or block"); 340899909Sluigi if (have_state) 3409168819Smaxim errx(EX_USAGE, "only one of keep-state and " 3410159636Soleg "limit is allowed"); 3411101116Sluigi have_state = cmd; 341298943Sluigi 341398943Sluigi cmd->len = F_INSN_SIZE(ipfw_insn_limit); 341498943Sluigi cmd->opcode = O_LIMIT; 3415159636Soleg c->limit_mask = c->conn_limit = 0; 341698943Sluigi 3417159636Soleg while (ac > 0) { 3418159636Soleg if ((val = match_token(limit_masks, *av)) <= 0) 341998943Sluigi break; 342098943Sluigi c->limit_mask |= val; 342198943Sluigi ac--; av++; 342298943Sluigi } 3423159636Soleg 342498943Sluigi if (c->limit_mask == 0) 3425159636Soleg errx(EX_USAGE, "limit: missing limit mask"); 3426159636Soleg 3427182823Srik GET_UINT_ARG(c->conn_limit, 1, IPFW_DEFAULT_RULE - 1, 3428182823Srik TOK_LIMIT, rule_options); 3429159636Soleg 343098943Sluigi ac--; av++; 343198943Sluigi break; 3432159636Soleg } 343398943Sluigi 3434102087Sluigi case TOK_PROTO: 3435102087Sluigi NEED1("missing protocol"); 3436145246Sbrooks if (add_proto(cmd, *av, &proto)) { 3437102087Sluigi ac--; av++; 3438102098Sluigi } else 3439116438Smaxim errx(EX_DATAERR, "invalid protocol ``%s''", 3440116438Smaxim *av); 3441102087Sluigi break; 3442106505Smaxim 3443102087Sluigi case TOK_SRCIP: 3444102087Sluigi NEED1("missing source IP"); 3445102087Sluigi if (add_srcip(cmd, *av)) { 3446102087Sluigi ac--; av++; 3447102087Sluigi } 3448102087Sluigi break; 3449102087Sluigi 3450102087Sluigi case TOK_DSTIP: 3451102087Sluigi NEED1("missing destination IP"); 3452102087Sluigi if (add_dstip(cmd, *av)) { 3453102087Sluigi ac--; av++; 3454102087Sluigi } 3455102087Sluigi break; 3456102087Sluigi 3457145246Sbrooks case TOK_SRCIP6: 3458145246Sbrooks NEED1("missing source IP6"); 3459145246Sbrooks if (add_srcip6(cmd, *av)) { 3460145246Sbrooks ac--; av++; 3461145246Sbrooks } 3462145246Sbrooks break; 3463145246Sbrooks 3464145246Sbrooks case TOK_DSTIP6: 3465145246Sbrooks NEED1("missing destination IP6"); 3466145246Sbrooks if (add_dstip6(cmd, *av)) { 3467145246Sbrooks ac--; av++; 3468145246Sbrooks } 3469145246Sbrooks break; 3470145246Sbrooks 3471102087Sluigi case TOK_SRCPORT: 3472102087Sluigi NEED1("missing source port"); 3473140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3474102087Sluigi add_ports(cmd, *av, proto, O_IP_SRCPORT)) { 3475102087Sluigi ac--; av++; 3476102087Sluigi } else 3477102087Sluigi errx(EX_DATAERR, "invalid source port %s", *av); 3478102087Sluigi break; 3479102087Sluigi 3480102087Sluigi case TOK_DSTPORT: 3481102087Sluigi NEED1("missing destination port"); 3482140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3483102087Sluigi add_ports(cmd, *av, proto, O_IP_DSTPORT)) { 3484102087Sluigi ac--; av++; 3485102087Sluigi } else 3486102087Sluigi errx(EX_DATAERR, "invalid destination port %s", 3487102087Sluigi *av); 3488102087Sluigi break; 3489102087Sluigi 3490102087Sluigi case TOK_MAC: 3491102087Sluigi if (add_mac(cmd, ac, av)) { 3492102087Sluigi ac -= 2; av += 2; 3493102087Sluigi } 3494102087Sluigi break; 3495102087Sluigi 3496102087Sluigi case TOK_MACTYPE: 3497102087Sluigi NEED1("missing mac type"); 3498102087Sluigi if (!add_mactype(cmd, ac, *av)) 3499116438Smaxim errx(EX_DATAERR, "invalid mac type %s", *av); 3500102087Sluigi ac--; av++; 3501102087Sluigi break; 3502102087Sluigi 3503112250Scjc case TOK_VERREVPATH: 3504112250Scjc fill_cmd(cmd, O_VERREVPATH, 0, 0); 3505112250Scjc break; 3506116919Sluigi 3507128575Sandre case TOK_VERSRCREACH: 3508128575Sandre fill_cmd(cmd, O_VERSRCREACH, 0, 0); 3509128575Sandre break; 3510128575Sandre 3511133387Sandre case TOK_ANTISPOOF: 3512133387Sandre fill_cmd(cmd, O_ANTISPOOF, 0, 0); 3513133387Sandre break; 3514133387Sandre 3515117241Sluigi case TOK_IPSEC: 3516117241Sluigi fill_cmd(cmd, O_IPSEC, 0, 0); 3517117241Sluigi break; 3518117241Sluigi 3519145246Sbrooks case TOK_IPV6: 3520145246Sbrooks fill_cmd(cmd, O_IP6, 0, 0); 3521145246Sbrooks break; 3522145246Sbrooks 3523146894Smlaier case TOK_IPV4: 3524146894Smlaier fill_cmd(cmd, O_IP4, 0, 0); 3525146894Smlaier break; 3526146894Smlaier 3527145246Sbrooks case TOK_EXT6HDR: 3528145246Sbrooks fill_ext6hdr( cmd, *av ); 3529145246Sbrooks ac--; av++; 3530145246Sbrooks break; 3531145246Sbrooks 3532145246Sbrooks case TOK_FLOWID: 3533145246Sbrooks if (proto != IPPROTO_IPV6 ) 3534145246Sbrooks errx( EX_USAGE, "flow-id filter is active " 3535145246Sbrooks "only for ipv6 protocol\n"); 3536145246Sbrooks fill_flow6( (ipfw_insn_u32 *) cmd, *av ); 3537145246Sbrooks ac--; av++; 3538145246Sbrooks break; 3539145246Sbrooks 3540117469Sluigi case TOK_COMMENT: 3541117469Sluigi fill_comment(cmd, ac, av); 3542117469Sluigi av += ac; 3543117469Sluigi ac = 0; 3544117469Sluigi break; 3545117469Sluigi 3546158879Soleg case TOK_TAGGED: 3547159636Soleg if (ac > 0 && strpbrk(*av, "-,")) { 3548158879Soleg if (!add_ports(cmd, *av, 0, O_TAGGED)) 3549159636Soleg errx(EX_DATAERR, "tagged: invalid tag" 3550159636Soleg " list: %s", *av); 3551158879Soleg } 3552159636Soleg else { 3553159636Soleg uint16_t tag; 3554159636Soleg 3555182823Srik GET_UINT_ARG(tag, 1, IPFW_DEFAULT_RULE - 1, 3556182823Srik TOK_TAGGED, rule_options); 3557159636Soleg fill_cmd(cmd, O_TAGGED, 0, tag); 3558159636Soleg } 3559158879Soleg ac--; av++; 3560158879Soleg break; 3561158879Soleg 3562178888Sjulian case TOK_FIB: 3563178888Sjulian NEED1("fib requires fib number"); 3564178888Sjulian fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0)); 3565178888Sjulian ac--; av++; 3566178888Sjulian break; 3567178888Sjulian 356898943Sluigi default: 356998943Sluigi errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); 357098943Sluigi } 357198943Sluigi if (F_LEN(cmd) > 0) { /* prepare to advance */ 357298943Sluigi prev = cmd; 357398943Sluigi cmd = next_cmd(cmd); 357498943Sluigi } 357598943Sluigi } 357698943Sluigi 357798943Sluigidone: 357898943Sluigi /* 357998943Sluigi * Now copy stuff into the rule. 358098943Sluigi * If we have a keep-state option, the first instruction 358198943Sluigi * must be a PROBE_STATE (which is generated here). 358298943Sluigi * If we have a LOG option, it was stored as the first command, 358398943Sluigi * and now must be moved to the top of the action part. 358498943Sluigi */ 358598943Sluigi dst = (ipfw_insn *)rule->cmd; 358698943Sluigi 358798943Sluigi /* 3588107289Sluigi * First thing to write into the command stream is the match probability. 3589107289Sluigi */ 3590107289Sluigi if (match_prob != 1) { /* 1 means always match */ 3591107289Sluigi dst->opcode = O_PROB; 3592107289Sluigi dst->len = 2; 3593107289Sluigi *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); 3594107289Sluigi dst += dst->len; 3595107289Sluigi } 3596107289Sluigi 3597107289Sluigi /* 359898943Sluigi * generate O_PROBE_STATE if necessary 359998943Sluigi */ 3600101116Sluigi if (have_state && have_state->opcode != O_CHECK_STATE) { 360198943Sluigi fill_cmd(dst, O_PROBE_STATE, 0, 0); 360298943Sluigi dst = next_cmd(dst); 360398943Sluigi } 3604158879Soleg 3605158879Soleg /* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */ 360698943Sluigi for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { 360798943Sluigi i = F_LEN(src); 360898943Sluigi 3609101116Sluigi switch (src->opcode) { 3610101116Sluigi case O_LOG: 3611101116Sluigi case O_KEEP_STATE: 3612101116Sluigi case O_LIMIT: 3613136071Sgreen case O_ALTQ: 3614158879Soleg case O_TAG: 3615101116Sluigi break; 3616101116Sluigi default: 3617117328Sluigi bcopy(src, dst, i * sizeof(uint32_t)); 361898943Sluigi dst += i; 361998943Sluigi } 362098943Sluigi } 362198943Sluigi 362298943Sluigi /* 3623101116Sluigi * put back the have_state command as last opcode 3624101116Sluigi */ 3625101295Sluigi if (have_state && have_state->opcode != O_CHECK_STATE) { 3626101116Sluigi i = F_LEN(have_state); 3627117328Sluigi bcopy(have_state, dst, i * sizeof(uint32_t)); 3628101116Sluigi dst += i; 3629101116Sluigi } 3630101116Sluigi /* 363198943Sluigi * start action section 363298943Sluigi */ 363398943Sluigi rule->act_ofs = dst - rule->cmd; 363498943Sluigi 3635158879Soleg /* put back O_LOG, O_ALTQ, O_TAG if necessary */ 3636136071Sgreen if (have_log) { 3637136071Sgreen i = F_LEN(have_log); 3638136071Sgreen bcopy(have_log, dst, i * sizeof(uint32_t)); 363998943Sluigi dst += i; 364098943Sluigi } 3641136071Sgreen if (have_altq) { 3642136071Sgreen i = F_LEN(have_altq); 3643136071Sgreen bcopy(have_altq, dst, i * sizeof(uint32_t)); 3644136071Sgreen dst += i; 3645136071Sgreen } 3646158879Soleg if (have_tag) { 3647158879Soleg i = F_LEN(have_tag); 3648158879Soleg bcopy(have_tag, dst, i * sizeof(uint32_t)); 3649158879Soleg dst += i; 3650158879Soleg } 365198943Sluigi /* 365298943Sluigi * copy all other actions 365398943Sluigi */ 365498943Sluigi for (src = (ipfw_insn *)actbuf; src != action; src += i) { 365598943Sluigi i = F_LEN(src); 3656117328Sluigi bcopy(src, dst, i * sizeof(uint32_t)); 365798943Sluigi dst += i; 365898943Sluigi } 365998943Sluigi 3660117328Sluigi rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd); 3661117469Sluigi i = (char *)dst - (char *)rule; 3662119740Stmm if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1) 366398943Sluigi err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD"); 3664187764Sluigi if (!co.do_quiet) 3665117469Sluigi show_ipfw(rule, 0, 0); 366698943Sluigi} 366798943Sluigi 3668187767Sluigi/* 3669187767Sluigi * clear the counters or the log counters. 3670187767Sluigi */ 3671187767Sluigivoid 3672187767Sluigiipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */) 367398943Sluigi{ 3674170923Smaxim uint32_t arg, saved_arg; 367598943Sluigi int failed = EX_OK; 3676170923Smaxim char const *errstr; 3677187767Sluigi char const *name = optname ? "RESETLOG" : "ZERO"; 367898943Sluigi 3679187767Sluigi optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO; 3680187767Sluigi 368198943Sluigi av++; ac--; 368298943Sluigi 368398943Sluigi if (!ac) { 368498943Sluigi /* clear all entries */ 3685117328Sluigi if (do_cmd(optname, NULL, 0) < 0) 3686117328Sluigi err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name); 3687187764Sluigi if (!co.do_quiet) 3688117328Sluigi printf("%s.\n", optname == IP_FW_ZERO ? 3689117328Sluigi "Accounting cleared":"Logging counts reset"); 369098943Sluigi 369198943Sluigi return; 369298943Sluigi } 369398943Sluigi 369498943Sluigi while (ac) { 369598943Sluigi /* Rule number */ 369698943Sluigi if (isdigit(**av)) { 3697170923Smaxim arg = strtonum(*av, 0, 0xffff, &errstr); 3698170923Smaxim if (errstr) 3699170923Smaxim errx(EX_DATAERR, 3700170923Smaxim "invalid rule number %s\n", *av); 3701170923Smaxim saved_arg = arg; 3702187764Sluigi if (co.use_set) 3703187764Sluigi arg |= (1 << 24) | ((co.use_set - 1) << 16); 370498943Sluigi av++; 370598943Sluigi ac--; 3706170923Smaxim if (do_cmd(optname, &arg, sizeof(arg))) { 3707117328Sluigi warn("rule %u: setsockopt(IP_FW_%s)", 3708170923Smaxim saved_arg, name); 370998943Sluigi failed = EX_UNAVAILABLE; 3710187764Sluigi } else if (!co.do_quiet) 3711170923Smaxim printf("Entry %d %s.\n", saved_arg, 3712117328Sluigi optname == IP_FW_ZERO ? 3713117328Sluigi "cleared" : "logging count reset"); 371498943Sluigi } else { 371598943Sluigi errx(EX_USAGE, "invalid rule number ``%s''", *av); 371698943Sluigi } 371798943Sluigi } 371898943Sluigi if (failed != EX_OK) 371998943Sluigi exit(failed); 372098943Sluigi} 372198943Sluigi 3722187767Sluigivoid 3723187767Sluigiipfw_flush(int force) 372498943Sluigi{ 3725187764Sluigi int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH; 372698943Sluigi 3727187764Sluigi if (!force && !co.do_quiet) { /* need to ask user */ 372898943Sluigi int c; 372998943Sluigi 373098943Sluigi printf("Are you sure? [yn] "); 373198943Sluigi fflush(stdout); 373298943Sluigi do { 373398943Sluigi c = toupper(getc(stdin)); 373498943Sluigi while (c != '\n' && getc(stdin) != '\n') 373598943Sluigi if (feof(stdin)) 373698943Sluigi return; /* and do not flush */ 373798943Sluigi } while (c != 'Y' && c != 'N'); 373898943Sluigi printf("\n"); 373998943Sluigi if (c == 'N') /* user said no */ 374098943Sluigi return; 374198943Sluigi } 3742170923Smaxim /* `ipfw set N flush` - is the same that `ipfw delete set N` */ 3743187764Sluigi if (co.use_set) { 3744187764Sluigi uint32_t arg = ((co.use_set - 1) & 0xffff) | (1 << 24); 3745170923Smaxim if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0) 3746170923Smaxim err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)"); 3747170923Smaxim } else if (do_cmd(cmd, NULL, 0) < 0) 374898943Sluigi err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)", 3749187764Sluigi co.do_pipe ? "DUMMYNET" : "FW"); 3750187764Sluigi if (!co.do_quiet) 3751187764Sluigi printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules"); 375298943Sluigi} 375398943Sluigi 3754117544Sluigi 3755183407Srikstatic void table_list(ipfw_table_entry ent, int need_header); 3756183228Srik 3757117544Sluigi/* 3758130281Sru * This one handles all table-related commands 3759130281Sru * ipfw table N add addr[/masklen] [value] 3760130281Sru * ipfw table N delete addr[/masklen] 3761183407Srik * ipfw table {N | all} flush 3762183407Srik * ipfw table {N | all} list 3763130281Sru */ 3764187767Sluigivoid 3765187767Sluigiipfw_table_handler(int ac, char *av[]) 3766130281Sru{ 3767130281Sru ipfw_table_entry ent; 3768130281Sru int do_add; 3769183407Srik int is_all; 3770183241Srik size_t len; 3771130281Sru char *p; 3772183407Srik uint32_t a; 3773183241Srik uint32_t tables_max; 3774130281Sru 3775183263Skeramida len = sizeof(tables_max); 3776183241Srik if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, 3777183241Srik NULL, 0) == -1) { 3778183241Srik#ifdef IPFW_TABLES_MAX 3779183241Srik warn("Warn: Failed to get the max tables number via sysctl. " 3780183241Srik "Using the compiled in defaults. \nThe reason was"); 3781183241Srik tables_max = IPFW_TABLES_MAX; 3782183241Srik#else 3783183241Srik errx(1, "Failed sysctlbyname(\"net.inet.ip.fw.tables_max\")"); 3784183241Srik#endif 3785183241Srik } 3786183241Srik 3787130281Sru ac--; av++; 3788130281Sru if (ac && isdigit(**av)) { 3789130281Sru ent.tbl = atoi(*av); 3790183407Srik is_all = 0; 3791130281Sru ac--; av++; 3792183407Srik } else if (ac && _substrcmp(*av, "all") == 0) { 3793183407Srik ent.tbl = 0; 3794183407Srik is_all = 1; 3795183407Srik ac--; av++; 3796130281Sru } else 3797183407Srik errx(EX_USAGE, "table number or 'all' keyword required"); 3798183241Srik if (ent.tbl >= tables_max) 3799183241Srik errx(EX_USAGE, "The table number exceeds the maximum allowed " 3800183241Srik "value (%d)", tables_max - 1); 3801130281Sru NEED1("table needs command"); 3802183407Srik if (is_all && _substrcmp(*av, "list") != 0 3803183407Srik && _substrcmp(*av, "flush") != 0) 3804183407Srik errx(EX_USAGE, "table number required"); 3805183407Srik 3806140271Sbrooks if (_substrcmp(*av, "add") == 0 || 3807140271Sbrooks _substrcmp(*av, "delete") == 0) { 3808130281Sru do_add = **av == 'a'; 3809130281Sru ac--; av++; 3810130281Sru if (!ac) 3811130281Sru errx(EX_USAGE, "IP address required"); 3812130281Sru p = strchr(*av, '/'); 3813130281Sru if (p) { 3814130281Sru *p++ = '\0'; 3815130281Sru ent.masklen = atoi(p); 3816130281Sru if (ent.masklen > 32) 3817130281Sru errx(EX_DATAERR, "bad width ``%s''", p); 3818130281Sru } else 3819130281Sru ent.masklen = 32; 3820130281Sru if (lookup_host(*av, (struct in_addr *)&ent.addr) != 0) 3821130298Sru errx(EX_NOHOST, "hostname ``%s'' unknown", *av); 3822130281Sru ac--; av++; 3823161424Sjulian if (do_add && ac) { 3824161424Sjulian unsigned int tval; 3825161424Sjulian /* isdigit is a bit of a hack here.. */ 3826161424Sjulian if (strchr(*av, (int)'.') == NULL && isdigit(**av)) { 3827161424Sjulian ent.value = strtoul(*av, NULL, 0); 3828161424Sjulian } else { 3829161424Sjulian if (lookup_host(*av, (struct in_addr *)&tval) == 0) { 3830161424Sjulian /* The value must be stored in host order * 3831161424Sjulian * so that the values < 65k can be distinguished */ 3832161424Sjulian ent.value = ntohl(tval); 3833161424Sjulian } else { 3834161424Sjulian errx(EX_NOHOST, "hostname ``%s'' unknown", *av); 3835161424Sjulian } 3836161424Sjulian } 3837161424Sjulian } else 3838130281Sru ent.value = 0; 3839130281Sru if (do_cmd(do_add ? IP_FW_TABLE_ADD : IP_FW_TABLE_DEL, 3840157335Sjulian &ent, sizeof(ent)) < 0) { 3841155639Sjulian /* If running silent, don't bomb out on these errors. */ 3842187764Sluigi if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH)))) 3843155639Sjulian err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)", 3844155639Sjulian do_add ? "ADD" : "DEL"); 3845155639Sjulian /* In silent mode, react to a failed add by deleting */ 3846157332Sjulian if (do_add) { 3847155639Sjulian do_cmd(IP_FW_TABLE_DEL, &ent, sizeof(ent)); 3848155639Sjulian if (do_cmd(IP_FW_TABLE_ADD, 3849155639Sjulian &ent, sizeof(ent)) < 0) 3850155639Sjulian err(EX_OSERR, 3851155639Sjulian "setsockopt(IP_FW_TABLE_ADD)"); 3852157332Sjulian } 3853157335Sjulian } 3854140271Sbrooks } else if (_substrcmp(*av, "flush") == 0) { 3855183407Srik a = is_all ? tables_max : (ent.tbl + 1); 3856183407Srik do { 3857183407Srik if (do_cmd(IP_FW_TABLE_FLUSH, &ent.tbl, 3858183407Srik sizeof(ent.tbl)) < 0) 3859183407Srik err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)"); 3860183407Srik } while (++ent.tbl < a); 3861140271Sbrooks } else if (_substrcmp(*av, "list") == 0) { 3862183407Srik a = is_all ? tables_max : (ent.tbl + 1); 3863183407Srik do { 3864183415Srik table_list(ent, is_all); 3865183407Srik } while (++ent.tbl < a); 3866183228Srik } else 3867183228Srik errx(EX_USAGE, "invalid table command %s", *av); 3868183228Srik} 3869183205Srik 3870183228Srikstatic void 3871183407Sriktable_list(ipfw_table_entry ent, int need_header) 3872183228Srik{ 3873183228Srik ipfw_table *tbl; 3874183228Srik socklen_t l; 3875183228Srik uint32_t a; 3876183205Srik 3877183228Srik a = ent.tbl; 3878183228Srik l = sizeof(a); 3879183228Srik if (do_cmd(IP_FW_TABLE_GETSIZE, &a, (uintptr_t)&l) < 0) 3880183228Srik err(EX_OSERR, "getsockopt(IP_FW_TABLE_GETSIZE)"); 3881183228Srik 3882183228Srik /* If a is zero we have nothing to do, the table is empty. */ 3883183228Srik if (a == 0) 3884183228Srik return; 3885183228Srik 3886183228Srik l = sizeof(*tbl) + a * sizeof(ipfw_table_entry); 3887187716Sluigi tbl = safe_calloc(1, l); 3888183228Srik tbl->tbl = ent.tbl; 3889183228Srik if (do_cmd(IP_FW_TABLE_LIST, tbl, (uintptr_t)&l) < 0) 3890183228Srik err(EX_OSERR, "getsockopt(IP_FW_TABLE_LIST)"); 3891183407Srik if (tbl->cnt && need_header) 3892183407Srik printf("---table(%d)---\n", tbl->tbl); 3893183228Srik for (a = 0; a < tbl->cnt; a++) { 3894183228Srik unsigned int tval; 3895183228Srik tval = tbl->ent[a].value; 3896187764Sluigi if (co.do_value_as_ip) { 3897183228Srik char tbuf[128]; 3898183228Srik strncpy(tbuf, inet_ntoa(*(struct in_addr *) 3899161456Sjulian &tbl->ent[a].addr), 127); 3900183228Srik /* inet_ntoa expects network order */ 3901183228Srik tval = htonl(tval); 3902183228Srik printf("%s/%u %s\n", tbuf, tbl->ent[a].masklen, 3903183228Srik inet_ntoa(*(struct in_addr *)&tval)); 3904183228Srik } else { 3905183228Srik printf("%s/%u %u\n", 3906183228Srik inet_ntoa(*(struct in_addr *)&tbl->ent[a].addr), 3907183228Srik tbl->ent[a].masklen, tval); 3908130281Sru } 3909183228Srik } 3910183228Srik free(tbl); 3911130281Sru} 3912