ipfw2.c revision 234597
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: stable/9/sbin/ipfw/ipfw2.c 234597 2012-04-23 07:15:15Z melifaro $ 2198943Sluigi */ 2298943Sluigi 23187604Sluigi#include <sys/types.h> 24223262Sbenl#include <sys/param.h> 2598943Sluigi#include <sys/socket.h> 2698943Sluigi#include <sys/sockio.h> 2798943Sluigi#include <sys/sysctl.h> 2898943Sluigi 29187767Sluigi#include "ipfw2.h" 30187767Sluigi 3198943Sluigi#include <ctype.h> 3298943Sluigi#include <err.h> 3398943Sluigi#include <errno.h> 3498943Sluigi#include <grp.h> 3598943Sluigi#include <netdb.h> 3698943Sluigi#include <pwd.h> 3798943Sluigi#include <stdio.h> 3898943Sluigi#include <stdlib.h> 3998943Sluigi#include <string.h> 4098943Sluigi#include <sysexits.h> 41187983Sluigi#include <time.h> /* ctime */ 42187604Sluigi#include <timeconv.h> /* _long_to_time */ 43136071Sgreen#include <unistd.h> 44136071Sgreen#include <fcntl.h> 45234597Smelifaro#include <stddef.h> /* offsetof */ 4698943Sluigi 47169424Smaxim#include <net/ethernet.h> 48187983Sluigi#include <net/if.h> /* only IFNAMSIZ */ 4998943Sluigi#include <netinet/in.h> 50187983Sluigi#include <netinet/in_systm.h> /* only n_short, n_long */ 5198943Sluigi#include <netinet/ip.h> 5298943Sluigi#include <netinet/ip_icmp.h> 5398943Sluigi#include <netinet/ip_fw.h> 5498943Sluigi#include <netinet/tcp.h> 5598943Sluigi#include <arpa/inet.h> 5698943Sluigi 57187767Sluigistruct cmdline_opts co; /* global options */ 5898943Sluigi 59187767Sluigiint resvd_set_number = RESVD_SET; 60187764Sluigi 61234597Smelifaroint ipfw_socket = -1; 62234597Smelifaro 63234597Smelifaro#ifndef s6_addr32 64234597Smelifaro#define s6_addr32 __u6_addr.__u6_addr32 65234597Smelifaro#endif 66234597Smelifaro 67159636Soleg#define GET_UINT_ARG(arg, min, max, tok, s_x) do { \ 68204591Sluigi if (!av[0]) \ 69159636Soleg errx(EX_USAGE, "%s: missing argument", match_value(s_x, tok)); \ 70159636Soleg if (_substrcmp(*av, "tablearg") == 0) { \ 71159636Soleg arg = IP_FW_TABLEARG; \ 72159636Soleg break; \ 73159636Soleg } \ 74159636Soleg \ 75159636Soleg { \ 76204591Sluigi long _xval; \ 77159636Soleg char *end; \ 78159636Soleg \ 79204591Sluigi _xval = strtol(*av, &end, 10); \ 80159636Soleg \ 81204591Sluigi if (!isdigit(**av) || *end != '\0' || (_xval == 0 && errno == EINVAL)) \ 82159636Soleg errx(EX_DATAERR, "%s: invalid argument: %s", \ 83159636Soleg match_value(s_x, tok), *av); \ 84159636Soleg \ 85204591Sluigi if (errno == ERANGE || _xval < min || _xval > max) \ 86159636Soleg errx(EX_DATAERR, "%s: argument is out of range (%u..%u): %s", \ 87159636Soleg match_value(s_x, tok), min, max, *av); \ 88159636Soleg \ 89204591Sluigi if (_xval == IP_FW_TABLEARG) \ 90159636Soleg errx(EX_DATAERR, "%s: illegal argument value: %s", \ 91159636Soleg match_value(s_x, tok), *av); \ 92204591Sluigi arg = _xval; \ 93159636Soleg } \ 94158879Soleg} while (0) 95158879Soleg 96187762Sluigistatic void 97187762SluigiPRINT_UINT_ARG(const char *str, uint32_t arg) 98187762Sluigi{ 99187762Sluigi if (str != NULL) 100187762Sluigi printf("%s",str); 101187762Sluigi if (arg == IP_FW_TABLEARG) 102187762Sluigi printf("tablearg"); 103187762Sluigi else 104187762Sluigi printf("%u", arg); 105187762Sluigi} 106159636Soleg 10798943Sluigistatic struct _s_x f_tcpflags[] = { 10898943Sluigi { "syn", TH_SYN }, 10998943Sluigi { "fin", TH_FIN }, 11098943Sluigi { "ack", TH_ACK }, 11198943Sluigi { "psh", TH_PUSH }, 11298943Sluigi { "rst", TH_RST }, 11398943Sluigi { "urg", TH_URG }, 11498943Sluigi { "tcp flag", 0 }, 11598943Sluigi { NULL, 0 } 11698943Sluigi}; 11798943Sluigi 11898943Sluigistatic struct _s_x f_tcpopts[] = { 11998943Sluigi { "mss", IP_FW_TCPOPT_MSS }, 12098943Sluigi { "maxseg", IP_FW_TCPOPT_MSS }, 12198943Sluigi { "window", IP_FW_TCPOPT_WINDOW }, 12298943Sluigi { "sack", IP_FW_TCPOPT_SACK }, 12398943Sluigi { "ts", IP_FW_TCPOPT_TS }, 12498943Sluigi { "timestamp", IP_FW_TCPOPT_TS }, 12598943Sluigi { "cc", IP_FW_TCPOPT_CC }, 12698943Sluigi { "tcp option", 0 }, 12798943Sluigi { NULL, 0 } 12898943Sluigi}; 12998943Sluigi 13098943Sluigi/* 13198943Sluigi * IP options span the range 0 to 255 so we need to remap them 13298943Sluigi * (though in fact only the low 5 bits are significant). 13398943Sluigi */ 13498943Sluigistatic struct _s_x f_ipopts[] = { 13598943Sluigi { "ssrr", IP_FW_IPOPT_SSRR}, 13698943Sluigi { "lsrr", IP_FW_IPOPT_LSRR}, 13798943Sluigi { "rr", IP_FW_IPOPT_RR}, 13898943Sluigi { "ts", IP_FW_IPOPT_TS}, 13998943Sluigi { "ip option", 0 }, 14098943Sluigi { NULL, 0 } 14198943Sluigi}; 14298943Sluigi 14398943Sluigistatic struct _s_x f_iptos[] = { 14498943Sluigi { "lowdelay", IPTOS_LOWDELAY}, 14598943Sluigi { "throughput", IPTOS_THROUGHPUT}, 14698943Sluigi { "reliability", IPTOS_RELIABILITY}, 14798943Sluigi { "mincost", IPTOS_MINCOST}, 148172801Srpaulo { "congestion", IPTOS_ECN_CE}, 149172801Srpaulo { "ecntransport", IPTOS_ECN_ECT0}, 15098943Sluigi { "ip tos option", 0}, 15198943Sluigi { NULL, 0 } 15298943Sluigi}; 15398943Sluigi 15498943Sluigistatic struct _s_x limit_masks[] = { 15598943Sluigi {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT}, 15698943Sluigi {"src-addr", DYN_SRC_ADDR}, 15798943Sluigi {"src-port", DYN_SRC_PORT}, 15898943Sluigi {"dst-addr", DYN_DST_ADDR}, 15998943Sluigi {"dst-port", DYN_DST_PORT}, 16098943Sluigi {NULL, 0} 16198943Sluigi}; 16298943Sluigi 16398943Sluigi/* 16498943Sluigi * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines 16598943Sluigi * This is only used in this code. 16698943Sluigi */ 16798943Sluigi#define IPPROTO_ETHERTYPE 0x1000 16898943Sluigistatic struct _s_x ether_types[] = { 16998943Sluigi /* 17098943Sluigi * Note, we cannot use "-:&/" in the names because they are field 17198943Sluigi * separators in the type specifications. Also, we use s = NULL as 17298943Sluigi * end-delimiter, because a type of 0 can be legal. 17398943Sluigi */ 17498943Sluigi { "ip", 0x0800 }, 17598943Sluigi { "ipv4", 0x0800 }, 17698943Sluigi { "ipv6", 0x86dd }, 17798943Sluigi { "arp", 0x0806 }, 17898943Sluigi { "rarp", 0x8035 }, 17998943Sluigi { "vlan", 0x8100 }, 18098943Sluigi { "loop", 0x9000 }, 18198943Sluigi { "trail", 0x1000 }, 18298943Sluigi { "at", 0x809b }, 18398943Sluigi { "atalk", 0x809b }, 18498943Sluigi { "aarp", 0x80f3 }, 18598943Sluigi { "pppoe_disc", 0x8863 }, 18698943Sluigi { "pppoe_sess", 0x8864 }, 18798943Sluigi { "ipx_8022", 0x00E0 }, 18898943Sluigi { "ipx_8023", 0x0000 }, 18998943Sluigi { "ipx_ii", 0x8137 }, 19098943Sluigi { "ipx_snap", 0x8137 }, 19198943Sluigi { "ipx", 0x8137 }, 19298943Sluigi { "ns", 0x0600 }, 19398943Sluigi { NULL, 0 } 19498943Sluigi}; 19598943Sluigi 19698943Sluigi 197187769Sluigistatic struct _s_x rule_actions[] = { 19898943Sluigi { "accept", TOK_ACCEPT }, 19998943Sluigi { "pass", TOK_ACCEPT }, 20098943Sluigi { "allow", TOK_ACCEPT }, 20198943Sluigi { "permit", TOK_ACCEPT }, 20298943Sluigi { "count", TOK_COUNT }, 20398943Sluigi { "pipe", TOK_PIPE }, 20498943Sluigi { "queue", TOK_QUEUE }, 20598943Sluigi { "divert", TOK_DIVERT }, 20698943Sluigi { "tee", TOK_TEE }, 207141351Sglebius { "netgraph", TOK_NETGRAPH }, 208141351Sglebius { "ngtee", TOK_NGTEE }, 20998943Sluigi { "fwd", TOK_FORWARD }, 21098943Sluigi { "forward", TOK_FORWARD }, 21198943Sluigi { "skipto", TOK_SKIPTO }, 21298943Sluigi { "deny", TOK_DENY }, 21398943Sluigi { "drop", TOK_DENY }, 21498943Sluigi { "reject", TOK_REJECT }, 215149020Sbz { "reset6", TOK_RESET6 }, 21698943Sluigi { "reset", TOK_RESET }, 217149020Sbz { "unreach6", TOK_UNREACH6 }, 21899475Sluigi { "unreach", TOK_UNREACH }, 21998943Sluigi { "check-state", TOK_CHECKSTATE }, 220117469Sluigi { "//", TOK_COMMENT }, 221220802Sglebius { "nat", TOK_NAT }, 222190633Spiso { "reass", TOK_REASS }, 223178888Sjulian { "setfib", TOK_SETFIB }, 224223666Sae { "call", TOK_CALL }, 225223666Sae { "return", TOK_RETURN }, 226117328Sluigi { NULL, 0 } /* terminator */ 22798943Sluigi}; 22898943Sluigi 229187769Sluigistatic struct _s_x rule_action_params[] = { 230136071Sgreen { "altq", TOK_ALTQ }, 231136071Sgreen { "log", TOK_LOG }, 232158879Soleg { "tag", TOK_TAG }, 233158879Soleg { "untag", TOK_UNTAG }, 234136071Sgreen { NULL, 0 } /* terminator */ 235136071Sgreen}; 236136071Sgreen 237200567Sluigi/* 238200567Sluigi * The 'lookup' instruction accepts one of the following arguments. 239200567Sluigi * -1 is a terminator for the list. 240200567Sluigi * Arguments are passed as v[1] in O_DST_LOOKUP options. 241200567Sluigi */ 242200567Sluigistatic int lookup_key[] = { 243200567Sluigi TOK_DSTIP, TOK_SRCIP, TOK_DSTPORT, TOK_SRCPORT, 244205169Sluigi TOK_UID, TOK_JAIL, TOK_DSCP, -1 }; 245200567Sluigi 246187769Sluigistatic struct _s_x rule_options[] = { 247158879Soleg { "tagged", TOK_TAGGED }, 24898943Sluigi { "uid", TOK_UID }, 24998943Sluigi { "gid", TOK_GID }, 250133600Scsjp { "jail", TOK_JAIL }, 25198943Sluigi { "in", TOK_IN }, 25298943Sluigi { "limit", TOK_LIMIT }, 25398943Sluigi { "keep-state", TOK_KEEPSTATE }, 25498943Sluigi { "bridged", TOK_LAYER2 }, 25598943Sluigi { "layer2", TOK_LAYER2 }, 25698943Sluigi { "out", TOK_OUT }, 257136073Sgreen { "diverted", TOK_DIVERTED }, 258136073Sgreen { "diverted-loopback", TOK_DIVERTEDLOOPBACK }, 259136073Sgreen { "diverted-output", TOK_DIVERTEDOUTPUT }, 26098943Sluigi { "xmit", TOK_XMIT }, 26198943Sluigi { "recv", TOK_RECV }, 26298943Sluigi { "via", TOK_VIA }, 26398943Sluigi { "fragment", TOK_FRAG }, 26498943Sluigi { "frag", TOK_FRAG }, 265178888Sjulian { "fib", TOK_FIB }, 26698943Sluigi { "ipoptions", TOK_IPOPTS }, 26798943Sluigi { "ipopts", TOK_IPOPTS }, 26898943Sluigi { "iplen", TOK_IPLEN }, 26998943Sluigi { "ipid", TOK_IPID }, 27098943Sluigi { "ipprecedence", TOK_IPPRECEDENCE }, 271205169Sluigi { "dscp", TOK_DSCP }, 27298943Sluigi { "iptos", TOK_IPTOS }, 27398943Sluigi { "ipttl", TOK_IPTTL }, 27498943Sluigi { "ipversion", TOK_IPVER }, 27598943Sluigi { "ipver", TOK_IPVER }, 27698943Sluigi { "estab", TOK_ESTAB }, 27798943Sluigi { "established", TOK_ESTAB }, 27898943Sluigi { "setup", TOK_SETUP }, 279215179Sluigi { "sockarg", TOK_SOCKARG }, 280136075Sgreen { "tcpdatalen", TOK_TCPDATALEN }, 28198943Sluigi { "tcpflags", TOK_TCPFLAGS }, 28298943Sluigi { "tcpflgs", TOK_TCPFLAGS }, 28398943Sluigi { "tcpoptions", TOK_TCPOPTS }, 28498943Sluigi { "tcpopts", TOK_TCPOPTS }, 28598943Sluigi { "tcpseq", TOK_TCPSEQ }, 28698943Sluigi { "tcpack", TOK_TCPACK }, 28798943Sluigi { "tcpwin", TOK_TCPWIN }, 28899909Sluigi { "icmptype", TOK_ICMPTYPES }, 28998943Sluigi { "icmptypes", TOK_ICMPTYPES }, 290102087Sluigi { "dst-ip", TOK_DSTIP }, 291102087Sluigi { "src-ip", TOK_SRCIP }, 292102087Sluigi { "dst-port", TOK_DSTPORT }, 293102087Sluigi { "src-port", TOK_SRCPORT }, 294102087Sluigi { "proto", TOK_PROTO }, 295102087Sluigi { "MAC", TOK_MAC }, 296102087Sluigi { "mac", TOK_MAC }, 297102087Sluigi { "mac-type", TOK_MACTYPE }, 298112250Scjc { "verrevpath", TOK_VERREVPATH }, 299128575Sandre { "versrcreach", TOK_VERSRCREACH }, 300133387Sandre { "antispoof", TOK_ANTISPOOF }, 301117241Sluigi { "ipsec", TOK_IPSEC }, 302145246Sbrooks { "icmp6type", TOK_ICMP6TYPES }, 303145246Sbrooks { "icmp6types", TOK_ICMP6TYPES }, 304145246Sbrooks { "ext6hdr", TOK_EXT6HDR}, 305145246Sbrooks { "flow-id", TOK_FLOWID}, 306145246Sbrooks { "ipv6", TOK_IPV6}, 307145246Sbrooks { "ip6", TOK_IPV6}, 308146894Smlaier { "ipv4", TOK_IPV4}, 309146894Smlaier { "ip4", TOK_IPV4}, 310145246Sbrooks { "dst-ipv6", TOK_DSTIP6}, 311145246Sbrooks { "dst-ip6", TOK_DSTIP6}, 312145246Sbrooks { "src-ipv6", TOK_SRCIP6}, 313145246Sbrooks { "src-ip6", TOK_SRCIP6}, 314200567Sluigi { "lookup", TOK_LOOKUP}, 315117469Sluigi { "//", TOK_COMMENT }, 31698943Sluigi 31798943Sluigi { "not", TOK_NOT }, /* pseudo option */ 31898943Sluigi { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ 31998943Sluigi { "or", TOK_OR }, /* pseudo option */ 32098943Sluigi { "|", /* escape */ TOK_OR }, /* pseudo option */ 321101641Sluigi { "{", TOK_STARTBRACE }, /* pseudo option */ 322101641Sluigi { "(", TOK_STARTBRACE }, /* pseudo option */ 323101641Sluigi { "}", TOK_ENDBRACE }, /* pseudo option */ 324101641Sluigi { ")", TOK_ENDBRACE }, /* pseudo option */ 325117328Sluigi { NULL, 0 } /* terminator */ 32698943Sluigi}; 32798943Sluigi 328206843Sluigi/* 329206843Sluigi * Helper routine to print a possibly unaligned uint64_t on 330206843Sluigi * various platform. If width > 0, print the value with 331206843Sluigi * the desired width, followed by a space; 332206843Sluigi * otherwise, return the required width. 333187787Sluigi */ 334206843Sluigiint 335206843Sluigipr_u64(uint64_t *pd, int width) 336187787Sluigi{ 337206843Sluigi#ifdef TCC 338206843Sluigi#define U64_FMT "I64" 339206843Sluigi#else 340206843Sluigi#define U64_FMT "llu" 341206843Sluigi#endif 342206846Sluigi uint64_t u; 343206846Sluigi unsigned long long d; 344115793Sticso 345206846Sluigi bcopy (pd, &u, sizeof(u)); 346206846Sluigi d = u; 347206843Sluigi return (width > 0) ? 348206843Sluigi printf("%*" U64_FMT " ", width, d) : 349206843Sluigi snprintf(NULL, 0, "%" U64_FMT, d) ; 350206843Sluigi#undef U64_FMT 351129389Sstefanf} 352115793Sticso 353187767Sluigivoid * 354187716Sluigisafe_calloc(size_t number, size_t size) 355187716Sluigi{ 356187716Sluigi void *ret = calloc(number, size); 357187716Sluigi 358187716Sluigi if (ret == NULL) 359187716Sluigi err(EX_OSERR, "calloc"); 360187716Sluigi return ret; 361187716Sluigi} 362187716Sluigi 363187767Sluigivoid * 364187716Sluigisafe_realloc(void *ptr, size_t size) 365187716Sluigi{ 366187716Sluigi void *ret = realloc(ptr, size); 367187716Sluigi 368187716Sluigi if (ret == NULL) 369187716Sluigi err(EX_OSERR, "realloc"); 370187716Sluigi return ret; 371187716Sluigi} 372187716Sluigi 373117328Sluigi/* 374117328Sluigi * conditionally runs the command. 375204591Sluigi * Selected options or negative -> getsockopt 376117328Sluigi */ 377187769Sluigiint 378119740Stmmdo_cmd(int optname, void *optval, uintptr_t optlen) 379117328Sluigi{ 380117328Sluigi int i; 381117577Sluigi 382187764Sluigi if (co.test_only) 383117328Sluigi return 0; 384117328Sluigi 385234597Smelifaro if (ipfw_socket == -1) 386234597Smelifaro ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 387234597Smelifaro if (ipfw_socket < 0) 388117328Sluigi err(EX_UNAVAILABLE, "socket"); 389117328Sluigi 390117328Sluigi if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET || 391234597Smelifaro optname == IP_FW_ADD || optname == IP_FW3 || 392220802Sglebius optname == IP_FW_NAT_GET_CONFIG || 393204591Sluigi optname < 0 || 394204591Sluigi optname == IP_FW_NAT_GET_LOG) { 395204591Sluigi if (optname < 0) 396204591Sluigi optname = -optname; 397234597Smelifaro i = getsockopt(ipfw_socket, IPPROTO_IP, optname, optval, 398117328Sluigi (socklen_t *)optlen); 399204591Sluigi } else { 400234597Smelifaro i = setsockopt(ipfw_socket, IPPROTO_IP, optname, optval, optlen); 401204591Sluigi } 402117328Sluigi return i; 403117328Sluigi} 404117328Sluigi 405234597Smelifaro/* 406234597Smelifaro * do_setcmd3 - pass ipfw control cmd to kernel 407234597Smelifaro * @optname: option name 408234597Smelifaro * @optval: pointer to option data 409234597Smelifaro * @optlen: option length 410234597Smelifaro * 411234597Smelifaro * Function encapsulates option value in IP_FW3 socket option 412234597Smelifaro * and calls setsockopt(). 413234597Smelifaro * Function returns 0 on success or -1 otherwise. 414234597Smelifaro */ 415234597Smelifaroint 416234597Smelifarodo_setcmd3(int optname, void *optval, socklen_t optlen) 417234597Smelifaro{ 418234597Smelifaro socklen_t len; 419234597Smelifaro ip_fw3_opheader *op3; 420234597Smelifaro 421234597Smelifaro if (co.test_only) 422234597Smelifaro return (0); 423234597Smelifaro 424234597Smelifaro if (ipfw_socket == -1) 425234597Smelifaro ipfw_socket = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 426234597Smelifaro if (ipfw_socket < 0) 427234597Smelifaro err(EX_UNAVAILABLE, "socket"); 428234597Smelifaro 429234597Smelifaro len = sizeof(ip_fw3_opheader) + optlen; 430234597Smelifaro op3 = alloca(len); 431234597Smelifaro /* Zero reserved fields */ 432234597Smelifaro memset(op3, 0, sizeof(ip_fw3_opheader)); 433234597Smelifaro memcpy(op3 + 1, optval, optlen); 434234597Smelifaro op3->opcode = optname; 435234597Smelifaro 436234597Smelifaro return setsockopt(ipfw_socket, IPPROTO_IP, IP_FW3, op3, len); 437234597Smelifaro} 438234597Smelifaro 43998943Sluigi/** 44098943Sluigi * match_token takes a table and a string, returns the value associated 441117328Sluigi * with the string (-1 in case of failure). 44298943Sluigi */ 443187769Sluigiint 44498943Sluigimatch_token(struct _s_x *table, char *string) 44598943Sluigi{ 44698943Sluigi struct _s_x *pt; 447117469Sluigi uint i = strlen(string); 44898943Sluigi 44998943Sluigi for (pt = table ; i && pt->s != NULL ; pt++) 45098943Sluigi if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) 45198943Sluigi return pt->x; 45298943Sluigi return -1; 453129389Sstefanf} 45498943Sluigi 455117328Sluigi/** 456117328Sluigi * match_value takes a table and a value, returns the string associated 457117328Sluigi * with the value (NULL in case of failure). 458117328Sluigi */ 459187770Sluigichar const * 460117469Sluigimatch_value(struct _s_x *p, int value) 46198943Sluigi{ 46298943Sluigi for (; p->s != NULL; p++) 46398943Sluigi if (p->x == value) 46498943Sluigi return p->s; 46598943Sluigi return NULL; 46698943Sluigi} 46798943Sluigi 46898943Sluigi/* 469140271Sbrooks * _substrcmp takes two strings and returns 1 if they do not match, 470140271Sbrooks * and 0 if they match exactly or the first string is a sub-string 471140271Sbrooks * of the second. A warning is printed to stderr in the case that the 472140271Sbrooks * first string is a sub-string of the second. 473140271Sbrooks * 474140271Sbrooks * This function will be removed in the future through the usual 475140271Sbrooks * deprecation process. 476140271Sbrooks */ 477187767Sluigiint 478140271Sbrooks_substrcmp(const char *str1, const char* str2) 479140271Sbrooks{ 480220804Sglebius 481140271Sbrooks if (strncmp(str1, str2, strlen(str1)) != 0) 482140271Sbrooks return 1; 483140271Sbrooks 484140271Sbrooks if (strlen(str1) != strlen(str2)) 485140271Sbrooks warnx("DEPRECATED: '%s' matched '%s' as a sub-string", 486140271Sbrooks str1, str2); 487140271Sbrooks return 0; 488140271Sbrooks} 489140271Sbrooks 490140271Sbrooks/* 491140271Sbrooks * _substrcmp2 takes three strings and returns 1 if the first two do not match, 492140271Sbrooks * and 0 if they match exactly or the second string is a sub-string 493140271Sbrooks * of the first. A warning is printed to stderr in the case that the 494140271Sbrooks * first string does not match the third. 495140271Sbrooks * 496140271Sbrooks * This function exists to warn about the bizzare construction 497140271Sbrooks * strncmp(str, "by", 2) which is used to allow people to use a shotcut 498140271Sbrooks * for "bytes". The problem is that in addition to accepting "by", 499140271Sbrooks * "byt", "byte", and "bytes", it also excepts "by_rabid_dogs" and any 500140271Sbrooks * other string beginning with "by". 501140271Sbrooks * 502140271Sbrooks * This function will be removed in the future through the usual 503140271Sbrooks * deprecation process. 504140271Sbrooks */ 505187769Sluigiint 506140271Sbrooks_substrcmp2(const char *str1, const char* str2, const char* str3) 507140271Sbrooks{ 508220804Sglebius 509140271Sbrooks if (strncmp(str1, str2, strlen(str2)) != 0) 510140271Sbrooks return 1; 511140271Sbrooks 512140271Sbrooks if (strcmp(str1, str3) != 0) 513140271Sbrooks warnx("DEPRECATED: '%s' matched '%s'", 514140271Sbrooks str1, str3); 515140271Sbrooks return 0; 516140271Sbrooks} 517140271Sbrooks 518140271Sbrooks/* 51998943Sluigi * prints one port, symbolic or numeric 52098943Sluigi */ 52198943Sluigistatic void 522117328Sluigiprint_port(int proto, uint16_t port) 52398943Sluigi{ 52498943Sluigi 52598943Sluigi if (proto == IPPROTO_ETHERTYPE) { 526117469Sluigi char const *s; 52798943Sluigi 528187764Sluigi if (co.do_resolv && (s = match_value(ether_types, port)) ) 52998943Sluigi printf("%s", s); 53098943Sluigi else 53198943Sluigi printf("0x%04x", port); 53298943Sluigi } else { 53398943Sluigi struct servent *se = NULL; 534187764Sluigi if (co.do_resolv) { 53598943Sluigi struct protoent *pe = getprotobynumber(proto); 53698943Sluigi 53798943Sluigi se = getservbyport(htons(port), pe ? pe->p_name : NULL); 53898943Sluigi } 53998943Sluigi if (se) 54098943Sluigi printf("%s", se->s_name); 54198943Sluigi else 54298943Sluigi printf("%d", port); 54398943Sluigi } 54498943Sluigi} 54598943Sluigi 546187769Sluigistatic struct _s_x _port_name[] = { 547117328Sluigi {"dst-port", O_IP_DSTPORT}, 548117328Sluigi {"src-port", O_IP_SRCPORT}, 549117328Sluigi {"ipid", O_IPID}, 550117328Sluigi {"iplen", O_IPLEN}, 551117328Sluigi {"ipttl", O_IPTTL}, 552117328Sluigi {"mac-type", O_MAC_TYPE}, 553136075Sgreen {"tcpdatalen", O_TCPDATALEN}, 554234278Sglebius {"tcpwin", O_TCPWIN}, 555158879Soleg {"tagged", O_TAGGED}, 556117328Sluigi {NULL, 0} 557117328Sluigi}; 558117328Sluigi 55998943Sluigi/* 560117328Sluigi * Print the values in a list 16-bit items of the types above. 56198943Sluigi * XXX todo: add support for mask. 56298943Sluigi */ 56398943Sluigistatic void 564102087Sluigiprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode) 56598943Sluigi{ 566117328Sluigi uint16_t *p = cmd->ports; 56798943Sluigi int i; 568117469Sluigi char const *sep; 56998943Sluigi 570116690Sluigi if (opcode != 0) { 571117328Sluigi sep = match_value(_port_name, opcode); 572117328Sluigi if (sep == NULL) 573116690Sluigi sep = "???"; 574116690Sluigi printf (" %s", sep); 575116690Sluigi } 576116690Sluigi sep = " "; 57798943Sluigi for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { 578193702Sluigi printf("%s", sep); 57998943Sluigi print_port(proto, p[0]); 58098943Sluigi if (p[0] != p[1]) { 58198943Sluigi printf("-"); 58298943Sluigi print_port(proto, p[1]); 58398943Sluigi } 58498943Sluigi sep = ","; 58598943Sluigi } 58698943Sluigi} 58798943Sluigi 58898943Sluigi/* 58998943Sluigi * Like strtol, but also translates service names into port numbers 59098943Sluigi * for some protocols. 59198943Sluigi * In particular: 59298943Sluigi * proto == -1 disables the protocol check; 59398943Sluigi * proto == IPPROTO_ETHERTYPE looks up an internal table 59498943Sluigi * proto == <some value in /etc/protocols> matches the values there. 595101628Sluigi * Returns *end == s in case the parameter is not found. 59698943Sluigi */ 59798943Sluigistatic int 59898943Sluigistrtoport(char *s, char **end, int base, int proto) 59998943Sluigi{ 600101628Sluigi char *p, *buf; 601101628Sluigi char *s1; 60298943Sluigi int i; 60398943Sluigi 604101628Sluigi *end = s; /* default - not found */ 605117577Sluigi if (*s == '\0') 606101628Sluigi return 0; /* not found */ 607106505Smaxim 60898943Sluigi if (isdigit(*s)) 60998943Sluigi return strtol(s, end, base); 61098943Sluigi 61198943Sluigi /* 612101628Sluigi * find separator. '\\' escapes the next char. 61398943Sluigi */ 614101628Sluigi for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++) 615101628Sluigi if (*s1 == '\\' && s1[1] != '\0') 616101628Sluigi s1++; 61798943Sluigi 618187716Sluigi buf = safe_calloc(s1 - s + 1, 1); 619101628Sluigi 620101628Sluigi /* 621101628Sluigi * copy into a buffer skipping backslashes 622101628Sluigi */ 623101628Sluigi for (p = s, i = 0; p != s1 ; p++) 624117577Sluigi if (*p != '\\') 625101628Sluigi buf[i++] = *p; 626101628Sluigi buf[i++] = '\0'; 627101628Sluigi 62898943Sluigi if (proto == IPPROTO_ETHERTYPE) { 629101628Sluigi i = match_token(ether_types, buf); 630101628Sluigi free(buf); 631101628Sluigi if (i != -1) { /* found */ 63298943Sluigi *end = s1; 63398943Sluigi return i; 63498943Sluigi } 63598943Sluigi } else { 63698943Sluigi struct protoent *pe = NULL; 63798943Sluigi struct servent *se; 63898943Sluigi 63998943Sluigi if (proto != 0) 64098943Sluigi pe = getprotobynumber(proto); 64198943Sluigi setservent(1); 642101628Sluigi se = getservbyname(buf, pe ? pe->p_name : NULL); 643101628Sluigi free(buf); 64498943Sluigi if (se != NULL) { 64598943Sluigi *end = s1; 64698943Sluigi return ntohs(se->s_port); 64798943Sluigi } 64898943Sluigi } 649101628Sluigi return 0; /* not found */ 65098943Sluigi} 65198943Sluigi 65298943Sluigi/* 653117328Sluigi * Fill the body of the command with the list of port ranges. 65498943Sluigi */ 65598943Sluigistatic int 65698943Sluigifill_newports(ipfw_insn_u16 *cmd, char *av, int proto) 65798943Sluigi{ 658117328Sluigi uint16_t a, b, *p = cmd->ports; 65998943Sluigi int i = 0; 660102087Sluigi char *s = av; 66198943Sluigi 662102087Sluigi while (*s) { 66398943Sluigi a = strtoport(av, &s, 0, proto); 664159636Soleg if (s == av) /* empty or invalid argument */ 665159636Soleg return (0); 666159636Soleg 667159636Soleg switch (*s) { 668159636Soleg case '-': /* a range */ 669159636Soleg av = s + 1; 67098943Sluigi b = strtoport(av, &s, 0, proto); 671159636Soleg /* Reject expressions like '1-abc' or '1-2-3'. */ 672159636Soleg if (s == av || (*s != ',' && *s != '\0')) 673159636Soleg return (0); 67498943Sluigi p[0] = a; 67598943Sluigi p[1] = b; 676159636Soleg break; 677159636Soleg case ',': /* comma separated list */ 678159636Soleg case '\0': 67998943Sluigi p[0] = p[1] = a; 680159636Soleg break; 681159636Soleg default: 682159636Soleg warnx("port list: invalid separator <%c> in <%s>", 683101978Sluigi *s, av); 684159636Soleg return (0); 685159636Soleg } 686159636Soleg 687102087Sluigi i++; 688102087Sluigi p += 2; 689159636Soleg av = s + 1; 69098943Sluigi } 69198943Sluigi if (i > 0) { 692159636Soleg if (i + 1 > F_LEN_MASK) 693102087Sluigi errx(EX_DATAERR, "too many ports/ranges\n"); 694159636Soleg cmd->o.len |= i + 1; /* leave F_NOT and F_OR untouched */ 69598943Sluigi } 696159636Soleg return (i); 69798943Sluigi} 69898943Sluigi 69998943Sluigistatic struct _s_x icmpcodes[] = { 70098943Sluigi { "net", ICMP_UNREACH_NET }, 70198943Sluigi { "host", ICMP_UNREACH_HOST }, 70298943Sluigi { "protocol", ICMP_UNREACH_PROTOCOL }, 70398943Sluigi { "port", ICMP_UNREACH_PORT }, 70498943Sluigi { "needfrag", ICMP_UNREACH_NEEDFRAG }, 70598943Sluigi { "srcfail", ICMP_UNREACH_SRCFAIL }, 70698943Sluigi { "net-unknown", ICMP_UNREACH_NET_UNKNOWN }, 70798943Sluigi { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN }, 70898943Sluigi { "isolated", ICMP_UNREACH_ISOLATED }, 70998943Sluigi { "net-prohib", ICMP_UNREACH_NET_PROHIB }, 71098943Sluigi { "host-prohib", ICMP_UNREACH_HOST_PROHIB }, 71198943Sluigi { "tosnet", ICMP_UNREACH_TOSNET }, 71298943Sluigi { "toshost", ICMP_UNREACH_TOSHOST }, 71398943Sluigi { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB }, 71498943Sluigi { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE }, 71598943Sluigi { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF }, 71698943Sluigi { NULL, 0 } 71798943Sluigi}; 71898943Sluigi 71998943Sluigistatic void 72098943Sluigifill_reject_code(u_short *codep, char *str) 72198943Sluigi{ 72298943Sluigi int val; 72398943Sluigi char *s; 72498943Sluigi 72598943Sluigi val = strtoul(str, &s, 0); 72698943Sluigi if (s == str || *s != '\0' || val >= 0x100) 72798943Sluigi val = match_token(icmpcodes, str); 728102087Sluigi if (val < 0) 72998943Sluigi errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); 73098943Sluigi *codep = val; 73198943Sluigi return; 73298943Sluigi} 73398943Sluigi 73498943Sluigistatic void 735117328Sluigiprint_reject_code(uint16_t code) 73698943Sluigi{ 737117469Sluigi char const *s = match_value(icmpcodes, code); 73898943Sluigi 73998943Sluigi if (s != NULL) 74099475Sluigi printf("unreach %s", s); 74198943Sluigi else 74299475Sluigi printf("unreach %u", code); 74398943Sluigi} 74498943Sluigi 74598943Sluigi/* 74698943Sluigi * Returns the number of bits set (from left) in a contiguous bitmask, 74798943Sluigi * or -1 if the mask is not contiguous. 74898943Sluigi * XXX this needs a proper fix. 74998943Sluigi * This effectively works on masks in big-endian (network) format. 75098943Sluigi * when compiled on little endian architectures. 75198943Sluigi * 75298943Sluigi * First bit is bit 7 of the first byte -- note, for MAC addresses, 75398943Sluigi * the first bit on the wire is bit 0 of the first byte. 75498943Sluigi * len is the max length in bits. 75598943Sluigi */ 756187770Sluigiint 757117577Sluigicontigmask(uint8_t *p, int len) 75898943Sluigi{ 75998943Sluigi int i, n; 760117577Sluigi 76198943Sluigi for (i=0; i<len ; i++) 76298943Sluigi if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */ 76398943Sluigi break; 76498943Sluigi for (n=i+1; n < len; n++) 76598943Sluigi if ( (p[n/8] & (1 << (7 - (n%8)))) != 0) 76698943Sluigi return -1; /* mask not contiguous */ 76798943Sluigi return i; 76898943Sluigi} 76998943Sluigi 77098943Sluigi/* 77198943Sluigi * print flags set/clear in the two bitmasks passed as parameters. 77298943Sluigi * There is a specialized check for f_tcpflags. 77398943Sluigi */ 77498943Sluigistatic void 775117469Sluigiprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list) 77698943Sluigi{ 777117469Sluigi char const *comma = ""; 77898943Sluigi int i; 779117577Sluigi uint8_t set = cmd->arg1 & 0xff; 780117577Sluigi uint8_t clear = (cmd->arg1 >> 8) & 0xff; 78198943Sluigi 78298943Sluigi if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { 78398943Sluigi printf(" setup"); 78498943Sluigi return; 78598943Sluigi } 78698943Sluigi 78798943Sluigi printf(" %s ", name); 78898943Sluigi for (i=0; list[i].x != 0; i++) { 78998943Sluigi if (set & list[i].x) { 79098943Sluigi set &= ~list[i].x; 79198943Sluigi printf("%s%s", comma, list[i].s); 79298943Sluigi comma = ","; 79398943Sluigi } 79498943Sluigi if (clear & list[i].x) { 79598943Sluigi clear &= ~list[i].x; 79698943Sluigi printf("%s!%s", comma, list[i].s); 79798943Sluigi comma = ","; 79898943Sluigi } 79998943Sluigi } 80098943Sluigi} 80198943Sluigi 80298943Sluigi/* 80398943Sluigi * Print the ip address contained in a command. 80498943Sluigi */ 80598943Sluigistatic void 806117469Sluigiprint_ip(ipfw_insn_ip *cmd, char const *s) 80798943Sluigi{ 80898943Sluigi struct hostent *he = NULL; 809204591Sluigi uint32_t len = F_LEN((ipfw_insn *)cmd); 810117328Sluigi uint32_t *a = ((ipfw_insn_u32 *)cmd)->d; 81198943Sluigi 812200567Sluigi if (cmd->o.opcode == O_IP_DST_LOOKUP && len > F_INSN_SIZE(ipfw_insn_u32)) { 813200567Sluigi uint32_t d = a[1]; 814200567Sluigi const char *arg = "<invalid>"; 815200567Sluigi 816200567Sluigi if (d < sizeof(lookup_key)/sizeof(lookup_key[0])) 817200567Sluigi arg = match_value(rule_options, lookup_key[d]); 818200567Sluigi printf("%s lookup %s %d", cmd->o.len & F_NOT ? " not": "", 819200567Sluigi arg, cmd->o.arg1); 820200567Sluigi return; 821200567Sluigi } 822102087Sluigi printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); 82398943Sluigi 82498943Sluigi if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { 82598943Sluigi printf("me"); 82698943Sluigi return; 82798943Sluigi } 828130281Sru if (cmd->o.opcode == O_IP_SRC_LOOKUP || 829130281Sru cmd->o.opcode == O_IP_DST_LOOKUP) { 830130281Sru printf("table(%u", ((ipfw_insn *)cmd)->arg1); 831130281Sru if (len == F_INSN_SIZE(ipfw_insn_u32)) 832130281Sru printf(",%u", *a); 833130281Sru printf(")"); 834130281Sru return; 835130281Sru } 83698943Sluigi if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { 837117328Sluigi uint32_t x, *map = (uint32_t *)&(cmd->mask); 838116716Sluigi int i, j; 83998943Sluigi char comma = '{'; 84098943Sluigi 84198943Sluigi x = cmd->o.arg1 - 1; 84298943Sluigi x = htonl( ~x ); 84398943Sluigi cmd->addr.s_addr = htonl(cmd->addr.s_addr); 84498943Sluigi printf("%s/%d", inet_ntoa(cmd->addr), 845117577Sluigi contigmask((uint8_t *)&x, 32)); 84698943Sluigi x = cmd->addr.s_addr = htonl(cmd->addr.s_addr); 84798943Sluigi x &= 0xff; /* base */ 848116716Sluigi /* 849116716Sluigi * Print bits and ranges. 850116716Sluigi * Locate first bit set (i), then locate first bit unset (j). 851116716Sluigi * If we have 3+ consecutive bits set, then print them as a 852116716Sluigi * range, otherwise only print the initial bit and rescan. 853116716Sluigi */ 85498943Sluigi for (i=0; i < cmd->o.arg1; i++) 855117328Sluigi if (map[i/32] & (1<<(i & 31))) { 856116716Sluigi for (j=i+1; j < cmd->o.arg1; j++) 857117328Sluigi if (!(map[ j/32] & (1<<(j & 31)))) 858116716Sluigi break; 85998943Sluigi printf("%c%d", comma, i+x); 860116716Sluigi if (j>i+2) { /* range has at least 3 elements */ 861116716Sluigi printf("-%d", j-1+x); 862116716Sluigi i = j-1; 863116716Sluigi } 86498943Sluigi comma = ','; 86598943Sluigi } 86698943Sluigi printf("}"); 86798943Sluigi return; 86898943Sluigi } 869117328Sluigi /* 870117328Sluigi * len == 2 indicates a single IP, whereas lists of 1 or more 871117328Sluigi * addr/mask pairs have len = (2n+1). We convert len to n so we 872117328Sluigi * use that to count the number of entries. 873117328Sluigi */ 874117328Sluigi for (len = len / 2; len > 0; len--, a += 2) { 875117328Sluigi int mb = /* mask length */ 876117328Sluigi (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ? 877117577Sluigi 32 : contigmask((uint8_t *)&(a[1]), 32); 878187764Sluigi if (mb == 32 && co.do_resolv) 879117328Sluigi he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET); 88098943Sluigi if (he != NULL) /* resolved to name */ 88198943Sluigi printf("%s", he->h_name); 88298943Sluigi else if (mb == 0) /* any */ 88398943Sluigi printf("any"); 88498943Sluigi else { /* numeric IP followed by some kind of mask */ 885117328Sluigi printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) ); 88698943Sluigi if (mb < 0) 887117328Sluigi printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) ); 88898943Sluigi else if (mb < 32) 88998943Sluigi printf("/%d", mb); 89098943Sluigi } 891117328Sluigi if (len > 1) 892117328Sluigi printf(","); 893117328Sluigi } 89498943Sluigi} 89598943Sluigi 89698943Sluigi/* 89798943Sluigi * prints a MAC address/mask pair 89898943Sluigi */ 89998943Sluigistatic void 900117577Sluigiprint_mac(uint8_t *addr, uint8_t *mask) 90198943Sluigi{ 90298943Sluigi int l = contigmask(mask, 48); 90398943Sluigi 90498943Sluigi if (l == 0) 90598943Sluigi printf(" any"); 90698943Sluigi else { 90798943Sluigi printf(" %02x:%02x:%02x:%02x:%02x:%02x", 90898943Sluigi addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 90998943Sluigi if (l == -1) 91098943Sluigi printf("&%02x:%02x:%02x:%02x:%02x:%02x", 91198943Sluigi mask[0], mask[1], mask[2], 91298943Sluigi mask[3], mask[4], mask[5]); 91398943Sluigi else if (l < 48) 91498943Sluigi printf("/%d", l); 91598943Sluigi } 91698943Sluigi} 91798943Sluigi 91899475Sluigistatic void 91999475Sluigifill_icmptypes(ipfw_insn_u32 *cmd, char *av) 92099475Sluigi{ 921117328Sluigi uint8_t type; 92298943Sluigi 92399475Sluigi cmd->d[0] = 0; 92499475Sluigi while (*av) { 92599475Sluigi if (*av == ',') 92699475Sluigi av++; 92799475Sluigi 92899475Sluigi type = strtoul(av, &av, 0); 92999475Sluigi 93099475Sluigi if (*av != ',' && *av != '\0') 93199475Sluigi errx(EX_DATAERR, "invalid ICMP type"); 93299475Sluigi 93399475Sluigi if (type > 31) 93499475Sluigi errx(EX_DATAERR, "ICMP type out of range"); 93599475Sluigi 93699475Sluigi cmd->d[0] |= 1 << type; 93799475Sluigi } 93899475Sluigi cmd->o.opcode = O_ICMPTYPE; 93999475Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 94099475Sluigi} 94199475Sluigi 94299475Sluigistatic void 94399475Sluigiprint_icmptypes(ipfw_insn_u32 *cmd) 94499475Sluigi{ 94599475Sluigi int i; 94699475Sluigi char sep= ' '; 94799475Sluigi 94899475Sluigi printf(" icmptypes"); 94999475Sluigi for (i = 0; i < 32; i++) { 95099475Sluigi if ( (cmd->d[0] & (1 << (i))) == 0) 95199475Sluigi continue; 95299475Sluigi printf("%c%d", sep, i); 95399475Sluigi sep = ','; 95499475Sluigi } 95599475Sluigi} 95699475Sluigi 95798943Sluigi/* 95898943Sluigi * show_ipfw() prints the body of an ipfw rule. 95998943Sluigi * Because the standard rule has at least proto src_ip dst_ip, we use 96098943Sluigi * a helper function to produce these entries if not provided explicitly. 961102087Sluigi * The first argument is the list of fields we have, the second is 962102087Sluigi * the list of fields we want to be printed. 963101978Sluigi * 964102087Sluigi * Special cases if we have provided a MAC header: 965102087Sluigi * + if the rule does not contain IP addresses/ports, do not print them; 966102087Sluigi * + if the rule does not contain an IP proto, print "all" instead of "ip"; 967102087Sluigi * 968102087Sluigi * Once we have 'have_options', IP header fields are printed as options. 96998943Sluigi */ 970101978Sluigi#define HAVE_PROTO 0x0001 971101978Sluigi#define HAVE_SRCIP 0x0002 972101978Sluigi#define HAVE_DSTIP 0x0004 973169139Smaxim#define HAVE_PROTO4 0x0008 974169139Smaxim#define HAVE_PROTO6 0x0010 975205179Sluigi#define HAVE_IP 0x0100 976102087Sluigi#define HAVE_OPTIONS 0x8000 97798943Sluigi 97898943Sluigistatic void 979187477Sluigishow_prerequisites(int *flags, int want, int cmd __unused) 98098943Sluigi{ 981187764Sluigi if (co.comment_only) 982123495Sluigi return; 983102087Sluigi if ( (*flags & HAVE_IP) == HAVE_IP) 984102087Sluigi *flags |= HAVE_OPTIONS; 985102087Sluigi 986102087Sluigi if ( !(*flags & HAVE_OPTIONS)) { 987187477Sluigi if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) { 988146894Smlaier if ( (*flags & HAVE_PROTO4)) 989146894Smlaier printf(" ip4"); 990146894Smlaier else if ( (*flags & HAVE_PROTO6)) 991146894Smlaier printf(" ip6"); 992146894Smlaier else 993146894Smlaier printf(" ip"); 994187477Sluigi } 995102087Sluigi if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) 996102087Sluigi printf(" from any"); 997102087Sluigi if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP)) 998102087Sluigi printf(" to any"); 999102087Sluigi } 100098943Sluigi *flags |= want; 100198943Sluigi} 100298943Sluigi 100398943Sluigistatic void 1004112189Smaximshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) 100598943Sluigi{ 1006107291Skeramida static int twidth = 0; 100798943Sluigi int l; 1008158879Soleg ipfw_insn *cmd, *tagptr = NULL; 1009187477Sluigi const char *comment = NULL; /* ptr to comment if we have one */ 101098943Sluigi int proto = 0; /* default */ 101198943Sluigi int flags = 0; /* prerequisites */ 101298943Sluigi ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */ 1013136071Sgreen ipfw_insn_altq *altqptr = NULL; /* set if we find an O_ALTQ */ 101498943Sluigi int or_block = 0; /* we are in an or block */ 1015117328Sluigi uint32_t set_disable; 101698943Sluigi 1017115793Sticso bcopy(&rule->next_rule, &set_disable, sizeof(set_disable)); 1018101628Sluigi 1019101628Sluigi if (set_disable & (1 << rule->set)) { /* disabled */ 1020187764Sluigi if (!co.show_sets) 1021101628Sluigi return; 1022101628Sluigi else 1023101628Sluigi printf("# DISABLED "); 1024101628Sluigi } 102598943Sluigi printf("%05u ", rule->rulenum); 102698943Sluigi 1027206843Sluigi if (pcwidth > 0 || bcwidth > 0) { 1028206843Sluigi pr_u64(&rule->pcnt, pcwidth); 1029206843Sluigi pr_u64(&rule->bcnt, bcwidth); 1030206843Sluigi } 103198943Sluigi 1032187764Sluigi if (co.do_time == 2) 1033117472Sluigi printf("%10u ", rule->timestamp); 1034187764Sluigi else if (co.do_time == 1) { 1035107291Skeramida char timestr[30]; 1036107291Skeramida time_t t = (time_t)0; 1037107291Skeramida 1038107291Skeramida if (twidth == 0) { 1039107291Skeramida strcpy(timestr, ctime(&t)); 1040107291Skeramida *strchr(timestr, '\n') = '\0'; 1041107291Skeramida twidth = strlen(timestr); 1042107291Skeramida } 104398943Sluigi if (rule->timestamp) { 1044107291Skeramida t = _long_to_time(rule->timestamp); 104598943Sluigi 104698943Sluigi strcpy(timestr, ctime(&t)); 104798943Sluigi *strchr(timestr, '\n') = '\0'; 104898943Sluigi printf("%s ", timestr); 104998943Sluigi } else { 1050107291Skeramida printf("%*s", twidth, " "); 105198943Sluigi } 105298943Sluigi } 105398943Sluigi 1054187764Sluigi if (co.show_sets) 1055101628Sluigi printf("set %d ", rule->set); 1056101628Sluigi 105798943Sluigi /* 1058107289Sluigi * print the optional "match probability" 1059107289Sluigi */ 1060107289Sluigi if (rule->cmd_len > 0) { 1061107289Sluigi cmd = rule->cmd ; 1062107289Sluigi if (cmd->opcode == O_PROB) { 1063107289Sluigi ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd; 1064107289Sluigi double d = 1.0 * p->d[0]; 1065107289Sluigi 1066107289Sluigi d = (d / 0x7fffffff); 1067107289Sluigi printf("prob %f ", d); 1068107289Sluigi } 1069107289Sluigi } 1070107289Sluigi 1071107289Sluigi /* 107298943Sluigi * first print actions 107398943Sluigi */ 1074220802Sglebius for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule); 107598943Sluigi l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 107698943Sluigi switch(cmd->opcode) { 107798943Sluigi case O_CHECK_STATE: 107898943Sluigi printf("check-state"); 1079205179Sluigi /* avoid printing anything else */ 1080205179Sluigi flags = HAVE_PROTO | HAVE_SRCIP | 1081205179Sluigi HAVE_DSTIP | HAVE_IP; 108298943Sluigi break; 108398943Sluigi 108498943Sluigi case O_ACCEPT: 108598943Sluigi printf("allow"); 108698943Sluigi break; 108798943Sluigi 108898943Sluigi case O_COUNT: 108998943Sluigi printf("count"); 109098943Sluigi break; 109198943Sluigi 109298943Sluigi case O_DENY: 109398943Sluigi printf("deny"); 109498943Sluigi break; 109598943Sluigi 109699475Sluigi case O_REJECT: 109799475Sluigi if (cmd->arg1 == ICMP_REJECT_RST) 109899475Sluigi printf("reset"); 109999475Sluigi else if (cmd->arg1 == ICMP_UNREACH_HOST) 110099475Sluigi printf("reject"); 110199475Sluigi else 110299475Sluigi print_reject_code(cmd->arg1); 110399475Sluigi break; 110499475Sluigi 1105149020Sbz case O_UNREACH6: 1106149020Sbz if (cmd->arg1 == ICMP6_UNREACH_RST) 1107149020Sbz printf("reset6"); 1108149020Sbz else 1109149020Sbz print_unreach6_code(cmd->arg1); 1110149020Sbz break; 1111149020Sbz 1112159636Soleg case O_SKIPTO: 1113159636Soleg PRINT_UINT_ARG("skipto ", cmd->arg1); 111498943Sluigi break; 111598943Sluigi 111698943Sluigi case O_PIPE: 1117159636Soleg PRINT_UINT_ARG("pipe ", cmd->arg1); 1118159636Soleg break; 1119159636Soleg 112098943Sluigi case O_QUEUE: 1121159636Soleg PRINT_UINT_ARG("queue ", cmd->arg1); 1122159636Soleg break; 1123159636Soleg 112498943Sluigi case O_DIVERT: 1125159636Soleg PRINT_UINT_ARG("divert ", cmd->arg1); 1126159636Soleg break; 1127159636Soleg 112898943Sluigi case O_TEE: 1129159636Soleg PRINT_UINT_ARG("tee ", cmd->arg1); 1130159636Soleg break; 1131159636Soleg 1132141351Sglebius case O_NETGRAPH: 1133159636Soleg PRINT_UINT_ARG("netgraph ", cmd->arg1); 1134159636Soleg break; 1135159636Soleg 1136141351Sglebius case O_NGTEE: 1137159636Soleg PRINT_UINT_ARG("ngtee ", cmd->arg1); 1138159636Soleg break; 1139141351Sglebius 114098943Sluigi case O_FORWARD_IP: 114198943Sluigi { 114298943Sluigi ipfw_insn_sa *s = (ipfw_insn_sa *)cmd; 114398943Sluigi 1144161424Sjulian if (s->sa.sin_addr.s_addr == INADDR_ANY) { 1145161424Sjulian printf("fwd tablearg"); 1146161424Sjulian } else { 1147161424Sjulian printf("fwd %s", inet_ntoa(s->sa.sin_addr)); 1148161424Sjulian } 114998943Sluigi if (s->sa.sin_port) 1150103241Sluigi printf(",%d", s->sa.sin_port); 115198943Sluigi } 115298943Sluigi break; 115398943Sluigi 1154225044Sbz case O_FORWARD_IP6: 1155225044Sbz { 1156225044Sbz char buf[4 + INET6_ADDRSTRLEN + 1]; 1157225044Sbz ipfw_insn_sa6 *s = (ipfw_insn_sa6 *)cmd; 1158225044Sbz 1159225044Sbz printf("fwd %s", inet_ntop(AF_INET6, &s->sa.sin6_addr, 1160225044Sbz buf, sizeof(buf))); 1161225044Sbz if (s->sa.sin6_port) 1162225044Sbz printf(",%d", s->sa.sin6_port); 1163225044Sbz } 1164225044Sbz break; 1165225044Sbz 116698943Sluigi case O_LOG: /* O_LOG is printed last */ 116798943Sluigi logptr = (ipfw_insn_log *)cmd; 116898943Sluigi break; 116998943Sluigi 1170136071Sgreen case O_ALTQ: /* O_ALTQ is printed after O_LOG */ 1171136071Sgreen altqptr = (ipfw_insn_altq *)cmd; 1172136071Sgreen break; 1173136071Sgreen 1174158879Soleg case O_TAG: 1175158879Soleg tagptr = cmd; 1176158879Soleg break; 1177158879Soleg 1178165648Spiso case O_NAT: 1179223080Sae if (cmd->arg1 != 0) 1180223080Sae PRINT_UINT_ARG("nat ", cmd->arg1); 1181223080Sae else 1182223080Sae printf("nat global"); 1183223080Sae break; 1184220804Sglebius 1185178888Sjulian case O_SETFIB: 1186178888Sjulian PRINT_UINT_ARG("setfib ", cmd->arg1); 1187178888Sjulian break; 1188190633Spiso 1189190633Spiso case O_REASS: 1190190633Spiso printf("reass"); 1191190633Spiso break; 1192220804Sglebius 1193223666Sae case O_CALLRETURN: 1194223666Sae if (cmd->len & F_NOT) 1195223666Sae printf("return"); 1196223666Sae else 1197223666Sae PRINT_UINT_ARG("call ", cmd->arg1); 1198223666Sae break; 1199223666Sae 120098943Sluigi default: 1201136071Sgreen printf("** unrecognized action %d len %d ", 120298943Sluigi cmd->opcode, cmd->len); 120398943Sluigi } 120498943Sluigi } 120598943Sluigi if (logptr) { 120698943Sluigi if (logptr->max_log > 0) 120799909Sluigi printf(" log logamount %d", logptr->max_log); 120898943Sluigi else 120999909Sluigi printf(" log"); 121098943Sluigi } 1211204591Sluigi#ifndef NO_ALTQ 1212136071Sgreen if (altqptr) { 1213187983Sluigi print_altq_cmd(altqptr); 1214136071Sgreen } 1215204591Sluigi#endif 1216158879Soleg if (tagptr) { 1217158879Soleg if (tagptr->len & F_NOT) 1218159636Soleg PRINT_UINT_ARG(" untag ", tagptr->arg1); 1219158879Soleg else 1220159636Soleg PRINT_UINT_ARG(" tag ", tagptr->arg1); 1221158879Soleg } 1222136071Sgreen 122398943Sluigi /* 1224102087Sluigi * then print the body. 122598943Sluigi */ 1226220802Sglebius for (l = rule->act_ofs, cmd = rule->cmd ; 1227146894Smlaier l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { 1228146894Smlaier if ((cmd->len & F_OR) || (cmd->len & F_NOT)) 1229146894Smlaier continue; 1230146894Smlaier if (cmd->opcode == O_IP4) { 1231146894Smlaier flags |= HAVE_PROTO4; 1232146894Smlaier break; 1233146894Smlaier } else if (cmd->opcode == O_IP6) { 1234146894Smlaier flags |= HAVE_PROTO6; 1235146894Smlaier break; 1236220804Sglebius } 1237146894Smlaier } 1238102087Sluigi if (rule->_pad & 1) { /* empty rules before options */ 1239187764Sluigi if (!co.do_compact) { 1240146894Smlaier show_prerequisites(&flags, HAVE_PROTO, 0); 1241146894Smlaier printf(" from any to any"); 1242146894Smlaier } 1243205179Sluigi flags |= HAVE_IP | HAVE_OPTIONS | HAVE_PROTO | 1244205179Sluigi HAVE_SRCIP | HAVE_DSTIP; 1245102087Sluigi } 1246102087Sluigi 1247187764Sluigi if (co.comment_only) 1248123495Sluigi comment = "..."; 1249123495Sluigi 1250220802Sglebius for (l = rule->act_ofs, cmd = rule->cmd ; 125198943Sluigi l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { 125299475Sluigi /* useful alias */ 125399475Sluigi ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; 125498943Sluigi 1255187764Sluigi if (co.comment_only) { 1256123495Sluigi if (cmd->opcode != O_NOP) 1257123495Sluigi continue; 1258123495Sluigi printf(" // %s\n", (char *)(cmd + 1)); 1259123495Sluigi return; 1260123495Sluigi } 1261123495Sluigi 1262102087Sluigi show_prerequisites(&flags, 0, cmd->opcode); 1263102087Sluigi 126498943Sluigi switch(cmd->opcode) { 1265117577Sluigi case O_PROB: 1266107289Sluigi break; /* done already */ 1267107289Sluigi 126898943Sluigi case O_PROBE_STATE: 126998943Sluigi break; /* no need to print anything here */ 127098943Sluigi 127198943Sluigi case O_IP_SRC: 1272130281Sru case O_IP_SRC_LOOKUP: 127398943Sluigi case O_IP_SRC_MASK: 127498943Sluigi case O_IP_SRC_ME: 127598943Sluigi case O_IP_SRC_SET: 1276102087Sluigi show_prerequisites(&flags, HAVE_PROTO, 0); 127798943Sluigi if (!(flags & HAVE_SRCIP)) 127898943Sluigi printf(" from"); 127998943Sluigi if ((cmd->len & F_OR) && !or_block) 128098943Sluigi printf(" {"); 1281102087Sluigi print_ip((ipfw_insn_ip *)cmd, 1282102087Sluigi (flags & HAVE_OPTIONS) ? " src-ip" : ""); 128398943Sluigi flags |= HAVE_SRCIP; 128498943Sluigi break; 128598943Sluigi 128698943Sluigi case O_IP_DST: 1287130281Sru case O_IP_DST_LOOKUP: 128898943Sluigi case O_IP_DST_MASK: 128998943Sluigi case O_IP_DST_ME: 129098943Sluigi case O_IP_DST_SET: 1291102087Sluigi show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); 129298943Sluigi if (!(flags & HAVE_DSTIP)) 129398943Sluigi printf(" to"); 129498943Sluigi if ((cmd->len & F_OR) && !or_block) 129598943Sluigi printf(" {"); 1296102087Sluigi print_ip((ipfw_insn_ip *)cmd, 1297102087Sluigi (flags & HAVE_OPTIONS) ? " dst-ip" : ""); 129898943Sluigi flags |= HAVE_DSTIP; 129998943Sluigi break; 130098943Sluigi 1301145246Sbrooks case O_IP6_SRC: 1302145246Sbrooks case O_IP6_SRC_MASK: 1303145246Sbrooks case O_IP6_SRC_ME: 1304147105Smlaier show_prerequisites(&flags, HAVE_PROTO, 0); 1305145246Sbrooks if (!(flags & HAVE_SRCIP)) 1306145246Sbrooks printf(" from"); 1307145246Sbrooks if ((cmd->len & F_OR) && !or_block) 1308145246Sbrooks printf(" {"); 1309145246Sbrooks print_ip6((ipfw_insn_ip6 *)cmd, 1310145246Sbrooks (flags & HAVE_OPTIONS) ? " src-ip6" : ""); 1311145246Sbrooks flags |= HAVE_SRCIP | HAVE_PROTO; 1312145246Sbrooks break; 1313145246Sbrooks 1314145246Sbrooks case O_IP6_DST: 1315145246Sbrooks case O_IP6_DST_MASK: 1316145246Sbrooks case O_IP6_DST_ME: 1317145246Sbrooks show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); 1318145246Sbrooks if (!(flags & HAVE_DSTIP)) 1319145246Sbrooks printf(" to"); 1320145246Sbrooks if ((cmd->len & F_OR) && !or_block) 1321145246Sbrooks printf(" {"); 1322145246Sbrooks print_ip6((ipfw_insn_ip6 *)cmd, 1323145246Sbrooks (flags & HAVE_OPTIONS) ? " dst-ip6" : ""); 1324145246Sbrooks flags |= HAVE_DSTIP; 1325145246Sbrooks break; 1326145246Sbrooks 1327145246Sbrooks case O_FLOW6ID: 1328145246Sbrooks print_flow6id( (ipfw_insn_u32 *) cmd ); 1329145246Sbrooks flags |= HAVE_OPTIONS; 1330145246Sbrooks break; 1331145246Sbrooks 133298943Sluigi case O_IP_DSTPORT: 1333205179Sluigi show_prerequisites(&flags, 1334205179Sluigi HAVE_PROTO | HAVE_SRCIP | 1335205179Sluigi HAVE_DSTIP | HAVE_IP, 0); 133698943Sluigi case O_IP_SRCPORT: 1337224942Sjhb if (flags & HAVE_DSTIP) 1338224942Sjhb flags |= HAVE_IP; 1339205179Sluigi show_prerequisites(&flags, 1340205179Sluigi HAVE_PROTO | HAVE_SRCIP, 0); 1341101641Sluigi if ((cmd->len & F_OR) && !or_block) 1342101641Sluigi printf(" {"); 1343172306Smaxim if (cmd->len & F_NOT) 1344172306Smaxim printf(" not"); 1345102087Sluigi print_newports((ipfw_insn_u16 *)cmd, proto, 1346102087Sluigi (flags & HAVE_OPTIONS) ? cmd->opcode : 0); 134798943Sluigi break; 134898943Sluigi 134998943Sluigi case O_PROTO: { 1350145246Sbrooks struct protoent *pe = NULL; 135198943Sluigi 135298943Sluigi if ((cmd->len & F_OR) && !or_block) 135398943Sluigi printf(" {"); 135498943Sluigi if (cmd->len & F_NOT) 135598943Sluigi printf(" not"); 135698943Sluigi proto = cmd->arg1; 1357145567Sbrooks pe = getprotobynumber(cmd->arg1); 1358146894Smlaier if ((flags & (HAVE_PROTO4 | HAVE_PROTO6)) && 1359146894Smlaier !(flags & HAVE_PROTO)) 1360146894Smlaier show_prerequisites(&flags, 1361205179Sluigi HAVE_PROTO | HAVE_IP | HAVE_SRCIP | 1362205179Sluigi HAVE_DSTIP | HAVE_OPTIONS, 0); 1363102087Sluigi if (flags & HAVE_OPTIONS) 1364102087Sluigi printf(" proto"); 136598943Sluigi if (pe) 136698943Sluigi printf(" %s", pe->p_name); 136798943Sluigi else 136898943Sluigi printf(" %u", cmd->arg1); 136998943Sluigi } 137098943Sluigi flags |= HAVE_PROTO; 137198943Sluigi break; 1372106505Smaxim 137398943Sluigi default: /*options ... */ 1374146894Smlaier if (!(cmd->len & (F_OR|F_NOT))) 1375146894Smlaier if (((cmd->opcode == O_IP6) && 1376146894Smlaier (flags & HAVE_PROTO6)) || 1377146894Smlaier ((cmd->opcode == O_IP4) && 1378146894Smlaier (flags & HAVE_PROTO4))) 1379146894Smlaier break; 1380205179Sluigi show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP | 1381205179Sluigi HAVE_DSTIP | HAVE_IP | HAVE_OPTIONS, 0); 138298943Sluigi if ((cmd->len & F_OR) && !or_block) 138398943Sluigi printf(" {"); 138498943Sluigi if (cmd->len & F_NOT && cmd->opcode != O_IN) 138598943Sluigi printf(" not"); 138698943Sluigi switch(cmd->opcode) { 1387169139Smaxim case O_MACADDR2: { 1388169139Smaxim ipfw_insn_mac *m = (ipfw_insn_mac *)cmd; 1389169139Smaxim 1390169139Smaxim printf(" MAC"); 1391169139Smaxim print_mac(m->addr, m->mask); 1392169139Smaxim print_mac(m->addr + 6, m->mask + 6); 1393169139Smaxim } 1394169139Smaxim break; 1395169139Smaxim 1396169139Smaxim case O_MAC_TYPE: 1397169139Smaxim print_newports((ipfw_insn_u16 *)cmd, 1398169139Smaxim IPPROTO_ETHERTYPE, cmd->opcode); 1399169139Smaxim break; 1400169139Smaxim 1401169139Smaxim 140298943Sluigi case O_FRAG: 140398943Sluigi printf(" frag"); 140498943Sluigi break; 140598943Sluigi 1406178888Sjulian case O_FIB: 1407178888Sjulian printf(" fib %u", cmd->arg1 ); 1408178888Sjulian break; 1409215179Sluigi case O_SOCKARG: 1410215179Sluigi printf(" sockarg"); 1411215179Sluigi break; 1412178888Sjulian 141398943Sluigi case O_IN: 141498943Sluigi printf(cmd->len & F_NOT ? " out" : " in"); 141598943Sluigi break; 141698943Sluigi 1417136073Sgreen case O_DIVERTED: 1418136073Sgreen switch (cmd->arg1) { 1419136073Sgreen case 3: 1420136073Sgreen printf(" diverted"); 1421136073Sgreen break; 1422136073Sgreen case 1: 1423136073Sgreen printf(" diverted-loopback"); 1424136073Sgreen break; 1425136073Sgreen case 2: 1426136073Sgreen printf(" diverted-output"); 1427136073Sgreen break; 1428136073Sgreen default: 1429136073Sgreen printf(" diverted-?<%u>", cmd->arg1); 1430136073Sgreen break; 1431136073Sgreen } 1432136073Sgreen break; 1433136073Sgreen 143498943Sluigi case O_LAYER2: 143598943Sluigi printf(" layer2"); 143698943Sluigi break; 143798943Sluigi case O_XMIT: 143898943Sluigi case O_RECV: 1439140423Sglebius case O_VIA: 1440140423Sglebius { 1441117469Sluigi char const *s; 144298943Sluigi ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd; 144398943Sluigi 144498943Sluigi if (cmd->opcode == O_XMIT) 144598943Sluigi s = "xmit"; 144698943Sluigi else if (cmd->opcode == O_RECV) 144798943Sluigi s = "recv"; 1448117469Sluigi else /* if (cmd->opcode == O_VIA) */ 144998943Sluigi s = "via"; 145098943Sluigi if (cmdif->name[0] == '\0') 145199475Sluigi printf(" %s %s", s, 145299475Sluigi inet_ntoa(cmdif->p.ip)); 1453234597Smelifaro else if (cmdif->name[0] == '\1') /* interface table */ 1454234597Smelifaro printf(" %s table(%d)", s, cmdif->p.glob); 1455140423Sglebius else 1456140423Sglebius printf(" %s %s", s, cmdif->name); 1457140423Sglebius 145898943Sluigi break; 1459140423Sglebius } 146098943Sluigi case O_IPID: 1461116690Sluigi if (F_LEN(cmd) == 1) 1462116690Sluigi printf(" ipid %u", cmd->arg1 ); 1463116690Sluigi else 1464116690Sluigi print_newports((ipfw_insn_u16 *)cmd, 0, 1465116690Sluigi O_IPID); 146698943Sluigi break; 146798943Sluigi 146898943Sluigi case O_IPTTL: 1469116690Sluigi if (F_LEN(cmd) == 1) 1470116690Sluigi printf(" ipttl %u", cmd->arg1 ); 1471116690Sluigi else 1472116690Sluigi print_newports((ipfw_insn_u16 *)cmd, 0, 1473116690Sluigi O_IPTTL); 147498943Sluigi break; 147598943Sluigi 147698943Sluigi case O_IPVER: 147798943Sluigi printf(" ipver %u", cmd->arg1 ); 147898943Sluigi break; 147998943Sluigi 148099475Sluigi case O_IPPRECEDENCE: 148199475Sluigi printf(" ipprecedence %u", (cmd->arg1) >> 5 ); 148299475Sluigi break; 148399475Sluigi 148498943Sluigi case O_IPLEN: 1485116690Sluigi if (F_LEN(cmd) == 1) 1486116690Sluigi printf(" iplen %u", cmd->arg1 ); 1487116690Sluigi else 1488116690Sluigi print_newports((ipfw_insn_u16 *)cmd, 0, 1489116690Sluigi O_IPLEN); 149098943Sluigi break; 149198943Sluigi 1492101116Sluigi case O_IPOPT: 149398943Sluigi print_flags("ipoptions", cmd, f_ipopts); 149498943Sluigi break; 149598943Sluigi 149699475Sluigi case O_IPTOS: 149799475Sluigi print_flags("iptos", cmd, f_iptos); 149899475Sluigi break; 149999475Sluigi 150099475Sluigi case O_ICMPTYPE: 150199475Sluigi print_icmptypes((ipfw_insn_u32 *)cmd); 150299475Sluigi break; 150399475Sluigi 150498943Sluigi case O_ESTAB: 150598943Sluigi printf(" established"); 150698943Sluigi break; 150798943Sluigi 1508136075Sgreen case O_TCPDATALEN: 1509136075Sgreen if (F_LEN(cmd) == 1) 1510136075Sgreen printf(" tcpdatalen %u", cmd->arg1 ); 1511136075Sgreen else 1512136075Sgreen print_newports((ipfw_insn_u16 *)cmd, 0, 1513136075Sgreen O_TCPDATALEN); 1514136075Sgreen break; 1515136075Sgreen 151698943Sluigi case O_TCPFLAGS: 151798943Sluigi print_flags("tcpflags", cmd, f_tcpflags); 151898943Sluigi break; 151998943Sluigi 152098943Sluigi case O_TCPOPTS: 152198943Sluigi print_flags("tcpoptions", cmd, f_tcpopts); 152298943Sluigi break; 152398943Sluigi 152498943Sluigi case O_TCPWIN: 1525234278Sglebius if (F_LEN(cmd) == 1) 1526234278Sglebius printf(" tcpwin %u", cmd->arg1); 1527234278Sglebius else 1528234278Sglebius print_newports((ipfw_insn_u16 *)cmd, 0, 1529234278Sglebius O_TCPWIN); 153098943Sluigi break; 153198943Sluigi 153298943Sluigi case O_TCPACK: 153398943Sluigi printf(" tcpack %d", ntohl(cmd32->d[0])); 153498943Sluigi break; 153598943Sluigi 153698943Sluigi case O_TCPSEQ: 153798943Sluigi printf(" tcpseq %d", ntohl(cmd32->d[0])); 153898943Sluigi break; 153998943Sluigi 154098943Sluigi case O_UID: 154198943Sluigi { 154298943Sluigi struct passwd *pwd = getpwuid(cmd32->d[0]); 154398943Sluigi 154498943Sluigi if (pwd) 154598943Sluigi printf(" uid %s", pwd->pw_name); 154698943Sluigi else 154798943Sluigi printf(" uid %u", cmd32->d[0]); 154898943Sluigi } 154998943Sluigi break; 155098943Sluigi 155198943Sluigi case O_GID: 155298943Sluigi { 155398943Sluigi struct group *grp = getgrgid(cmd32->d[0]); 155498943Sluigi 155598943Sluigi if (grp) 155698943Sluigi printf(" gid %s", grp->gr_name); 155798943Sluigi else 155898943Sluigi printf(" gid %u", cmd32->d[0]); 155998943Sluigi } 156098943Sluigi break; 156198943Sluigi 1562133600Scsjp case O_JAIL: 1563133600Scsjp printf(" jail %d", cmd32->d[0]); 1564133600Scsjp break; 1565133600Scsjp 1566112250Scjc case O_VERREVPATH: 1567112250Scjc printf(" verrevpath"); 1568112250Scjc break; 1569116919Sluigi 1570128575Sandre case O_VERSRCREACH: 1571128575Sandre printf(" versrcreach"); 1572128575Sandre break; 1573128575Sandre 1574133387Sandre case O_ANTISPOOF: 1575133387Sandre printf(" antispoof"); 1576133387Sandre break; 1577133387Sandre 1578117241Sluigi case O_IPSEC: 1579117241Sluigi printf(" ipsec"); 1580117241Sluigi break; 1581117241Sluigi 1582117469Sluigi case O_NOP: 1583117626Sluigi comment = (char *)(cmd + 1); 1584117469Sluigi break; 1585117469Sluigi 158698943Sluigi case O_KEEP_STATE: 158798943Sluigi printf(" keep-state"); 158898943Sluigi break; 158998943Sluigi 1590159636Soleg case O_LIMIT: { 159198943Sluigi struct _s_x *p = limit_masks; 159298943Sluigi ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; 1593117328Sluigi uint8_t x = c->limit_mask; 1594117469Sluigi char const *comma = " "; 159598943Sluigi 159698943Sluigi printf(" limit"); 1597117577Sluigi for (; p->x != 0 ; p++) 159899909Sluigi if ((x & p->x) == p->x) { 159998943Sluigi x &= ~p->x; 160098943Sluigi printf("%s%s", comma, p->s); 160198943Sluigi comma = ","; 160298943Sluigi } 1603159636Soleg PRINT_UINT_ARG(" ", c->conn_limit); 160498943Sluigi break; 1605159636Soleg } 160698943Sluigi 1607146894Smlaier case O_IP6: 1608152923Sume printf(" ip6"); 1609145246Sbrooks break; 1610145246Sbrooks 1611146894Smlaier case O_IP4: 1612152923Sume printf(" ip4"); 1613146894Smlaier break; 1614146894Smlaier 1615145246Sbrooks case O_ICMP6TYPE: 1616145246Sbrooks print_icmp6types((ipfw_insn_u32 *)cmd); 1617145246Sbrooks break; 1618145246Sbrooks 1619145246Sbrooks case O_EXT_HDR: 1620145246Sbrooks print_ext6hdr( (ipfw_insn *) cmd ); 1621145246Sbrooks break; 1622145246Sbrooks 1623158879Soleg case O_TAGGED: 1624158879Soleg if (F_LEN(cmd) == 1) 1625159636Soleg PRINT_UINT_ARG(" tagged ", cmd->arg1); 1626158879Soleg else 1627159636Soleg print_newports((ipfw_insn_u16 *)cmd, 0, 1628159636Soleg O_TAGGED); 1629158879Soleg break; 1630158879Soleg 163198943Sluigi default: 163298943Sluigi printf(" [opcode %d len %d]", 163398943Sluigi cmd->opcode, cmd->len); 163498943Sluigi } 163598943Sluigi } 163698943Sluigi if (cmd->len & F_OR) { 163798943Sluigi printf(" or"); 163898943Sluigi or_block = 1; 163998943Sluigi } else if (or_block) { 164098943Sluigi printf(" }"); 164198943Sluigi or_block = 0; 164298943Sluigi } 164398943Sluigi } 1644205179Sluigi show_prerequisites(&flags, HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP 1645220802Sglebius | HAVE_IP, 0); 1646117626Sluigi if (comment) 1647117626Sluigi printf(" // %s", comment); 164898943Sluigi printf("\n"); 164998943Sluigi} 165098943Sluigi 165198943Sluigistatic void 1652112189Smaximshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth) 165398943Sluigi{ 165498943Sluigi struct protoent *pe; 165598943Sluigi struct in_addr a; 1656115793Sticso uint16_t rulenum; 1657159160Smlaier char buf[INET6_ADDRSTRLEN]; 165898943Sluigi 1659187764Sluigi if (!co.do_expired) { 166098943Sluigi if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT)) 166198943Sluigi return; 166298943Sluigi } 1663115793Sticso bcopy(&d->rule, &rulenum, sizeof(rulenum)); 1664117328Sluigi printf("%05d", rulenum); 1665206843Sluigi if (pcwidth > 0 || bcwidth > 0) { 1666206843Sluigi printf(" "); 1667206843Sluigi pr_u64(&d->pcnt, pcwidth); 1668206843Sluigi pr_u64(&d->bcnt, bcwidth); 1669206843Sluigi printf("(%ds)", d->expire); 1670206843Sluigi } 167198943Sluigi switch (d->dyn_type) { 167298943Sluigi case O_LIMIT_PARENT: 167398943Sluigi printf(" PARENT %d", d->count); 167498943Sluigi break; 167598943Sluigi case O_LIMIT: 167698943Sluigi printf(" LIMIT"); 167798943Sluigi break; 167898943Sluigi case O_KEEP_STATE: /* bidir, no mask */ 1679106505Smaxim printf(" STATE"); 168098943Sluigi break; 168198943Sluigi } 168298943Sluigi 168398943Sluigi if ((pe = getprotobynumber(d->id.proto)) != NULL) 168498943Sluigi printf(" %s", pe->p_name); 168598943Sluigi else 168698943Sluigi printf(" proto %u", d->id.proto); 168798943Sluigi 1688159160Smlaier if (d->id.addr_type == 4) { 1689159160Smlaier a.s_addr = htonl(d->id.src_ip); 1690159160Smlaier printf(" %s %d", inet_ntoa(a), d->id.src_port); 169198943Sluigi 1692159160Smlaier a.s_addr = htonl(d->id.dst_ip); 1693159160Smlaier printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port); 1694159160Smlaier } else if (d->id.addr_type == 6) { 1695159160Smlaier printf(" %s %d", inet_ntop(AF_INET6, &d->id.src_ip6, buf, 1696159160Smlaier sizeof(buf)), d->id.src_port); 1697159160Smlaier printf(" <-> %s %d", inet_ntop(AF_INET6, &d->id.dst_ip6, buf, 1698159160Smlaier sizeof(buf)), d->id.dst_port); 1699159160Smlaier } else 1700159160Smlaier printf(" UNKNOWN <-> UNKNOWN\n"); 1701220804Sglebius 170298943Sluigi printf("\n"); 170398943Sluigi} 170498943Sluigi 1705101978Sluigi/* 1706101978Sluigi * This one handles all set-related commands 1707101978Sluigi * ipfw set { show | enable | disable } 1708101978Sluigi * ipfw set swap X Y 1709101978Sluigi * ipfw set move X to Y 1710101978Sluigi * ipfw set move rule X to Y 1711101978Sluigi */ 1712187767Sluigivoid 1713204591Sluigiipfw_sets_handler(char *av[]) 1714101978Sluigi{ 1715117328Sluigi uint32_t set_disable, masks[2]; 1716101978Sluigi int i, nbytes; 1717117328Sluigi uint16_t rulenum; 1718117328Sluigi uint8_t cmd, new_set; 1719101978Sluigi 1720101978Sluigi av++; 1721101978Sluigi 1722204591Sluigi if (av[0] == NULL) 1723101978Sluigi errx(EX_USAGE, "set needs command"); 1724140271Sbrooks if (_substrcmp(*av, "show") == 0) { 1725204717Sluigi void *data = NULL; 1726117469Sluigi char const *msg; 1727204717Sluigi int nalloc; 1728101978Sluigi 1729204717Sluigi nalloc = nbytes = sizeof(struct ip_fw); 1730204717Sluigi while (nbytes >= nalloc) { 1731204717Sluigi if (data) 1732204717Sluigi free(data); 1733204717Sluigi nalloc = nalloc * 2 + 200; 1734204717Sluigi nbytes = nalloc; 1735206843Sluigi data = safe_calloc(1, nbytes); 1736206843Sluigi if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0) 1737206843Sluigi err(EX_OSERR, "getsockopt(IP_FW_GET)"); 1738204717Sluigi } 1739204717Sluigi 1740115793Sticso bcopy(&((struct ip_fw *)data)->next_rule, 1741115793Sticso &set_disable, sizeof(set_disable)); 1742101978Sluigi 1743117655Sluigi for (i = 0, msg = "disable" ; i < RESVD_SET; i++) 1744117577Sluigi if ((set_disable & (1<<i))) { 1745101978Sluigi printf("%s %d", msg, i); 1746101978Sluigi msg = ""; 1747101978Sluigi } 1748101978Sluigi msg = (set_disable) ? " enable" : "enable"; 1749117655Sluigi for (i = 0; i < RESVD_SET; i++) 1750117577Sluigi if (!(set_disable & (1<<i))) { 1751101978Sluigi printf("%s %d", msg, i); 1752101978Sluigi msg = ""; 1753101978Sluigi } 1754101978Sluigi printf("\n"); 1755140271Sbrooks } else if (_substrcmp(*av, "swap") == 0) { 1756204591Sluigi av++; 1757204591Sluigi if ( av[0] == NULL || av[1] == NULL ) 1758101978Sluigi errx(EX_USAGE, "set swap needs 2 set numbers\n"); 1759101978Sluigi rulenum = atoi(av[0]); 1760101978Sluigi new_set = atoi(av[1]); 1761117655Sluigi if (!isdigit(*(av[0])) || rulenum > RESVD_SET) 1762101978Sluigi errx(EX_DATAERR, "invalid set number %s\n", av[0]); 1763117655Sluigi if (!isdigit(*(av[1])) || new_set > RESVD_SET) 1764101978Sluigi errx(EX_DATAERR, "invalid set number %s\n", av[1]); 1765101978Sluigi masks[0] = (4 << 24) | (new_set << 16) | (rulenum); 1766117328Sluigi i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); 1767140271Sbrooks } else if (_substrcmp(*av, "move") == 0) { 1768204591Sluigi av++; 1769204717Sluigi if (av[0] && _substrcmp(*av, "rule") == 0) { 1770101978Sluigi cmd = 2; 1771204591Sluigi av++; 1772101978Sluigi } else 1773101978Sluigi cmd = 3; 1774204591Sluigi if (av[0] == NULL || av[1] == NULL || av[2] == NULL || 1775204591Sluigi av[3] != NULL || _substrcmp(av[1], "to") != 0) 1776101978Sluigi errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); 1777101978Sluigi rulenum = atoi(av[0]); 1778101978Sluigi new_set = atoi(av[2]); 1779117655Sluigi if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) || 1780182823Srik (cmd == 2 && rulenum == IPFW_DEFAULT_RULE) ) 1781101978Sluigi errx(EX_DATAERR, "invalid source number %s\n", av[0]); 1782117655Sluigi if (!isdigit(*(av[2])) || new_set > RESVD_SET) 1783101978Sluigi errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); 1784101978Sluigi masks[0] = (cmd << 24) | (new_set << 16) | (rulenum); 1785117328Sluigi i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); 1786140271Sbrooks } else if (_substrcmp(*av, "disable") == 0 || 1787140271Sbrooks _substrcmp(*av, "enable") == 0 ) { 1788140271Sbrooks int which = _substrcmp(*av, "enable") == 0 ? 1 : 0; 1789101978Sluigi 1790204591Sluigi av++; 1791101978Sluigi masks[0] = masks[1] = 0; 1792101978Sluigi 1793204717Sluigi while (av[0]) { 1794101978Sluigi if (isdigit(**av)) { 1795101978Sluigi i = atoi(*av); 1796117655Sluigi if (i < 0 || i > RESVD_SET) 1797101978Sluigi errx(EX_DATAERR, 1798101978Sluigi "invalid set number %d\n", i); 1799101978Sluigi masks[which] |= (1<<i); 1800140271Sbrooks } else if (_substrcmp(*av, "disable") == 0) 1801101978Sluigi which = 0; 1802140271Sbrooks else if (_substrcmp(*av, "enable") == 0) 1803101978Sluigi which = 1; 1804101978Sluigi else 1805101978Sluigi errx(EX_DATAERR, 1806101978Sluigi "invalid set command %s\n", *av); 1807204591Sluigi av++; 1808101978Sluigi } 1809101978Sluigi if ( (masks[0] & masks[1]) != 0 ) 1810101978Sluigi errx(EX_DATAERR, 1811101978Sluigi "cannot enable and disable the same set\n"); 1812101978Sluigi 1813117328Sluigi i = do_cmd(IP_FW_DEL, masks, sizeof(masks)); 1814101978Sluigi if (i) 1815101978Sluigi warn("set enable/disable: setsockopt(IP_FW_DEL)"); 1816101978Sluigi } else 1817101978Sluigi errx(EX_USAGE, "invalid set command %s\n", *av); 1818101978Sluigi} 1819101978Sluigi 1820187767Sluigivoid 1821204591Sluigiipfw_sysctl_handler(char *av[], int which) 1822109126Sdillon{ 1823109126Sdillon av++; 1824109126Sdillon 1825204591Sluigi if (av[0] == NULL) { 1826109126Sdillon warnx("missing keyword to enable/disable\n"); 1827140271Sbrooks } else if (_substrcmp(*av, "firewall") == 0) { 1828116770Sluigi sysctlbyname("net.inet.ip.fw.enable", NULL, 0, 1829116770Sluigi &which, sizeof(which)); 1830206266Sume sysctlbyname("net.inet6.ip6.fw.enable", NULL, 0, 1831206266Sume &which, sizeof(which)); 1832140271Sbrooks } else if (_substrcmp(*av, "one_pass") == 0) { 1833116770Sluigi sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0, 1834116770Sluigi &which, sizeof(which)); 1835140271Sbrooks } else if (_substrcmp(*av, "debug") == 0) { 1836116770Sluigi sysctlbyname("net.inet.ip.fw.debug", NULL, 0, 1837116770Sluigi &which, sizeof(which)); 1838140271Sbrooks } else if (_substrcmp(*av, "verbose") == 0) { 1839116770Sluigi sysctlbyname("net.inet.ip.fw.verbose", NULL, 0, 1840116770Sluigi &which, sizeof(which)); 1841140271Sbrooks } else if (_substrcmp(*av, "dyn_keepalive") == 0) { 1842116770Sluigi sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0, 1843116770Sluigi &which, sizeof(which)); 1844204591Sluigi#ifndef NO_ALTQ 1845140271Sbrooks } else if (_substrcmp(*av, "altq") == 0) { 1846136071Sgreen altq_set_enabled(which); 1847204591Sluigi#endif 1848109126Sdillon } else { 1849109126Sdillon warnx("unrecognize enable/disable keyword: %s\n", *av); 1850109126Sdillon } 1851109126Sdillon} 1852109126Sdillon 1853187767Sluigivoid 1854187767Sluigiipfw_list(int ac, char *av[], int show_counters) 185598943Sluigi{ 185698943Sluigi struct ip_fw *r; 185798943Sluigi ipfw_dyn_rule *dynrules, *d; 185898943Sluigi 1859117469Sluigi#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r))) 1860117469Sluigi char *lim; 1861117469Sluigi void *data = NULL; 1862112189Smaxim int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width; 186398943Sluigi int exitval = EX_OK; 186498943Sluigi int lac; 186598943Sluigi char **lav; 1866117469Sluigi u_long rnum, last; 186798943Sluigi char *endptr; 186898943Sluigi int seen = 0; 1869170923Smaxim uint8_t set; 187098943Sluigi 1871187764Sluigi const int ocmd = co.do_pipe ? IP_DUMMYNET_GET : IP_FW_GET; 187298943Sluigi int nalloc = 1024; /* start somewhere... */ 187398943Sluigi 1874135036Smaxim last = 0; 1875135036Smaxim 1876187764Sluigi if (co.test_only) { 1877117328Sluigi fprintf(stderr, "Testing only, list disabled\n"); 1878117328Sluigi return; 1879117328Sluigi } 1880204591Sluigi if (co.do_pipe) { 1881204591Sluigi dummynet_list(ac, av, show_counters); 1882204591Sluigi return; 1883204591Sluigi } 1884117328Sluigi 188598943Sluigi ac--; 188698943Sluigi av++; 188798943Sluigi 188898943Sluigi /* get rules or pipes from kernel, resizing array as necessary */ 188998943Sluigi nbytes = nalloc; 189098943Sluigi 189198943Sluigi while (nbytes >= nalloc) { 189298943Sluigi nalloc = nalloc * 2 + 200; 189398943Sluigi nbytes = nalloc; 1894187716Sluigi data = safe_realloc(data, nbytes); 1895119740Stmm if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0) 189698943Sluigi err(EX_OSERR, "getsockopt(IP_%s_GET)", 1897187764Sluigi co.do_pipe ? "DUMMYNET" : "FW"); 189898943Sluigi } 189998943Sluigi 190098943Sluigi /* 190198943Sluigi * Count static rules. They have variable size so we 190298943Sluigi * need to scan the list to count them. 190398943Sluigi */ 1904117469Sluigi for (nstat = 1, r = data, lim = (char *)data + nbytes; 1905182823Srik r->rulenum < IPFW_DEFAULT_RULE && (char *)r < lim; 1906117469Sluigi ++nstat, r = NEXT(r) ) 190798943Sluigi ; /* nothing */ 190898943Sluigi 190998943Sluigi /* 191098943Sluigi * Count dynamic rules. This is easier as they have 191198943Sluigi * fixed size. 191298943Sluigi */ 1913117469Sluigi r = NEXT(r); 191498943Sluigi dynrules = (ipfw_dyn_rule *)r ; 1915117469Sluigi n = (char *)r - (char *)data; 191698943Sluigi ndyn = (nbytes - n) / sizeof *dynrules; 191798943Sluigi 1918112189Smaxim /* if showing stats, figure out column widths ahead of time */ 1919112189Smaxim bcwidth = pcwidth = 0; 1920117469Sluigi if (show_counters) { 1921117469Sluigi for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) { 1922170923Smaxim /* skip rules from another set */ 1923187764Sluigi if (co.use_set && r->set != co.use_set - 1) 1924170923Smaxim continue; 1925170923Smaxim 1926112189Smaxim /* packet counter */ 1927206843Sluigi width = pr_u64(&r->pcnt, 0); 1928112189Smaxim if (width > pcwidth) 1929112189Smaxim pcwidth = width; 1930112189Smaxim 1931112189Smaxim /* byte counter */ 1932206843Sluigi width = pr_u64(&r->bcnt, 0); 1933112189Smaxim if (width > bcwidth) 1934112189Smaxim bcwidth = width; 1935112189Smaxim } 1936112189Smaxim } 1937187764Sluigi if (co.do_dynamic && ndyn) { 1938112189Smaxim for (n = 0, d = dynrules; n < ndyn; n++, d++) { 1939187764Sluigi if (co.use_set) { 1940170923Smaxim /* skip rules from another set */ 1941171989Smaxim bcopy((char *)&d->rule + sizeof(uint16_t), 1942170923Smaxim &set, sizeof(uint8_t)); 1943187764Sluigi if (set != co.use_set - 1) 1944170923Smaxim continue; 1945170923Smaxim } 1946206843Sluigi width = pr_u64(&d->pcnt, 0); 1947112189Smaxim if (width > pcwidth) 1948112189Smaxim pcwidth = width; 1949112189Smaxim 1950206843Sluigi width = pr_u64(&d->bcnt, 0); 1951112189Smaxim if (width > bcwidth) 1952112189Smaxim bcwidth = width; 1953112189Smaxim } 1954112189Smaxim } 195598943Sluigi /* if no rule numbers were specified, list all rules */ 195698943Sluigi if (ac == 0) { 1957170923Smaxim for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) { 1958187764Sluigi if (co.use_set && r->set != co.use_set - 1) 1959170923Smaxim continue; 1960112189Smaxim show_ipfw(r, pcwidth, bcwidth); 1961170923Smaxim } 196298943Sluigi 1963187764Sluigi if (co.do_dynamic && ndyn) { 196498943Sluigi printf("## Dynamic rules (%d):\n", ndyn); 1965170923Smaxim for (n = 0, d = dynrules; n < ndyn; n++, d++) { 1966187764Sluigi if (co.use_set) { 1967171989Smaxim bcopy((char *)&d->rule + sizeof(uint16_t), 1968170923Smaxim &set, sizeof(uint8_t)); 1969187764Sluigi if (set != co.use_set - 1) 1970170923Smaxim continue; 1971170923Smaxim } 1972112189Smaxim show_dyn_ipfw(d, pcwidth, bcwidth); 197398943Sluigi } 1974170923Smaxim } 197598943Sluigi goto done; 197698943Sluigi } 197798943Sluigi 197898943Sluigi /* display specific rules requested on command line */ 197998943Sluigi 198098943Sluigi for (lac = ac, lav = av; lac != 0; lac--) { 198198943Sluigi /* convert command line rule # */ 1982117469Sluigi last = rnum = strtoul(*lav++, &endptr, 10); 1983117469Sluigi if (*endptr == '-') 1984117469Sluigi last = strtoul(endptr+1, &endptr, 10); 198598943Sluigi if (*endptr) { 198698943Sluigi exitval = EX_USAGE; 198798943Sluigi warnx("invalid rule number: %s", *(lav - 1)); 198898943Sluigi continue; 198998943Sluigi } 1990117469Sluigi for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) { 1991117469Sluigi if (r->rulenum > last) 199298943Sluigi break; 1993187764Sluigi if (co.use_set && r->set != co.use_set - 1) 1994170923Smaxim continue; 1995117469Sluigi if (r->rulenum >= rnum && r->rulenum <= last) { 1996112189Smaxim show_ipfw(r, pcwidth, bcwidth); 199798943Sluigi seen = 1; 199898943Sluigi } 199998943Sluigi } 200098943Sluigi if (!seen) { 200198943Sluigi /* give precedence to other error(s) */ 200298943Sluigi if (exitval == EX_OK) 200398943Sluigi exitval = EX_UNAVAILABLE; 200498943Sluigi warnx("rule %lu does not exist", rnum); 200598943Sluigi } 200698943Sluigi } 200798943Sluigi 2008187764Sluigi if (co.do_dynamic && ndyn) { 200998943Sluigi printf("## Dynamic rules:\n"); 201098943Sluigi for (lac = ac, lav = av; lac != 0; lac--) { 2011145246Sbrooks last = rnum = strtoul(*lav++, &endptr, 10); 2012117469Sluigi if (*endptr == '-') 2013117469Sluigi last = strtoul(endptr+1, &endptr, 10); 201498943Sluigi if (*endptr) 201598943Sluigi /* already warned */ 201698943Sluigi continue; 201798943Sluigi for (n = 0, d = dynrules; n < ndyn; n++, d++) { 2018115793Sticso uint16_t rulenum; 2019115793Sticso 2020115793Sticso bcopy(&d->rule, &rulenum, sizeof(rulenum)); 2021115793Sticso if (rulenum > rnum) 202298943Sluigi break; 2023187764Sluigi if (co.use_set) { 2024171989Smaxim bcopy((char *)&d->rule + sizeof(uint16_t), 2025170923Smaxim &set, sizeof(uint8_t)); 2026187764Sluigi if (set != co.use_set - 1) 2027170923Smaxim continue; 2028170923Smaxim } 2029117469Sluigi if (r->rulenum >= rnum && r->rulenum <= last) 2030112189Smaxim show_dyn_ipfw(d, pcwidth, bcwidth); 203198943Sluigi } 203298943Sluigi } 203398943Sluigi } 203498943Sluigi 203598943Sluigi ac = 0; 203698943Sluigi 203798943Sluigidone: 203898943Sluigi free(data); 203998943Sluigi 204098943Sluigi if (exitval != EX_OK) 204198943Sluigi exit(exitval); 2042117469Sluigi#undef NEXT 204398943Sluigi} 204498943Sluigi 204598943Sluigistatic int 204698943Sluigilookup_host (char *host, struct in_addr *ipaddr) 204798943Sluigi{ 204898943Sluigi struct hostent *he; 204998943Sluigi 205098943Sluigi if (!inet_aton(host, ipaddr)) { 205198943Sluigi if ((he = gethostbyname(host)) == NULL) 205298943Sluigi return(-1); 205398943Sluigi *ipaddr = *(struct in_addr *)he->h_addr_list[0]; 205498943Sluigi } 205598943Sluigi return(0); 205698943Sluigi} 205798943Sluigi 205898943Sluigi/* 205998943Sluigi * fills the addr and mask fields in the instruction as appropriate from av. 206098943Sluigi * Update length as appropriate. 206198943Sluigi * The following formats are allowed: 206298943Sluigi * me returns O_IP_*_ME 206398943Sluigi * 1.2.3.4 single IP address 206498943Sluigi * 1.2.3.4:5.6.7.8 address:mask 206598943Sluigi * 1.2.3.4/24 address/mask 206698943Sluigi * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet 2067117328Sluigi * We can have multiple comma-separated address/mask entries. 206898943Sluigi */ 206998943Sluigistatic void 207098943Sluigifill_ip(ipfw_insn_ip *cmd, char *av) 207198943Sluigi{ 2072117328Sluigi int len = 0; 2073117328Sluigi uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; 207498943Sluigi 207598943Sluigi cmd->o.len &= ~F_LEN_MASK; /* zero len */ 207698943Sluigi 2077140271Sbrooks if (_substrcmp(av, "any") == 0) 207898943Sluigi return; 207998943Sluigi 2080140271Sbrooks if (_substrcmp(av, "me") == 0) { 208198943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn); 208298943Sluigi return; 208398943Sluigi } 208498943Sluigi 2085140271Sbrooks if (strncmp(av, "table(", 6) == 0) { 2086130281Sru char *p = strchr(av + 6, ','); 2087130281Sru 2088130281Sru if (p) 2089130281Sru *p++ = '\0'; 2090130281Sru cmd->o.opcode = O_IP_DST_LOOKUP; 2091130281Sru cmd->o.arg1 = strtoul(av + 6, NULL, 0); 2092130281Sru if (p) { 2093130281Sru cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 2094130281Sru d[0] = strtoul(p, NULL, 0); 2095130281Sru } else 2096130281Sru cmd->o.len |= F_INSN_SIZE(ipfw_insn); 2097130281Sru return; 2098130281Sru } 2099130281Sru 2100117328Sluigi while (av) { 2101117328Sluigi /* 2102117328Sluigi * After the address we can have '/' or ':' indicating a mask, 2103117328Sluigi * ',' indicating another address follows, '{' indicating a 2104117328Sluigi * set of addresses of unspecified size. 2105117328Sluigi */ 2106165851Smlaier char *t = NULL, *p = strpbrk(av, "/:,{"); 2107117328Sluigi int masklen; 2108187477Sluigi char md, nd = '\0'; 2109117328Sluigi 211098943Sluigi if (p) { 211198943Sluigi md = *p; 211298943Sluigi *p++ = '\0'; 2113165851Smlaier if ((t = strpbrk(p, ",{")) != NULL) { 2114165851Smlaier nd = *t; 2115165851Smlaier *t = '\0'; 2116165851Smlaier } 2117117328Sluigi } else 2118117328Sluigi md = '\0'; 211998943Sluigi 2120117328Sluigi if (lookup_host(av, (struct in_addr *)&d[0]) != 0) 212198943Sluigi errx(EX_NOHOST, "hostname ``%s'' unknown", av); 212298943Sluigi switch (md) { 212398943Sluigi case ':': 2124117328Sluigi if (!inet_aton(p, (struct in_addr *)&d[1])) 212598943Sluigi errx(EX_DATAERR, "bad netmask ``%s''", p); 212698943Sluigi break; 212798943Sluigi case '/': 2128117328Sluigi masklen = atoi(p); 2129117328Sluigi if (masklen == 0) 2130117328Sluigi d[1] = htonl(0); /* mask */ 2131117328Sluigi else if (masklen > 32) 213298943Sluigi errx(EX_DATAERR, "bad width ``%s''", p); 213398943Sluigi else 2134117328Sluigi d[1] = htonl(~0 << (32 - masklen)); 213598943Sluigi break; 2136117328Sluigi case '{': /* no mask, assume /24 and put back the '{' */ 2137117328Sluigi d[1] = htonl(~0 << (32 - 24)); 2138117328Sluigi *(--p) = md; 2139117328Sluigi break; 2140117328Sluigi 2141117328Sluigi case ',': /* single address plus continuation */ 2142117328Sluigi *(--p) = md; 2143117328Sluigi /* FALLTHROUGH */ 2144117328Sluigi case 0: /* initialization value */ 214598943Sluigi default: 2146117328Sluigi d[1] = htonl(~0); /* force /32 */ 214798943Sluigi break; 214898943Sluigi } 2149117328Sluigi d[0] &= d[1]; /* mask base address with mask */ 2150165851Smlaier if (t) 2151165851Smlaier *t = nd; 2152117328Sluigi /* find next separator */ 215398943Sluigi if (p) 2154117328Sluigi p = strpbrk(p, ",{"); 2155117328Sluigi if (p && *p == '{') { 2156117328Sluigi /* 2157117328Sluigi * We have a set of addresses. They are stored as follows: 2158117328Sluigi * arg1 is the set size (powers of 2, 2..256) 2159117328Sluigi * addr is the base address IN HOST FORMAT 2160117328Sluigi * mask.. is an array of arg1 bits (rounded up to 2161117328Sluigi * the next multiple of 32) with bits set 2162117328Sluigi * for each host in the map. 2163117328Sluigi */ 2164117328Sluigi uint32_t *map = (uint32_t *)&cmd->mask; 216598943Sluigi int low, high; 2166117577Sluigi int i = contigmask((uint8_t *)&(d[1]), 32); 216798943Sluigi 2168117328Sluigi if (len > 0) 2169117328Sluigi errx(EX_DATAERR, "address set cannot be in a list"); 2170117328Sluigi if (i < 24 || i > 31) 2171117328Sluigi errx(EX_DATAERR, "invalid set with mask %d\n", i); 2172117328Sluigi cmd->o.arg1 = 1<<(32-i); /* map length */ 2173117328Sluigi d[0] = ntohl(d[0]); /* base addr in host format */ 217498943Sluigi cmd->o.opcode = O_IP_DST_SET; /* default */ 217598943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32; 2176101117Sluigi for (i = 0; i < (cmd->o.arg1+31)/32 ; i++) 2177117328Sluigi map[i] = 0; /* clear map */ 217898943Sluigi 2179117328Sluigi av = p + 1; 2180117328Sluigi low = d[0] & 0xff; 218198943Sluigi high = low + cmd->o.arg1 - 1; 2182117328Sluigi /* 2183117328Sluigi * Here, i stores the previous value when we specify a range 2184117328Sluigi * of addresses within a mask, e.g. 45-63. i = -1 means we 2185117328Sluigi * have no previous value. 2186117328Sluigi */ 2187116716Sluigi i = -1; /* previous value in a range */ 218898943Sluigi while (isdigit(*av)) { 218998943Sluigi char *s; 2190117328Sluigi int a = strtol(av, &s, 0); 219198943Sluigi 2192117328Sluigi if (s == av) { /* no parameter */ 2193117328Sluigi if (*av != '}') 2194117328Sluigi errx(EX_DATAERR, "set not closed\n"); 2195117328Sluigi if (i != -1) 2196117328Sluigi errx(EX_DATAERR, "incomplete range %d-", i); 2197117328Sluigi break; 2198117328Sluigi } 2199117328Sluigi if (a < low || a > high) 2200117328Sluigi errx(EX_DATAERR, "addr %d out of range [%d-%d]\n", 220198943Sluigi a, low, high); 220298943Sluigi a -= low; 2203116716Sluigi if (i == -1) /* no previous in range */ 2204116716Sluigi i = a; 2205116716Sluigi else { /* check that range is valid */ 2206116716Sluigi if (i > a) 2207116716Sluigi errx(EX_DATAERR, "invalid range %d-%d", 2208116716Sluigi i+low, a+low); 2209116716Sluigi if (*s == '-') 2210116716Sluigi errx(EX_DATAERR, "double '-' in range"); 2211116716Sluigi } 2212116716Sluigi for (; i <= a; i++) 2213117328Sluigi map[i/32] |= 1<<(i & 31); 2214116716Sluigi i = -1; 2215116716Sluigi if (*s == '-') 2216116716Sluigi i = a; 2217117328Sluigi else if (*s == '}') 2218116716Sluigi break; 221998943Sluigi av = s+1; 222098943Sluigi } 222198943Sluigi return; 222298943Sluigi } 2223117328Sluigi av = p; 2224117328Sluigi if (av) /* then *av must be a ',' */ 2225117328Sluigi av++; 222698943Sluigi 2227117328Sluigi /* Check this entry */ 2228117328Sluigi if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */ 2229117328Sluigi /* 2230117328Sluigi * 'any' turns the entire list into a NOP. 2231117328Sluigi * 'not any' never matches, so it is removed from the 2232117328Sluigi * list unless it is the only item, in which case we 2233117328Sluigi * report an error. 2234117328Sluigi */ 2235117328Sluigi if (cmd->o.len & F_NOT) { /* "not any" never matches */ 2236117328Sluigi if (av == NULL && len == 0) /* only this entry */ 2237117328Sluigi errx(EX_DATAERR, "not any never matches"); 2238117328Sluigi } 2239117328Sluigi /* else do nothing and skip this entry */ 2240128067Smaxim return; 2241117328Sluigi } 2242117328Sluigi /* A single IP can be stored in an optimized format */ 2243204591Sluigi if (d[1] == (uint32_t)~0 && av == NULL && len == 0) { 224498943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 2245117328Sluigi return; 2246117328Sluigi } 2247117328Sluigi len += 2; /* two words... */ 2248117328Sluigi d += 2; 2249117328Sluigi } /* end while */ 2250162363Sjhay if (len + 1 > F_LEN_MASK) 2251162363Sjhay errx(EX_DATAERR, "address list too long"); 2252117328Sluigi cmd->o.len |= len+1; 225398943Sluigi} 225498943Sluigi 225598943Sluigi 2256145246Sbrooks/* n2mask sets n bits of the mask */ 2257187769Sluigivoid 2258145246Sbrooksn2mask(struct in6_addr *mask, int n) 2259145246Sbrooks{ 2260145246Sbrooks static int minimask[9] = 2261145246Sbrooks { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff }; 2262145246Sbrooks u_char *p; 2263145246Sbrooks 2264145246Sbrooks memset(mask, 0, sizeof(struct in6_addr)); 2265145246Sbrooks p = (u_char *) mask; 2266145246Sbrooks for (; n > 0; p++, n -= 8) { 2267145246Sbrooks if (n >= 8) 2268145246Sbrooks *p = 0xff; 2269145246Sbrooks else 2270145246Sbrooks *p = minimask[n]; 2271145246Sbrooks } 2272145246Sbrooks return; 2273145246Sbrooks} 2274145246Sbrooks 227598943Sluigi/* 227698943Sluigi * helper function to process a set of flags and set bits in the 227798943Sluigi * appropriate masks. 227898943Sluigi */ 227998943Sluigistatic void 228098943Sluigifill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, 228198943Sluigi struct _s_x *flags, char *p) 228298943Sluigi{ 2283117328Sluigi uint8_t set=0, clear=0; 228498943Sluigi 228598943Sluigi while (p && *p) { 228698943Sluigi char *q; /* points to the separator */ 228798943Sluigi int val; 2288117328Sluigi uint8_t *which; /* mask we are working on */ 228998943Sluigi 229098943Sluigi if (*p == '!') { 229198943Sluigi p++; 229298943Sluigi which = &clear; 229398943Sluigi } else 229498943Sluigi which = &set; 229598943Sluigi q = strchr(p, ','); 229698943Sluigi if (q) 229798943Sluigi *q++ = '\0'; 229898943Sluigi val = match_token(flags, p); 229998943Sluigi if (val <= 0) 230098943Sluigi errx(EX_DATAERR, "invalid flag %s", p); 2301117328Sluigi *which |= (uint8_t)val; 230298943Sluigi p = q; 230398943Sluigi } 2304220802Sglebius cmd->opcode = opcode; 2305220802Sglebius cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; 2306220802Sglebius cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); 230798943Sluigi} 230898943Sluigi 230998943Sluigi 2310187767Sluigivoid 2311204591Sluigiipfw_delete(char *av[]) 231298943Sluigi{ 2313117328Sluigi uint32_t rulenum; 231498943Sluigi int i; 231598943Sluigi int exitval = EX_OK; 2316101628Sluigi int do_set = 0; 231798943Sluigi 2318204591Sluigi av++; 2319130013Scsjp NEED1("missing rule specification"); 2320204591Sluigi if ( *av && _substrcmp(*av, "set") == 0) { 2321170923Smaxim /* Do not allow using the following syntax: 2322170923Smaxim * ipfw set N delete set M 2323170923Smaxim */ 2324187764Sluigi if (co.use_set) 2325170923Smaxim errx(EX_DATAERR, "invalid syntax"); 2326101978Sluigi do_set = 1; /* delete set */ 2327204591Sluigi av++; 2328101978Sluigi } 232998943Sluigi 233098943Sluigi /* Rule number */ 2331204591Sluigi while (*av && isdigit(**av)) { 2332204591Sluigi i = atoi(*av); av++; 2333187764Sluigi if (co.do_nat) { 2334165648Spiso exitval = do_cmd(IP_FW_NAT_DEL, &i, sizeof i); 2335165648Spiso if (exitval) { 2336165648Spiso exitval = EX_UNAVAILABLE; 2337165648Spiso warn("rule %u not available", i); 2338165648Spiso } 2339187764Sluigi } else if (co.do_pipe) { 2340187769Sluigi exitval = ipfw_delete_pipe(co.do_pipe, i); 234198943Sluigi } else { 2342187764Sluigi if (co.use_set) 2343170923Smaxim rulenum = (i & 0xffff) | (5 << 24) | 2344187764Sluigi ((co.use_set - 1) << 16); 2345170923Smaxim else 2346101978Sluigi rulenum = (i & 0xffff) | (do_set << 24); 2347117328Sluigi i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum); 234898943Sluigi if (i) { 234998943Sluigi exitval = EX_UNAVAILABLE; 235098943Sluigi warn("rule %u: setsockopt(IP_FW_DEL)", 235198943Sluigi rulenum); 235298943Sluigi } 235398943Sluigi } 235498943Sluigi } 235598943Sluigi if (exitval != EX_OK) 235698943Sluigi exit(exitval); 235798943Sluigi} 235898943Sluigi 235998943Sluigi 236098943Sluigi/* 236198943Sluigi * fill the interface structure. We do not check the name as we can 236298943Sluigi * create interfaces dynamically, so checking them at insert time 236398943Sluigi * makes relatively little sense. 2364220802Sglebius * Interface names containing '*', '?', or '[' are assumed to be shell 2365121816Sbrooks * patterns which match interfaces. 236698943Sluigi */ 236798943Sluigistatic void 236898943Sluigifill_iface(ipfw_insn_if *cmd, char *arg) 236998943Sluigi{ 237098943Sluigi cmd->name[0] = '\0'; 237198943Sluigi cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); 237298943Sluigi 237398943Sluigi /* Parse the interface or address */ 2374140271Sbrooks if (strcmp(arg, "any") == 0) 237598943Sluigi cmd->o.len = 0; /* effectively ignore this command */ 2376234597Smelifaro else if (strncmp(arg, "table(", 6) == 0) { 2377234597Smelifaro char *p = strchr(arg + 6, ','); 2378234597Smelifaro if (p) 2379234597Smelifaro *p++ = '\0'; 2380234597Smelifaro cmd->name[0] = '\1'; /* Special value indicating table */ 2381234597Smelifaro cmd->p.glob = strtoul(arg + 6, NULL, 0); 2382234597Smelifaro } else if (!isdigit(*arg)) { 2383121816Sbrooks strlcpy(cmd->name, arg, sizeof(cmd->name)); 2384121816Sbrooks cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; 238598943Sluigi } else if (!inet_aton(arg, &cmd->p.ip)) 238698943Sluigi errx(EX_DATAERR, "bad ip address ``%s''", arg); 238798943Sluigi} 238898943Sluigi 238998943Sluigistatic void 2390169424Smaximget_mac_addr_mask(const char *p, uint8_t *addr, uint8_t *mask) 239198943Sluigi{ 2392204591Sluigi int i; 2393204591Sluigi size_t l; 2394169424Smaxim char *ap, *ptr, *optr; 2395169424Smaxim struct ether_addr *mac; 2396169424Smaxim const char *macset = "0123456789abcdefABCDEF:"; 239798943Sluigi 2398169424Smaxim if (strcmp(p, "any") == 0) { 2399169424Smaxim for (i = 0; i < ETHER_ADDR_LEN; i++) 2400169424Smaxim addr[i] = mask[i] = 0; 240198943Sluigi return; 2402169424Smaxim } 240398943Sluigi 2404169424Smaxim optr = ptr = strdup(p); 2405169424Smaxim if ((ap = strsep(&ptr, "&/")) != NULL && *ap != 0) { 2406169424Smaxim l = strlen(ap); 2407169424Smaxim if (strspn(ap, macset) != l || (mac = ether_aton(ap)) == NULL) 2408169424Smaxim errx(EX_DATAERR, "Incorrect MAC address"); 2409169424Smaxim bcopy(mac, addr, ETHER_ADDR_LEN); 2410169424Smaxim } else 2411169424Smaxim errx(EX_DATAERR, "Incorrect MAC address"); 2412169424Smaxim 2413169424Smaxim if (ptr != NULL) { /* we have mask? */ 2414169424Smaxim if (p[ptr - optr - 1] == '/') { /* mask len */ 2415204591Sluigi long ml = strtol(ptr, &ap, 10); 2416204591Sluigi if (*ap != 0 || ml > ETHER_ADDR_LEN * 8 || ml < 0) 2417169424Smaxim errx(EX_DATAERR, "Incorrect mask length"); 2418204591Sluigi for (i = 0; ml > 0 && i < ETHER_ADDR_LEN; ml -= 8, i++) 2419204591Sluigi mask[i] = (ml >= 8) ? 0xff: (~0) << (8 - ml); 2420169424Smaxim } else { /* mask */ 2421169424Smaxim l = strlen(ptr); 2422169424Smaxim if (strspn(ptr, macset) != l || 2423169424Smaxim (mac = ether_aton(ptr)) == NULL) 2424169424Smaxim errx(EX_DATAERR, "Incorrect mask"); 2425169424Smaxim bcopy(mac, mask, ETHER_ADDR_LEN); 242698943Sluigi } 2427169424Smaxim } else { /* default mask: ff:ff:ff:ff:ff:ff */ 2428169424Smaxim for (i = 0; i < ETHER_ADDR_LEN; i++) 242998943Sluigi mask[i] = 0xff; 243098943Sluigi } 2431169424Smaxim for (i = 0; i < ETHER_ADDR_LEN; i++) 243298943Sluigi addr[i] &= mask[i]; 2433169424Smaxim 2434169424Smaxim free(optr); 243598943Sluigi} 243698943Sluigi 243798943Sluigi/* 243898943Sluigi * helper function, updates the pointer to cmd with the length 243998943Sluigi * of the current command, and also cleans up the first word of 244098943Sluigi * the new command in case it has been clobbered before. 244198943Sluigi */ 244298943Sluigistatic ipfw_insn * 244398943Sluiginext_cmd(ipfw_insn *cmd) 244498943Sluigi{ 244598943Sluigi cmd += F_LEN(cmd); 244698943Sluigi bzero(cmd, sizeof(*cmd)); 244798943Sluigi return cmd; 244898943Sluigi} 244998943Sluigi 245098943Sluigi/* 2451117469Sluigi * Takes arguments and copies them into a comment 2452117469Sluigi */ 2453117469Sluigistatic void 2454204591Sluigifill_comment(ipfw_insn *cmd, char **av) 2455117469Sluigi{ 2456117469Sluigi int i, l; 2457117469Sluigi char *p = (char *)(cmd + 1); 2458117577Sluigi 2459117469Sluigi cmd->opcode = O_NOP; 2460117469Sluigi cmd->len = (cmd->len & (F_NOT | F_OR)); 2461117469Sluigi 2462117469Sluigi /* Compute length of comment string. */ 2463204591Sluigi for (i = 0, l = 0; av[i] != NULL; i++) 2464117469Sluigi l += strlen(av[i]) + 1; 2465117469Sluigi if (l == 0) 2466117469Sluigi return; 2467117469Sluigi if (l > 84) 2468117469Sluigi errx(EX_DATAERR, 2469117469Sluigi "comment too long (max 80 chars)"); 2470117469Sluigi l = 1 + (l+3)/4; 2471117469Sluigi cmd->len = (cmd->len & (F_NOT | F_OR)) | l; 2472204591Sluigi for (i = 0; av[i] != NULL; i++) { 2473117469Sluigi strcpy(p, av[i]); 2474117469Sluigi p += strlen(av[i]); 2475117469Sluigi *p++ = ' '; 2476117469Sluigi } 2477117469Sluigi *(--p) = '\0'; 2478117469Sluigi} 2479117577Sluigi 2480117469Sluigi/* 248198943Sluigi * A function to fill simple commands of size 1. 248298943Sluigi * Existing flags are preserved. 248398943Sluigi */ 248498943Sluigistatic void 2485117328Sluigifill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg) 248698943Sluigi{ 248798943Sluigi cmd->opcode = opcode; 248898943Sluigi cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; 248998943Sluigi cmd->arg1 = arg; 249098943Sluigi} 249198943Sluigi 249298943Sluigi/* 249398943Sluigi * Fetch and add the MAC address and type, with masks. This generates one or 249498943Sluigi * two microinstructions, and returns the pointer to the last one. 249598943Sluigi */ 249698943Sluigistatic ipfw_insn * 2497204591Sluigiadd_mac(ipfw_insn *cmd, char *av[]) 249898943Sluigi{ 2499102087Sluigi ipfw_insn_mac *mac; 250098943Sluigi 2501204591Sluigi if ( ( av[0] == NULL ) || ( av[1] == NULL ) ) 2502102098Sluigi errx(EX_DATAERR, "MAC dst src"); 250398943Sluigi 250498943Sluigi cmd->opcode = O_MACADDR2; 250598943Sluigi cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); 250698943Sluigi 250798943Sluigi mac = (ipfw_insn_mac *)cmd; 2508101978Sluigi get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ 2509169424Smaxim get_mac_addr_mask(av[1], &(mac->addr[ETHER_ADDR_LEN]), 2510169424Smaxim &(mac->mask[ETHER_ADDR_LEN])); /* src */ 2511102087Sluigi return cmd; 2512102087Sluigi} 251398943Sluigi 2514102087Sluigistatic ipfw_insn * 2515204591Sluigiadd_mactype(ipfw_insn *cmd, char *av) 2516102087Sluigi{ 2517204591Sluigi if (!av) 2518102087Sluigi errx(EX_DATAERR, "missing MAC type"); 2519102087Sluigi if (strcmp(av, "any") != 0) { /* we have a non-null type */ 2520102087Sluigi fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE); 252198943Sluigi cmd->opcode = O_MAC_TYPE; 2522102087Sluigi return cmd; 2523102087Sluigi } else 2524102087Sluigi return NULL; 2525102087Sluigi} 252698943Sluigi 2527102087Sluigistatic ipfw_insn * 2528152923Sumeadd_proto0(ipfw_insn *cmd, char *av, u_char *protop) 2529102087Sluigi{ 2530102087Sluigi struct protoent *pe; 2531152923Sume char *ep; 2532152923Sume int proto; 2533102087Sluigi 2534156315Sume proto = strtol(av, &ep, 10); 2535156315Sume if (*ep != '\0' || proto <= 0) { 2536152923Sume if ((pe = getprotobyname(av)) == NULL) 2537152923Sume return NULL; 2538152923Sume proto = pe->p_proto; 2539152923Sume } 2540145246Sbrooks 2541152923Sume fill_cmd(cmd, O_PROTO, 0, proto); 2542152923Sume *protop = proto; 2543152923Sume return cmd; 2544152923Sume} 2545152923Sume 2546152923Sumestatic ipfw_insn * 2547152923Sumeadd_proto(ipfw_insn *cmd, char *av, u_char *protop) 2548152923Sume{ 2549152923Sume u_char proto = IPPROTO_IP; 2550152923Sume 2551156315Sume if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) 2552146894Smlaier ; /* do not set O_IP4 nor O_IP6 */ 2553152923Sume else if (strcmp(av, "ip4") == 0) 2554152923Sume /* explicit "just IPv4" rule */ 2555152923Sume fill_cmd(cmd, O_IP4, 0, 0); 2556152923Sume else if (strcmp(av, "ip6") == 0) { 2557152923Sume /* explicit "just IPv6" rule */ 2558152923Sume proto = IPPROTO_IPV6; 2559152923Sume fill_cmd(cmd, O_IP6, 0, 0); 2560152923Sume } else 2561152923Sume return add_proto0(cmd, av, protop); 2562152923Sume 2563152923Sume *protop = proto; 2564152923Sume return cmd; 2565152923Sume} 2566152923Sume 2567152923Sumestatic ipfw_insn * 2568152923Sumeadd_proto_compat(ipfw_insn *cmd, char *av, u_char *protop) 2569152923Sume{ 2570152923Sume u_char proto = IPPROTO_IP; 2571152923Sume 2572152923Sume if (_substrcmp(av, "all") == 0 || strcmp(av, "ip") == 0) 2573152923Sume ; /* do not set O_IP4 nor O_IP6 */ 2574146894Smlaier else if (strcmp(av, "ipv4") == 0 || strcmp(av, "ip4") == 0) 2575146894Smlaier /* explicit "just IPv4" rule */ 2576146894Smlaier fill_cmd(cmd, O_IP4, 0, 0); 2577146894Smlaier else if (strcmp(av, "ipv6") == 0 || strcmp(av, "ip6") == 0) { 2578146894Smlaier /* explicit "just IPv6" rule */ 2579152923Sume proto = IPPROTO_IPV6; 2580146894Smlaier fill_cmd(cmd, O_IP6, 0, 0); 2581152923Sume } else 2582152923Sume return add_proto0(cmd, av, protop); 2583145246Sbrooks 2584152923Sume *protop = proto; 258598943Sluigi return cmd; 258698943Sluigi} 258798943Sluigi 2588102087Sluigistatic ipfw_insn * 2589102087Sluigiadd_srcip(ipfw_insn *cmd, char *av) 2590102087Sluigi{ 2591102087Sluigi fill_ip((ipfw_insn_ip *)cmd, av); 2592102087Sluigi if (cmd->opcode == O_IP_DST_SET) /* set */ 2593102087Sluigi cmd->opcode = O_IP_SRC_SET; 2594130281Sru else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 2595130281Sru cmd->opcode = O_IP_SRC_LOOKUP; 2596102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 2597102087Sluigi cmd->opcode = O_IP_SRC_ME; 2598102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 2599102087Sluigi cmd->opcode = O_IP_SRC; 2600117328Sluigi else /* addr/mask */ 2601102087Sluigi cmd->opcode = O_IP_SRC_MASK; 2602102087Sluigi return cmd; 2603102087Sluigi} 2604102087Sluigi 2605102087Sluigistatic ipfw_insn * 2606102087Sluigiadd_dstip(ipfw_insn *cmd, char *av) 2607102087Sluigi{ 2608102087Sluigi fill_ip((ipfw_insn_ip *)cmd, av); 2609102087Sluigi if (cmd->opcode == O_IP_DST_SET) /* set */ 2610102087Sluigi ; 2611130281Sru else if (cmd->opcode == O_IP_DST_LOOKUP) /* table */ 2612130281Sru ; 2613102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 2614102087Sluigi cmd->opcode = O_IP_DST_ME; 2615102087Sluigi else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 2616102087Sluigi cmd->opcode = O_IP_DST; 2617117328Sluigi else /* addr/mask */ 2618102087Sluigi cmd->opcode = O_IP_DST_MASK; 2619102087Sluigi return cmd; 2620102087Sluigi} 2621102087Sluigi 2622102087Sluigistatic ipfw_insn * 2623102087Sluigiadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode) 2624102087Sluigi{ 2625204591Sluigi /* XXX "any" is trapped before. Perhaps "to" */ 2626140271Sbrooks if (_substrcmp(av, "any") == 0) { 2627102087Sluigi return NULL; 2628102087Sluigi } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) { 2629102087Sluigi /* XXX todo: check that we have a protocol with ports */ 2630102087Sluigi cmd->opcode = opcode; 2631102087Sluigi return cmd; 2632102087Sluigi } 2633102087Sluigi return NULL; 2634102087Sluigi} 2635102087Sluigi 2636145246Sbrooksstatic ipfw_insn * 2637145246Sbrooksadd_src(ipfw_insn *cmd, char *av, u_char proto) 2638145246Sbrooks{ 2639145246Sbrooks struct in6_addr a; 2640158553Smlaier char *host, *ch; 2641158553Smlaier ipfw_insn *ret = NULL; 2642145246Sbrooks 2643158553Smlaier if ((host = strdup(av)) == NULL) 2644158553Smlaier return NULL; 2645158553Smlaier if ((ch = strrchr(host, '/')) != NULL) 2646158553Smlaier *ch = '\0'; 2647158553Smlaier 2648145246Sbrooks if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || 2649204591Sluigi inet_pton(AF_INET6, host, &a) == 1) 2650158553Smlaier ret = add_srcip6(cmd, av); 2651145246Sbrooks /* XXX: should check for IPv4, not !IPv6 */ 2652161483Sdwmalone if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || 2653204591Sluigi inet_pton(AF_INET6, host, &a) != 1)) 2654158553Smlaier ret = add_srcip(cmd, av); 2655161483Sdwmalone if (ret == NULL && strcmp(av, "any") != 0) 2656158553Smlaier ret = cmd; 2657145246Sbrooks 2658158553Smlaier free(host); 2659158553Smlaier return ret; 2660145246Sbrooks} 2661145246Sbrooks 2662145246Sbrooksstatic ipfw_insn * 2663145246Sbrooksadd_dst(ipfw_insn *cmd, char *av, u_char proto) 2664145246Sbrooks{ 2665145246Sbrooks struct in6_addr a; 2666158553Smlaier char *host, *ch; 2667158553Smlaier ipfw_insn *ret = NULL; 2668145246Sbrooks 2669158553Smlaier if ((host = strdup(av)) == NULL) 2670158553Smlaier return NULL; 2671158553Smlaier if ((ch = strrchr(host, '/')) != NULL) 2672158553Smlaier *ch = '\0'; 2673158553Smlaier 2674145246Sbrooks if (proto == IPPROTO_IPV6 || strcmp(av, "me6") == 0 || 2675204591Sluigi inet_pton(AF_INET6, host, &a) == 1) 2676158553Smlaier ret = add_dstip6(cmd, av); 2677145246Sbrooks /* XXX: should check for IPv4, not !IPv6 */ 2678161483Sdwmalone if (ret == NULL && (proto == IPPROTO_IP || strcmp(av, "me") == 0 || 2679204591Sluigi inet_pton(AF_INET6, host, &a) != 1)) 2680158553Smlaier ret = add_dstip(cmd, av); 2681161483Sdwmalone if (ret == NULL && strcmp(av, "any") != 0) 2682158553Smlaier ret = cmd; 2683145246Sbrooks 2684158553Smlaier free(host); 2685158553Smlaier return ret; 2686145246Sbrooks} 2687145246Sbrooks 268898943Sluigi/* 268998943Sluigi * Parse arguments and assemble the microinstructions which make up a rule. 269098943Sluigi * Rules are added into the 'rulebuf' and then copied in the correct order 269198943Sluigi * into the actual rule. 269298943Sluigi * 2693136071Sgreen * The syntax for a rule starts with the action, followed by 2694136071Sgreen * optional action parameters, and the various match patterns. 2695108533Sschweikh * In the assembled microcode, the first opcode must be an O_PROBE_STATE 269698943Sluigi * (generated if the rule includes a keep-state option), then the 2697136071Sgreen * various match patterns, log/altq actions, and the actual action. 2698106505Smaxim * 269998943Sluigi */ 2700187767Sluigivoid 2701204591Sluigiipfw_add(char *av[]) 270298943Sluigi{ 270398943Sluigi /* 270498943Sluigi * rules are added into the 'rulebuf' and then copied in 270598943Sluigi * the correct order into the actual rule. 270698943Sluigi * Some things that need to go out of order (prob, action etc.) 270798943Sluigi * go into actbuf[]. 270898943Sluigi */ 2709117328Sluigi static uint32_t rulebuf[255], actbuf[255], cmdbuf[255]; 271098943Sluigi 2711117469Sluigi ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; 2712102087Sluigi ipfw_insn *first_cmd; /* first match pattern */ 271398943Sluigi 271498943Sluigi struct ip_fw *rule; 271598943Sluigi 271698943Sluigi /* 271798943Sluigi * various flags used to record that we entered some fields. 271898943Sluigi */ 2719101116Sluigi ipfw_insn *have_state = NULL; /* check-state or keep-state */ 2720158879Soleg ipfw_insn *have_log = NULL, *have_altq = NULL, *have_tag = NULL; 2721134475Smaxim size_t len; 272298943Sluigi 272398943Sluigi int i; 272498943Sluigi 272598943Sluigi int open_par = 0; /* open parenthesis ( */ 272698943Sluigi 272798943Sluigi /* proto is here because it is used to fetch ports */ 272898943Sluigi u_char proto = IPPROTO_IP; /* default protocol */ 272998943Sluigi 2730107289Sluigi double match_prob = 1; /* match probability, default is always match */ 2731107289Sluigi 273298943Sluigi bzero(actbuf, sizeof(actbuf)); /* actions go here */ 273398943Sluigi bzero(cmdbuf, sizeof(cmdbuf)); 273498943Sluigi bzero(rulebuf, sizeof(rulebuf)); 273598943Sluigi 273698943Sluigi rule = (struct ip_fw *)rulebuf; 273798943Sluigi cmd = (ipfw_insn *)cmdbuf; 273898943Sluigi action = (ipfw_insn *)actbuf; 273998943Sluigi 2740204591Sluigi av++; 274198943Sluigi 274298943Sluigi /* [rule N] -- Rule number optional */ 2743204591Sluigi if (av[0] && isdigit(**av)) { 274498943Sluigi rule->rulenum = atoi(*av); 274598943Sluigi av++; 274698943Sluigi } 274798943Sluigi 2748117655Sluigi /* [set N] -- set number (0..RESVD_SET), optional */ 2749205631Sluigi if (av[0] && av[1] && _substrcmp(*av, "set") == 0) { 2750101628Sluigi int set = strtoul(av[1], NULL, 10); 2751117655Sluigi if (set < 0 || set > RESVD_SET) 2752101628Sluigi errx(EX_DATAERR, "illegal set %s", av[1]); 2753101628Sluigi rule->set = set; 2754204591Sluigi av += 2; 2755101628Sluigi } 2756101628Sluigi 275798943Sluigi /* [prob D] -- match probability, optional */ 2758204591Sluigi if (av[0] && av[1] && _substrcmp(*av, "prob") == 0) { 2759107289Sluigi match_prob = strtod(av[1], NULL); 276098943Sluigi 2761107289Sluigi if (match_prob <= 0 || match_prob > 1) 276298943Sluigi errx(EX_DATAERR, "illegal match prob. %s", av[1]); 2763204591Sluigi av += 2; 276498943Sluigi } 276598943Sluigi 276698943Sluigi /* action -- mandatory */ 276798943Sluigi NEED1("missing action"); 276898943Sluigi i = match_token(rule_actions, *av); 2769204591Sluigi av++; 277098943Sluigi action->len = 1; /* default */ 277198943Sluigi switch(i) { 277298943Sluigi case TOK_CHECKSTATE: 2773101116Sluigi have_state = action; 277498943Sluigi action->opcode = O_CHECK_STATE; 277598943Sluigi break; 277698943Sluigi 277798943Sluigi case TOK_ACCEPT: 277898943Sluigi action->opcode = O_ACCEPT; 277998943Sluigi break; 278098943Sluigi 278198943Sluigi case TOK_DENY: 278298943Sluigi action->opcode = O_DENY; 278399475Sluigi action->arg1 = 0; 278498943Sluigi break; 278598943Sluigi 278699475Sluigi case TOK_REJECT: 278799475Sluigi action->opcode = O_REJECT; 278899475Sluigi action->arg1 = ICMP_UNREACH_HOST; 278999475Sluigi break; 279099475Sluigi 279199475Sluigi case TOK_RESET: 279299475Sluigi action->opcode = O_REJECT; 279399475Sluigi action->arg1 = ICMP_REJECT_RST; 279499475Sluigi break; 279599475Sluigi 2796149020Sbz case TOK_RESET6: 2797149020Sbz action->opcode = O_UNREACH6; 2798149020Sbz action->arg1 = ICMP6_UNREACH_RST; 2799149020Sbz break; 2800149020Sbz 280199475Sluigi case TOK_UNREACH: 280299475Sluigi action->opcode = O_REJECT; 280399475Sluigi NEED1("missing reject code"); 280499475Sluigi fill_reject_code(&action->arg1, *av); 2805204591Sluigi av++; 280699475Sluigi break; 280799475Sluigi 2808149020Sbz case TOK_UNREACH6: 2809149020Sbz action->opcode = O_UNREACH6; 2810149020Sbz NEED1("missing unreach code"); 2811149020Sbz fill_unreach6_code(&action->arg1, *av); 2812204591Sluigi av++; 2813149020Sbz break; 2814149020Sbz 281598943Sluigi case TOK_COUNT: 281698943Sluigi action->opcode = O_COUNT; 281798943Sluigi break; 281898943Sluigi 2819176517Spiso case TOK_NAT: 2820223080Sae action->opcode = O_NAT; 2821223080Sae action->len = F_INSN_SIZE(ipfw_insn_nat); 2822223080Sae if (_substrcmp(*av, "global") == 0) { 2823223080Sae action->arg1 = 0; 2824223080Sae av++; 2825223080Sae break; 2826223080Sae } else 2827223080Sae goto chkarg; 2828178888Sjulian 282998943Sluigi case TOK_QUEUE: 2830153374Sglebius action->opcode = O_QUEUE; 2831153374Sglebius goto chkarg; 283298943Sluigi case TOK_PIPE: 2833153374Sglebius action->opcode = O_PIPE; 2834153374Sglebius goto chkarg; 283598943Sluigi case TOK_SKIPTO: 2836153374Sglebius action->opcode = O_SKIPTO; 2837153374Sglebius goto chkarg; 2838153374Sglebius case TOK_NETGRAPH: 2839153374Sglebius action->opcode = O_NETGRAPH; 2840153374Sglebius goto chkarg; 2841153374Sglebius case TOK_NGTEE: 2842153374Sglebius action->opcode = O_NGTEE; 2843153374Sglebius goto chkarg; 284498943Sluigi case TOK_DIVERT: 2845153374Sglebius action->opcode = O_DIVERT; 2846153374Sglebius goto chkarg; 284798943Sluigi case TOK_TEE: 2848153374Sglebius action->opcode = O_TEE; 2849223666Sae goto chkarg; 2850223666Sae case TOK_CALL: 2851223666Sae action->opcode = O_CALLRETURN; 2852220804Sglebiuschkarg: 2853204591Sluigi if (!av[0]) 2854153374Sglebius errx(EX_USAGE, "missing argument for %s", *(av - 1)); 2855153374Sglebius if (isdigit(**av)) { 2856153374Sglebius action->arg1 = strtoul(*av, NULL, 10); 2857153374Sglebius if (action->arg1 <= 0 || action->arg1 >= IP_FW_TABLEARG) 2858153374Sglebius errx(EX_DATAERR, "illegal argument for %s", 2859153374Sglebius *(av - 1)); 2860187762Sluigi } else if (_substrcmp(*av, "tablearg") == 0) { 2861153374Sglebius action->arg1 = IP_FW_TABLEARG; 2862153374Sglebius } else if (i == TOK_DIVERT || i == TOK_TEE) { 286398943Sluigi struct servent *s; 286498943Sluigi setservent(1); 286598943Sluigi s = getservbyname(av[0], "divert"); 286698943Sluigi if (s != NULL) 286798943Sluigi action->arg1 = ntohs(s->s_port); 286898943Sluigi else 286998943Sluigi errx(EX_DATAERR, "illegal divert/tee port"); 2870153374Sglebius } else 2871153374Sglebius errx(EX_DATAERR, "illegal argument for %s", *(av - 1)); 2872204591Sluigi av++; 287398943Sluigi break; 287498943Sluigi 287598943Sluigi case TOK_FORWARD: { 2876225044Sbz /* 2877225044Sbz * Locate the address-port separator (':' or ','). 2878225044Sbz * Could be one of the following: 2879225044Sbz * hostname:port 2880225044Sbz * IPv4 a.b.c.d,port 2881225044Sbz * IPv4 a.b.c.d:port 2882225044Sbz * IPv6 w:x:y::z,port 2883225044Sbz * The ':' can only be used with hostname and IPv4 address. 2884225044Sbz * XXX-BZ Should we also support [w:x:y::z]:port? 2885225044Sbz */ 2886225044Sbz struct sockaddr_storage result; 2887225044Sbz struct addrinfo *res; 288898943Sluigi char *s, *end; 2889225044Sbz int family; 2890225044Sbz u_short port_number; 289198943Sluigi 289298943Sluigi NEED1("missing forward address[:port]"); 289398943Sluigi 2894188005Sluigi /* 289598943Sluigi * locate the address-port separator (':' or ',') 289698943Sluigi */ 2897225044Sbz s = strchr(*av, ','); 2898225044Sbz if (s == NULL) { 2899225044Sbz /* Distinguish between IPv4:port and IPv6 cases. */ 2900225044Sbz s = strchr(*av, ':'); 2901225044Sbz if (s && strchr(s+1, ':')) 2902225044Sbz s = NULL; /* no port */ 2903225044Sbz } 2904225044Sbz 2905225044Sbz port_number = 0; 290698943Sluigi if (s != NULL) { 2907225044Sbz /* Terminate host portion and set s to start of port. */ 290898943Sluigi *(s++) = '\0'; 290998943Sluigi i = strtoport(s, &end, 0 /* base */, 0 /* proto */); 291098943Sluigi if (s == end) 291198943Sluigi errx(EX_DATAERR, 291298943Sluigi "illegal forwarding port ``%s''", s); 2913225044Sbz port_number = (u_short)i; 291498943Sluigi } 2915225044Sbz 2916225044Sbz if (_substrcmp(*av, "tablearg") == 0) { 2917225044Sbz family = PF_INET; 2918225044Sbz ((struct sockaddr_in*)&result)->sin_addr.s_addr = 2919225044Sbz INADDR_ANY; 2920225044Sbz } else { 2921225044Sbz /* 2922225044Sbz * Resolve the host name or address to a family and a 2923225044Sbz * network representation of the addres. 2924225044Sbz */ 2925225044Sbz if (getaddrinfo(*av, NULL, NULL, &res)) 2926225044Sbz errx(EX_DATAERR, NULL); 2927225044Sbz /* Just use the first host in the answer. */ 2928225044Sbz family = res->ai_family; 2929225044Sbz memcpy(&result, res->ai_addr, res->ai_addrlen); 2930225044Sbz freeaddrinfo(res); 2931225044Sbz } 2932225044Sbz 2933225044Sbz if (family == PF_INET) { 2934225044Sbz ipfw_insn_sa *p = (ipfw_insn_sa *)action; 2935225044Sbz 2936225044Sbz action->opcode = O_FORWARD_IP; 2937225044Sbz action->len = F_INSN_SIZE(ipfw_insn_sa); 2938225044Sbz 2939225044Sbz /* 2940225044Sbz * In the kernel we assume AF_INET and use only 2941225044Sbz * sin_port and sin_addr. Remember to set sin_len as 2942225044Sbz * the routing code seems to use it too. 2943225044Sbz */ 2944225044Sbz p->sa.sin_len = sizeof(struct sockaddr_in); 2945225044Sbz p->sa.sin_family = AF_INET; 2946225044Sbz p->sa.sin_port = port_number; 2947225044Sbz p->sa.sin_addr.s_addr = 2948225044Sbz ((struct sockaddr_in *)&result)->sin_addr.s_addr; 2949225044Sbz } else if (family == PF_INET6) { 2950225044Sbz ipfw_insn_sa6 *p = (ipfw_insn_sa6 *)action; 2951225044Sbz 2952225044Sbz action->opcode = O_FORWARD_IP6; 2953225044Sbz action->len = F_INSN_SIZE(ipfw_insn_sa6); 2954225044Sbz 2955225044Sbz p->sa.sin6_len = sizeof(struct sockaddr_in6); 2956225044Sbz p->sa.sin6_family = AF_INET6; 2957225044Sbz p->sa.sin6_port = port_number; 2958225044Sbz p->sa.sin6_flowinfo = 0; 2959225044Sbz p->sa.sin6_scope_id = 0; 2960225044Sbz /* No table support for v6 yet. */ 2961225044Sbz bcopy(&((struct sockaddr_in6*)&result)->sin6_addr, 2962225044Sbz &p->sa.sin6_addr, sizeof(p->sa.sin6_addr)); 2963225044Sbz } else { 2964225044Sbz errx(EX_DATAERR, "Invalid address family in forward action"); 2965225044Sbz } 2966204591Sluigi av++; 296798943Sluigi break; 2968161424Sjulian } 2969117469Sluigi case TOK_COMMENT: 2970117469Sluigi /* pretend it is a 'count' rule followed by the comment */ 2971117469Sluigi action->opcode = O_COUNT; 2972204591Sluigi av--; /* go back... */ 2973117469Sluigi break; 2974178888Sjulian 2975178888Sjulian case TOK_SETFIB: 2976178888Sjulian { 2977178888Sjulian int numfibs; 2978178916Sjulian size_t intsize = sizeof(int); 2979178888Sjulian 2980178888Sjulian action->opcode = O_SETFIB; 2981222473Sae NEED1("missing fib number"); 2982222473Sae if (_substrcmp(*av, "tablearg") == 0) { 2983222473Sae action->arg1 = IP_FW_TABLEARG; 2984222473Sae } else { 2985222473Sae action->arg1 = strtoul(*av, NULL, 10); 2986222473Sae if (sysctlbyname("net.fibs", &numfibs, &intsize, 2987222473Sae NULL, 0) == -1) 2988222473Sae errx(EX_DATAERR, "fibs not suported.\n"); 2989222473Sae if (action->arg1 >= numfibs) /* Temporary */ 2990222473Sae errx(EX_DATAERR, "fib too large.\n"); 2991222473Sae } 2992222473Sae av++; 2993222473Sae break; 2994178888Sjulian } 2995190633Spiso 2996190633Spiso case TOK_REASS: 2997190633Spiso action->opcode = O_REASS; 2998190633Spiso break; 2999220804Sglebius 3000223666Sae case TOK_RETURN: 3001223666Sae fill_cmd(action, O_CALLRETURN, F_NOT, 0); 3002223666Sae break; 3003223666Sae 300498943Sluigi default: 3005102087Sluigi errx(EX_DATAERR, "invalid action %s\n", av[-1]); 300698943Sluigi } 300798943Sluigi action = next_cmd(action); 300898943Sluigi 300998943Sluigi /* 3010136071Sgreen * [altq queuename] -- altq tag, optional 301198943Sluigi * [log [logamount N]] -- log, optional 301298943Sluigi * 3013136071Sgreen * If they exist, it go first in the cmdbuf, but then it is 301498943Sluigi * skipped in the copy section to the end of the buffer. 301598943Sluigi */ 3016204591Sluigi while (av[0] != NULL && (i = match_token(rule_action_params, *av)) != -1) { 3017204591Sluigi av++; 3018136071Sgreen switch (i) { 3019136071Sgreen case TOK_LOG: 3020136071Sgreen { 3021136071Sgreen ipfw_insn_log *c = (ipfw_insn_log *)cmd; 3022136071Sgreen int l; 302398943Sluigi 3024136071Sgreen if (have_log) 3025136071Sgreen errx(EX_DATAERR, 3026136071Sgreen "log cannot be specified more than once"); 3027136071Sgreen have_log = (ipfw_insn *)c; 3028136071Sgreen cmd->len = F_INSN_SIZE(ipfw_insn_log); 3029136071Sgreen cmd->opcode = O_LOG; 3030204591Sluigi if (av[0] && _substrcmp(*av, "logamount") == 0) { 3031204591Sluigi av++; 3032136071Sgreen NEED1("logamount requires argument"); 3033136071Sgreen l = atoi(*av); 3034136071Sgreen if (l < 0) 3035136071Sgreen errx(EX_DATAERR, 3036136071Sgreen "logamount must be positive"); 3037136071Sgreen c->max_log = l; 3038204591Sluigi av++; 3039136071Sgreen } else { 3040136071Sgreen len = sizeof(c->max_log); 3041136071Sgreen if (sysctlbyname("net.inet.ip.fw.verbose_limit", 3042136071Sgreen &c->max_log, &len, NULL, 0) == -1) 3043136071Sgreen errx(1, "sysctlbyname(\"%s\")", 3044136071Sgreen "net.inet.ip.fw.verbose_limit"); 3045136071Sgreen } 3046136071Sgreen } 3047136071Sgreen break; 3048136071Sgreen 3049204591Sluigi#ifndef NO_ALTQ 3050136071Sgreen case TOK_ALTQ: 3051136071Sgreen { 3052136071Sgreen ipfw_insn_altq *a = (ipfw_insn_altq *)cmd; 3053136071Sgreen 3054136071Sgreen NEED1("missing altq queue name"); 3055136071Sgreen if (have_altq) 3056136071Sgreen errx(EX_DATAERR, 3057136071Sgreen "altq cannot be specified more than once"); 3058136071Sgreen have_altq = (ipfw_insn *)a; 3059136071Sgreen cmd->len = F_INSN_SIZE(ipfw_insn_altq); 3060136071Sgreen cmd->opcode = O_ALTQ; 3061187983Sluigi a->qid = altq_name_to_qid(*av); 3062204591Sluigi av++; 3063136071Sgreen } 3064136071Sgreen break; 3065204591Sluigi#endif 3066136071Sgreen 3067158879Soleg case TOK_TAG: 3068159636Soleg case TOK_UNTAG: { 3069159636Soleg uint16_t tag; 3070159636Soleg 3071158879Soleg if (have_tag) 3072159636Soleg errx(EX_USAGE, "tag and untag cannot be " 3073159636Soleg "specified more than once"); 3074193516Sluigi GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, i, 3075182823Srik rule_action_params); 3076158879Soleg have_tag = cmd; 3077159636Soleg fill_cmd(cmd, O_TAG, (i == TOK_TAG) ? 0: F_NOT, tag); 3078204591Sluigi av++; 3079158879Soleg break; 3080159636Soleg } 3081158879Soleg 3082136071Sgreen default: 3083136071Sgreen abort(); 308498943Sluigi } 308598943Sluigi cmd = next_cmd(cmd); 308698943Sluigi } 308798943Sluigi 3088101116Sluigi if (have_state) /* must be a check-state, we are done */ 308998943Sluigi goto done; 309098943Sluigi 309198943Sluigi#define OR_START(target) \ 3092204591Sluigi if (av[0] && (*av[0] == '(' || *av[0] == '{')) { \ 309398943Sluigi if (open_par) \ 309498943Sluigi errx(EX_USAGE, "nested \"(\" not allowed\n"); \ 3095101641Sluigi prev = NULL; \ 309698943Sluigi open_par = 1; \ 309798943Sluigi if ( (av[0])[1] == '\0') { \ 3098204591Sluigi av++; \ 309998943Sluigi } else \ 310098943Sluigi (*av)++; \ 310198943Sluigi } \ 310298943Sluigi target: \ 310398943Sluigi 310498943Sluigi 310598943Sluigi#define CLOSE_PAR \ 310698943Sluigi if (open_par) { \ 3107204591Sluigi if (av[0] && ( \ 3108140271Sbrooks strcmp(*av, ")") == 0 || \ 3109140271Sbrooks strcmp(*av, "}") == 0)) { \ 3110101641Sluigi prev = NULL; \ 311198943Sluigi open_par = 0; \ 3112204591Sluigi av++; \ 311398943Sluigi } else \ 311498943Sluigi errx(EX_USAGE, "missing \")\"\n"); \ 311598943Sluigi } 3116106505Smaxim 311798943Sluigi#define NOT_BLOCK \ 3118204591Sluigi if (av[0] && _substrcmp(*av, "not") == 0) { \ 311998943Sluigi if (cmd->len & F_NOT) \ 312098943Sluigi errx(EX_USAGE, "double \"not\" not allowed\n"); \ 312198943Sluigi cmd->len |= F_NOT; \ 3122204591Sluigi av++; \ 312398943Sluigi } 312498943Sluigi 312598943Sluigi#define OR_BLOCK(target) \ 3126204591Sluigi if (av[0] && _substrcmp(*av, "or") == 0) { \ 312798943Sluigi if (prev == NULL || open_par == 0) \ 312898943Sluigi errx(EX_DATAERR, "invalid OR block"); \ 312998943Sluigi prev->len |= F_OR; \ 3130204591Sluigi av++; \ 313198943Sluigi goto target; \ 313298943Sluigi } \ 313398943Sluigi CLOSE_PAR; 313498943Sluigi 3135102087Sluigi first_cmd = cmd; 3136102098Sluigi 3137102098Sluigi#if 0 313898943Sluigi /* 3139102087Sluigi * MAC addresses, optional. 3140102087Sluigi * If we have this, we skip the part "proto from src to dst" 3141102087Sluigi * and jump straight to the option parsing. 3142102087Sluigi */ 3143102087Sluigi NOT_BLOCK; 3144102087Sluigi NEED1("missing protocol"); 3145140271Sbrooks if (_substrcmp(*av, "MAC") == 0 || 3146140271Sbrooks _substrcmp(*av, "mac") == 0) { 3147204591Sluigi av++; /* the "MAC" keyword */ 3148204591Sluigi add_mac(cmd, av); /* exits in case of errors */ 3149102087Sluigi cmd = next_cmd(cmd); 3150204591Sluigi av += 2; /* dst-mac and src-mac */ 3151102087Sluigi NOT_BLOCK; 3152102087Sluigi NEED1("missing mac type"); 3153204591Sluigi if (add_mactype(cmd, av[0])) 3154102087Sluigi cmd = next_cmd(cmd); 3155204591Sluigi av++; /* any or mac-type */ 3156102087Sluigi goto read_options; 3157102087Sluigi } 3158102098Sluigi#endif 3159102087Sluigi 3160102087Sluigi /* 316198943Sluigi * protocol, mandatory 316298943Sluigi */ 316398943Sluigi OR_START(get_proto); 316498943Sluigi NOT_BLOCK; 316598943Sluigi NEED1("missing protocol"); 3166152923Sume if (add_proto_compat(cmd, *av, &proto)) { 3167204591Sluigi av++; 3168147105Smlaier if (F_LEN(cmd) != 0) { 3169102087Sluigi prev = cmd; 3170102087Sluigi cmd = next_cmd(cmd); 3171102087Sluigi } 3172102098Sluigi } else if (first_cmd != cmd) { 3173116438Smaxim errx(EX_DATAERR, "invalid protocol ``%s''", *av); 3174102098Sluigi } else 3175102098Sluigi goto read_options; 317698943Sluigi OR_BLOCK(get_proto); 317798943Sluigi 317898943Sluigi /* 3179102087Sluigi * "from", mandatory 318098943Sluigi */ 3181204591Sluigi if ((av[0] == NULL) || _substrcmp(*av, "from") != 0) 318298943Sluigi errx(EX_USAGE, "missing ``from''"); 3183204591Sluigi av++; 318498943Sluigi 318598943Sluigi /* 318698943Sluigi * source IP, mandatory 318798943Sluigi */ 318898943Sluigi OR_START(source_ip); 318998943Sluigi NOT_BLOCK; /* optional "not" */ 319098943Sluigi NEED1("missing source address"); 3191145246Sbrooks if (add_src(cmd, *av, proto)) { 3192204591Sluigi av++; 3193102087Sluigi if (F_LEN(cmd) != 0) { /* ! any */ 3194102087Sluigi prev = cmd; 3195102087Sluigi cmd = next_cmd(cmd); 3196102087Sluigi } 3197145246Sbrooks } else 3198145246Sbrooks errx(EX_USAGE, "bad source address %s", *av); 319998943Sluigi OR_BLOCK(source_ip); 320098943Sluigi 320198943Sluigi /* 320298943Sluigi * source ports, optional 320398943Sluigi */ 320498943Sluigi NOT_BLOCK; /* optional "not" */ 3205204591Sluigi if ( av[0] != NULL ) { 3206140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3207102087Sluigi add_ports(cmd, *av, proto, O_IP_SRCPORT)) { 3208204591Sluigi av++; 3209102087Sluigi if (F_LEN(cmd) != 0) 3210102087Sluigi cmd = next_cmd(cmd); 3211101641Sluigi } 321298943Sluigi } 321398943Sluigi 321498943Sluigi /* 3215102087Sluigi * "to", mandatory 321698943Sluigi */ 3217204591Sluigi if ( (av[0] == NULL) || _substrcmp(*av, "to") != 0 ) 321898943Sluigi errx(EX_USAGE, "missing ``to''"); 3219204591Sluigi av++; 322098943Sluigi 322198943Sluigi /* 322298943Sluigi * destination, mandatory 322398943Sluigi */ 322498943Sluigi OR_START(dest_ip); 322598943Sluigi NOT_BLOCK; /* optional "not" */ 322698943Sluigi NEED1("missing dst address"); 3227145246Sbrooks if (add_dst(cmd, *av, proto)) { 3228204591Sluigi av++; 3229102087Sluigi if (F_LEN(cmd) != 0) { /* ! any */ 3230102087Sluigi prev = cmd; 3231102087Sluigi cmd = next_cmd(cmd); 3232102087Sluigi } 3233145246Sbrooks } else 3234145246Sbrooks errx( EX_USAGE, "bad destination address %s", *av); 323598943Sluigi OR_BLOCK(dest_ip); 323698943Sluigi 323798943Sluigi /* 323898943Sluigi * dest. ports, optional 323998943Sluigi */ 324098943Sluigi NOT_BLOCK; /* optional "not" */ 3241204591Sluigi if (av[0]) { 3242140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3243102087Sluigi add_ports(cmd, *av, proto, O_IP_DSTPORT)) { 3244204591Sluigi av++; 3245102087Sluigi if (F_LEN(cmd) != 0) 3246102087Sluigi cmd = next_cmd(cmd); 3247101641Sluigi } 324898943Sluigi } 324998943Sluigi 325098943Sluigiread_options: 3251204591Sluigi if (av[0] && first_cmd == cmd) { 3252102087Sluigi /* 3253102087Sluigi * nothing specified so far, store in the rule to ease 3254102087Sluigi * printout later. 3255102087Sluigi */ 3256102087Sluigi rule->_pad = 1; 3257102087Sluigi } 325898943Sluigi prev = NULL; 3259204591Sluigi while ( av[0] != NULL ) { 3260101641Sluigi char *s; 3261101641Sluigi ipfw_insn_u32 *cmd32; /* alias for cmd */ 326298943Sluigi 3263101641Sluigi s = *av; 3264101641Sluigi cmd32 = (ipfw_insn_u32 *)cmd; 3265101641Sluigi 326698943Sluigi if (*s == '!') { /* alternate syntax for NOT */ 326798943Sluigi if (cmd->len & F_NOT) 326898943Sluigi errx(EX_USAGE, "double \"not\" not allowed\n"); 326998943Sluigi cmd->len = F_NOT; 327098943Sluigi s++; 327198943Sluigi } 327298943Sluigi i = match_token(rule_options, s); 3273204591Sluigi av++; 327498943Sluigi switch(i) { 327598943Sluigi case TOK_NOT: 327698943Sluigi if (cmd->len & F_NOT) 327798943Sluigi errx(EX_USAGE, "double \"not\" not allowed\n"); 327898943Sluigi cmd->len = F_NOT; 327998943Sluigi break; 328098943Sluigi 328198943Sluigi case TOK_OR: 3282101641Sluigi if (open_par == 0 || prev == NULL) 328398943Sluigi errx(EX_USAGE, "invalid \"or\" block\n"); 328498943Sluigi prev->len |= F_OR; 328598943Sluigi break; 3286101641Sluigi 3287101641Sluigi case TOK_STARTBRACE: 3288101641Sluigi if (open_par) 3289101641Sluigi errx(EX_USAGE, "+nested \"(\" not allowed\n"); 3290101641Sluigi open_par = 1; 3291101641Sluigi break; 3292101641Sluigi 3293101641Sluigi case TOK_ENDBRACE: 3294101641Sluigi if (!open_par) 3295101641Sluigi errx(EX_USAGE, "+missing \")\"\n"); 3296101641Sluigi open_par = 0; 3297102087Sluigi prev = NULL; 3298220802Sglebius break; 3299101641Sluigi 330098943Sluigi case TOK_IN: 330198943Sluigi fill_cmd(cmd, O_IN, 0, 0); 330298943Sluigi break; 330398943Sluigi 330498943Sluigi case TOK_OUT: 330598943Sluigi cmd->len ^= F_NOT; /* toggle F_NOT */ 330698943Sluigi fill_cmd(cmd, O_IN, 0, 0); 330798943Sluigi break; 330898943Sluigi 3309136073Sgreen case TOK_DIVERTED: 3310136073Sgreen fill_cmd(cmd, O_DIVERTED, 0, 3); 3311136073Sgreen break; 3312136073Sgreen 3313136073Sgreen case TOK_DIVERTEDLOOPBACK: 3314136073Sgreen fill_cmd(cmd, O_DIVERTED, 0, 1); 3315136073Sgreen break; 3316136073Sgreen 3317136073Sgreen case TOK_DIVERTEDOUTPUT: 3318136073Sgreen fill_cmd(cmd, O_DIVERTED, 0, 2); 3319136073Sgreen break; 3320136073Sgreen 332198943Sluigi case TOK_FRAG: 332298943Sluigi fill_cmd(cmd, O_FRAG, 0, 0); 332398943Sluigi break; 332498943Sluigi 332598943Sluigi case TOK_LAYER2: 332698943Sluigi fill_cmd(cmd, O_LAYER2, 0, 0); 332798943Sluigi break; 332898943Sluigi 332998943Sluigi case TOK_XMIT: 333098943Sluigi case TOK_RECV: 333198943Sluigi case TOK_VIA: 333298943Sluigi NEED1("recv, xmit, via require interface name" 333398943Sluigi " or address"); 333498943Sluigi fill_iface((ipfw_insn_if *)cmd, av[0]); 3335204591Sluigi av++; 333698943Sluigi if (F_LEN(cmd) == 0) /* not a valid address */ 333798943Sluigi break; 333898943Sluigi if (i == TOK_XMIT) 333998943Sluigi cmd->opcode = O_XMIT; 334098943Sluigi else if (i == TOK_RECV) 334198943Sluigi cmd->opcode = O_RECV; 334298943Sluigi else if (i == TOK_VIA) 334398943Sluigi cmd->opcode = O_VIA; 334498943Sluigi break; 334598943Sluigi 334699475Sluigi case TOK_ICMPTYPES: 334799475Sluigi NEED1("icmptypes requires list of types"); 334899475Sluigi fill_icmptypes((ipfw_insn_u32 *)cmd, *av); 3349204591Sluigi av++; 335099475Sluigi break; 3351220804Sglebius 3352145246Sbrooks case TOK_ICMP6TYPES: 3353145246Sbrooks NEED1("icmptypes requires list of types"); 3354145246Sbrooks fill_icmp6types((ipfw_insn_icmp6 *)cmd, *av); 3355204591Sluigi av++; 3356145246Sbrooks break; 335799475Sluigi 335898943Sluigi case TOK_IPTTL: 335998943Sluigi NEED1("ipttl requires TTL"); 3360116690Sluigi if (strpbrk(*av, "-,")) { 3361116690Sluigi if (!add_ports(cmd, *av, 0, O_IPTTL)) 3362116690Sluigi errx(EX_DATAERR, "invalid ipttl %s", *av); 3363116690Sluigi } else 3364116690Sluigi fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); 3365204591Sluigi av++; 336698943Sluigi break; 336798943Sluigi 336898943Sluigi case TOK_IPID: 3369116690Sluigi NEED1("ipid requires id"); 3370116690Sluigi if (strpbrk(*av, "-,")) { 3371116690Sluigi if (!add_ports(cmd, *av, 0, O_IPID)) 3372116690Sluigi errx(EX_DATAERR, "invalid ipid %s", *av); 3373116690Sluigi } else 3374116690Sluigi fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); 3375204591Sluigi av++; 337698943Sluigi break; 337798943Sluigi 337898943Sluigi case TOK_IPLEN: 337998943Sluigi NEED1("iplen requires length"); 3380116690Sluigi if (strpbrk(*av, "-,")) { 3381116690Sluigi if (!add_ports(cmd, *av, 0, O_IPLEN)) 3382116690Sluigi errx(EX_DATAERR, "invalid ip len %s", *av); 3383116690Sluigi } else 3384116690Sluigi fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); 3385204591Sluigi av++; 338698943Sluigi break; 338798943Sluigi 338898943Sluigi case TOK_IPVER: 338998943Sluigi NEED1("ipver requires version"); 339098943Sluigi fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0)); 3391204591Sluigi av++; 339298943Sluigi break; 339398943Sluigi 339499475Sluigi case TOK_IPPRECEDENCE: 339599475Sluigi NEED1("ipprecedence requires value"); 339699475Sluigi fill_cmd(cmd, O_IPPRECEDENCE, 0, 339799475Sluigi (strtoul(*av, NULL, 0) & 7) << 5); 3398204591Sluigi av++; 339999475Sluigi break; 340099475Sluigi 340198943Sluigi case TOK_IPOPTS: 340298943Sluigi NEED1("missing argument for ipoptions"); 3403101116Sluigi fill_flags(cmd, O_IPOPT, f_ipopts, *av); 3404204591Sluigi av++; 340598943Sluigi break; 340698943Sluigi 340799475Sluigi case TOK_IPTOS: 340899475Sluigi NEED1("missing argument for iptos"); 3409101116Sluigi fill_flags(cmd, O_IPTOS, f_iptos, *av); 3410204591Sluigi av++; 341199475Sluigi break; 341299475Sluigi 341398943Sluigi case TOK_UID: 341498943Sluigi NEED1("uid requires argument"); 341598943Sluigi { 341698943Sluigi char *end; 341798943Sluigi uid_t uid; 341898943Sluigi struct passwd *pwd; 341998943Sluigi 342098943Sluigi cmd->opcode = O_UID; 342198943Sluigi uid = strtoul(*av, &end, 0); 342298943Sluigi pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av); 342398943Sluigi if (pwd == NULL) 342498943Sluigi errx(EX_DATAERR, "uid \"%s\" nonexistent", *av); 3425106504Smaxim cmd32->d[0] = pwd->pw_uid; 3426135089Scsjp cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 3427204591Sluigi av++; 342898943Sluigi } 342998943Sluigi break; 343098943Sluigi 343198943Sluigi case TOK_GID: 343298943Sluigi NEED1("gid requires argument"); 343398943Sluigi { 343498943Sluigi char *end; 343598943Sluigi gid_t gid; 343698943Sluigi struct group *grp; 343798943Sluigi 343898943Sluigi cmd->opcode = O_GID; 343998943Sluigi gid = strtoul(*av, &end, 0); 344098943Sluigi grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av); 344198943Sluigi if (grp == NULL) 344298943Sluigi errx(EX_DATAERR, "gid \"%s\" nonexistent", *av); 3443106504Smaxim cmd32->d[0] = grp->gr_gid; 3444135089Scsjp cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 3445204591Sluigi av++; 344698943Sluigi } 344798943Sluigi break; 344898943Sluigi 3449133600Scsjp case TOK_JAIL: 3450133600Scsjp NEED1("jail requires argument"); 3451133600Scsjp { 3452133600Scsjp char *end; 3453133600Scsjp int jid; 3454133600Scsjp 3455133600Scsjp cmd->opcode = O_JAIL; 3456133600Scsjp jid = (int)strtol(*av, &end, 0); 3457133600Scsjp if (jid < 0 || *end != '\0') 3458133600Scsjp errx(EX_DATAERR, "jail requires prison ID"); 3459135554Scsjp cmd32->d[0] = (uint32_t)jid; 3460135089Scsjp cmd->len |= F_INSN_SIZE(ipfw_insn_u32); 3461204591Sluigi av++; 3462133600Scsjp } 3463133600Scsjp break; 3464133600Scsjp 346598943Sluigi case TOK_ESTAB: 346698943Sluigi fill_cmd(cmd, O_ESTAB, 0, 0); 346798943Sluigi break; 346898943Sluigi 346998943Sluigi case TOK_SETUP: 347098943Sluigi fill_cmd(cmd, O_TCPFLAGS, 0, 347198943Sluigi (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); 347298943Sluigi break; 347398943Sluigi 3474136075Sgreen case TOK_TCPDATALEN: 3475136075Sgreen NEED1("tcpdatalen requires length"); 3476136075Sgreen if (strpbrk(*av, "-,")) { 3477136075Sgreen if (!add_ports(cmd, *av, 0, O_TCPDATALEN)) 3478136075Sgreen errx(EX_DATAERR, "invalid tcpdata len %s", *av); 3479136075Sgreen } else 3480136075Sgreen fill_cmd(cmd, O_TCPDATALEN, 0, 3481136075Sgreen strtoul(*av, NULL, 0)); 3482204591Sluigi av++; 3483136075Sgreen break; 3484136075Sgreen 348598943Sluigi case TOK_TCPOPTS: 348698943Sluigi NEED1("missing argument for tcpoptions"); 348798943Sluigi fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av); 3488204591Sluigi av++; 348998943Sluigi break; 349098943Sluigi 349198943Sluigi case TOK_TCPSEQ: 349298943Sluigi case TOK_TCPACK: 349398943Sluigi NEED1("tcpseq/tcpack requires argument"); 349498943Sluigi cmd->len = F_INSN_SIZE(ipfw_insn_u32); 349598943Sluigi cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK; 349698943Sluigi cmd32->d[0] = htonl(strtoul(*av, NULL, 0)); 3497204591Sluigi av++; 349898943Sluigi break; 349998943Sluigi 350098943Sluigi case TOK_TCPWIN: 350198943Sluigi NEED1("tcpwin requires length"); 3502234278Sglebius if (strpbrk(*av, "-,")) { 3503234278Sglebius if (!add_ports(cmd, *av, 0, O_TCPWIN)) 3504234278Sglebius errx(EX_DATAERR, "invalid tcpwin len %s", *av); 3505234278Sglebius } else 3506234278Sglebius fill_cmd(cmd, O_TCPWIN, 0, 3507234278Sglebius strtoul(*av, NULL, 0)); 3508204591Sluigi av++; 350998943Sluigi break; 351098943Sluigi 351198943Sluigi case TOK_TCPFLAGS: 351298943Sluigi NEED1("missing argument for tcpflags"); 351398943Sluigi cmd->opcode = O_TCPFLAGS; 351498943Sluigi fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av); 3515204591Sluigi av++; 351698943Sluigi break; 351798943Sluigi 351898943Sluigi case TOK_KEEPSTATE: 3519101641Sluigi if (open_par) 3520101641Sluigi errx(EX_USAGE, "keep-state cannot be part " 3521101641Sluigi "of an or block"); 352299909Sluigi if (have_state) 3523101116Sluigi errx(EX_USAGE, "only one of keep-state " 352499909Sluigi "and limit is allowed"); 3525101116Sluigi have_state = cmd; 352698943Sluigi fill_cmd(cmd, O_KEEP_STATE, 0, 0); 352798943Sluigi break; 352898943Sluigi 3529159636Soleg case TOK_LIMIT: { 3530159636Soleg ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; 3531159636Soleg int val; 3532159636Soleg 3533101641Sluigi if (open_par) 3534159636Soleg errx(EX_USAGE, 3535159636Soleg "limit cannot be part of an or block"); 353699909Sluigi if (have_state) 3537168819Smaxim errx(EX_USAGE, "only one of keep-state and " 3538159636Soleg "limit is allowed"); 3539101116Sluigi have_state = cmd; 354098943Sluigi 354198943Sluigi cmd->len = F_INSN_SIZE(ipfw_insn_limit); 354298943Sluigi cmd->opcode = O_LIMIT; 3543159636Soleg c->limit_mask = c->conn_limit = 0; 354498943Sluigi 3545204591Sluigi while ( av[0] != NULL ) { 3546159636Soleg if ((val = match_token(limit_masks, *av)) <= 0) 354798943Sluigi break; 354898943Sluigi c->limit_mask |= val; 3549204591Sluigi av++; 355098943Sluigi } 3551159636Soleg 355298943Sluigi if (c->limit_mask == 0) 3553159636Soleg errx(EX_USAGE, "limit: missing limit mask"); 3554159636Soleg 3555193516Sluigi GET_UINT_ARG(c->conn_limit, IPFW_ARG_MIN, IPFW_ARG_MAX, 3556182823Srik TOK_LIMIT, rule_options); 3557159636Soleg 3558204591Sluigi av++; 355998943Sluigi break; 3560159636Soleg } 356198943Sluigi 3562102087Sluigi case TOK_PROTO: 3563102087Sluigi NEED1("missing protocol"); 3564145246Sbrooks if (add_proto(cmd, *av, &proto)) { 3565204591Sluigi av++; 3566102098Sluigi } else 3567116438Smaxim errx(EX_DATAERR, "invalid protocol ``%s''", 3568116438Smaxim *av); 3569102087Sluigi break; 3570106505Smaxim 3571102087Sluigi case TOK_SRCIP: 3572102087Sluigi NEED1("missing source IP"); 3573102087Sluigi if (add_srcip(cmd, *av)) { 3574204591Sluigi av++; 3575102087Sluigi } 3576102087Sluigi break; 3577102087Sluigi 3578102087Sluigi case TOK_DSTIP: 3579102087Sluigi NEED1("missing destination IP"); 3580102087Sluigi if (add_dstip(cmd, *av)) { 3581204591Sluigi av++; 3582102087Sluigi } 3583102087Sluigi break; 3584102087Sluigi 3585145246Sbrooks case TOK_SRCIP6: 3586145246Sbrooks NEED1("missing source IP6"); 3587145246Sbrooks if (add_srcip6(cmd, *av)) { 3588204591Sluigi av++; 3589145246Sbrooks } 3590145246Sbrooks break; 3591220804Sglebius 3592145246Sbrooks case TOK_DSTIP6: 3593145246Sbrooks NEED1("missing destination IP6"); 3594145246Sbrooks if (add_dstip6(cmd, *av)) { 3595204591Sluigi av++; 3596145246Sbrooks } 3597145246Sbrooks break; 3598145246Sbrooks 3599102087Sluigi case TOK_SRCPORT: 3600102087Sluigi NEED1("missing source port"); 3601140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3602102087Sluigi add_ports(cmd, *av, proto, O_IP_SRCPORT)) { 3603204591Sluigi av++; 3604102087Sluigi } else 3605102087Sluigi errx(EX_DATAERR, "invalid source port %s", *av); 3606102087Sluigi break; 3607102087Sluigi 3608102087Sluigi case TOK_DSTPORT: 3609102087Sluigi NEED1("missing destination port"); 3610140271Sbrooks if (_substrcmp(*av, "any") == 0 || 3611102087Sluigi add_ports(cmd, *av, proto, O_IP_DSTPORT)) { 3612204591Sluigi av++; 3613102087Sluigi } else 3614102087Sluigi errx(EX_DATAERR, "invalid destination port %s", 3615102087Sluigi *av); 3616102087Sluigi break; 3617102087Sluigi 3618102087Sluigi case TOK_MAC: 3619204591Sluigi if (add_mac(cmd, av)) 3620204591Sluigi av += 2; 3621102087Sluigi break; 3622102087Sluigi 3623102087Sluigi case TOK_MACTYPE: 3624102087Sluigi NEED1("missing mac type"); 3625204591Sluigi if (!add_mactype(cmd, *av)) 3626116438Smaxim errx(EX_DATAERR, "invalid mac type %s", *av); 3627204591Sluigi av++; 3628102087Sluigi break; 3629102087Sluigi 3630112250Scjc case TOK_VERREVPATH: 3631112250Scjc fill_cmd(cmd, O_VERREVPATH, 0, 0); 3632112250Scjc break; 3633116919Sluigi 3634128575Sandre case TOK_VERSRCREACH: 3635128575Sandre fill_cmd(cmd, O_VERSRCREACH, 0, 0); 3636128575Sandre break; 3637128575Sandre 3638133387Sandre case TOK_ANTISPOOF: 3639133387Sandre fill_cmd(cmd, O_ANTISPOOF, 0, 0); 3640133387Sandre break; 3641133387Sandre 3642117241Sluigi case TOK_IPSEC: 3643117241Sluigi fill_cmd(cmd, O_IPSEC, 0, 0); 3644117241Sluigi break; 3645117241Sluigi 3646145246Sbrooks case TOK_IPV6: 3647145246Sbrooks fill_cmd(cmd, O_IP6, 0, 0); 3648145246Sbrooks break; 3649145246Sbrooks 3650146894Smlaier case TOK_IPV4: 3651146894Smlaier fill_cmd(cmd, O_IP4, 0, 0); 3652146894Smlaier break; 3653146894Smlaier 3654145246Sbrooks case TOK_EXT6HDR: 3655145246Sbrooks fill_ext6hdr( cmd, *av ); 3656204591Sluigi av++; 3657145246Sbrooks break; 3658145246Sbrooks 3659145246Sbrooks case TOK_FLOWID: 3660145246Sbrooks if (proto != IPPROTO_IPV6 ) 3661145246Sbrooks errx( EX_USAGE, "flow-id filter is active " 3662145246Sbrooks "only for ipv6 protocol\n"); 3663145246Sbrooks fill_flow6( (ipfw_insn_u32 *) cmd, *av ); 3664204591Sluigi av++; 3665145246Sbrooks break; 3666145246Sbrooks 3667117469Sluigi case TOK_COMMENT: 3668204591Sluigi fill_comment(cmd, av); 3669204591Sluigi av[0]=NULL; 3670117469Sluigi break; 3671117469Sluigi 3672158879Soleg case TOK_TAGGED: 3673204591Sluigi if (av[0] && strpbrk(*av, "-,")) { 3674158879Soleg if (!add_ports(cmd, *av, 0, O_TAGGED)) 3675159636Soleg errx(EX_DATAERR, "tagged: invalid tag" 3676159636Soleg " list: %s", *av); 3677158879Soleg } 3678159636Soleg else { 3679159636Soleg uint16_t tag; 3680159636Soleg 3681193516Sluigi GET_UINT_ARG(tag, IPFW_ARG_MIN, IPFW_ARG_MAX, 3682182823Srik TOK_TAGGED, rule_options); 3683159636Soleg fill_cmd(cmd, O_TAGGED, 0, tag); 3684159636Soleg } 3685204591Sluigi av++; 3686158879Soleg break; 3687158879Soleg 3688178888Sjulian case TOK_FIB: 3689178888Sjulian NEED1("fib requires fib number"); 3690178888Sjulian fill_cmd(cmd, O_FIB, 0, strtoul(*av, NULL, 0)); 3691204591Sluigi av++; 3692178888Sjulian break; 3693215179Sluigi case TOK_SOCKARG: 3694215179Sluigi fill_cmd(cmd, O_SOCKARG, 0, 0); 3695215179Sluigi break; 3696178888Sjulian 3697200567Sluigi case TOK_LOOKUP: { 3698200567Sluigi ipfw_insn_u32 *c = (ipfw_insn_u32 *)cmd; 3699200567Sluigi char *p; 3700200567Sluigi int j; 3701200567Sluigi 3702205169Sluigi if (!av[0] || !av[1]) 3703200567Sluigi errx(EX_USAGE, "format: lookup argument tablenum"); 3704200567Sluigi cmd->opcode = O_IP_DST_LOOKUP; 3705200567Sluigi cmd->len |= F_INSN_SIZE(ipfw_insn) + 2; 3706200567Sluigi i = match_token(rule_options, *av); 3707200567Sluigi for (j = 0; lookup_key[j] >= 0 ; j++) { 3708200567Sluigi if (i == lookup_key[j]) 3709200567Sluigi break; 3710200567Sluigi } 3711200567Sluigi if (lookup_key[j] <= 0) 3712200567Sluigi errx(EX_USAGE, "format: cannot lookup on %s", *av); 3713223262Sbenl __PAST_END(c->d, 1) = j; // i converted to option 3714204591Sluigi av++; 3715200567Sluigi cmd->arg1 = strtoul(*av, &p, 0); 3716200567Sluigi if (p && *p) 3717200567Sluigi errx(EX_USAGE, "format: lookup argument tablenum"); 3718204591Sluigi av++; 3719200567Sluigi } 3720200567Sluigi break; 3721200567Sluigi 372298943Sluigi default: 372398943Sluigi errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); 372498943Sluigi } 372598943Sluigi if (F_LEN(cmd) > 0) { /* prepare to advance */ 372698943Sluigi prev = cmd; 372798943Sluigi cmd = next_cmd(cmd); 372898943Sluigi } 372998943Sluigi } 373098943Sluigi 373198943Sluigidone: 373298943Sluigi /* 373398943Sluigi * Now copy stuff into the rule. 373498943Sluigi * If we have a keep-state option, the first instruction 373598943Sluigi * must be a PROBE_STATE (which is generated here). 373698943Sluigi * If we have a LOG option, it was stored as the first command, 373798943Sluigi * and now must be moved to the top of the action part. 373898943Sluigi */ 373998943Sluigi dst = (ipfw_insn *)rule->cmd; 374098943Sluigi 374198943Sluigi /* 3742107289Sluigi * First thing to write into the command stream is the match probability. 3743107289Sluigi */ 3744107289Sluigi if (match_prob != 1) { /* 1 means always match */ 3745107289Sluigi dst->opcode = O_PROB; 3746107289Sluigi dst->len = 2; 3747107289Sluigi *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); 3748107289Sluigi dst += dst->len; 3749107289Sluigi } 3750107289Sluigi 3751107289Sluigi /* 375298943Sluigi * generate O_PROBE_STATE if necessary 375398943Sluigi */ 3754101116Sluigi if (have_state && have_state->opcode != O_CHECK_STATE) { 375598943Sluigi fill_cmd(dst, O_PROBE_STATE, 0, 0); 375698943Sluigi dst = next_cmd(dst); 375798943Sluigi } 3758158879Soleg 3759158879Soleg /* copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT, O_ALTQ, O_TAG */ 376098943Sluigi for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { 376198943Sluigi i = F_LEN(src); 376298943Sluigi 3763101116Sluigi switch (src->opcode) { 3764101116Sluigi case O_LOG: 3765101116Sluigi case O_KEEP_STATE: 3766101116Sluigi case O_LIMIT: 3767136071Sgreen case O_ALTQ: 3768158879Soleg case O_TAG: 3769101116Sluigi break; 3770101116Sluigi default: 3771117328Sluigi bcopy(src, dst, i * sizeof(uint32_t)); 377298943Sluigi dst += i; 377398943Sluigi } 377498943Sluigi } 377598943Sluigi 377698943Sluigi /* 3777101116Sluigi * put back the have_state command as last opcode 3778101116Sluigi */ 3779101295Sluigi if (have_state && have_state->opcode != O_CHECK_STATE) { 3780101116Sluigi i = F_LEN(have_state); 3781117328Sluigi bcopy(have_state, dst, i * sizeof(uint32_t)); 3782101116Sluigi dst += i; 3783101116Sluigi } 3784101116Sluigi /* 378598943Sluigi * start action section 378698943Sluigi */ 378798943Sluigi rule->act_ofs = dst - rule->cmd; 378898943Sluigi 3789158879Soleg /* put back O_LOG, O_ALTQ, O_TAG if necessary */ 3790136071Sgreen if (have_log) { 3791136071Sgreen i = F_LEN(have_log); 3792136071Sgreen bcopy(have_log, dst, i * sizeof(uint32_t)); 379398943Sluigi dst += i; 379498943Sluigi } 3795136071Sgreen if (have_altq) { 3796136071Sgreen i = F_LEN(have_altq); 3797136071Sgreen bcopy(have_altq, dst, i * sizeof(uint32_t)); 3798136071Sgreen dst += i; 3799136071Sgreen } 3800158879Soleg if (have_tag) { 3801158879Soleg i = F_LEN(have_tag); 3802158879Soleg bcopy(have_tag, dst, i * sizeof(uint32_t)); 3803158879Soleg dst += i; 3804158879Soleg } 380598943Sluigi /* 380698943Sluigi * copy all other actions 380798943Sluigi */ 380898943Sluigi for (src = (ipfw_insn *)actbuf; src != action; src += i) { 380998943Sluigi i = F_LEN(src); 3810117328Sluigi bcopy(src, dst, i * sizeof(uint32_t)); 381198943Sluigi dst += i; 381298943Sluigi } 381398943Sluigi 3814117328Sluigi rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd); 3815117469Sluigi i = (char *)dst - (char *)rule; 3816119740Stmm if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1) 381798943Sluigi err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD"); 3818187764Sluigi if (!co.do_quiet) 3819117469Sluigi show_ipfw(rule, 0, 0); 382098943Sluigi} 382198943Sluigi 3822187767Sluigi/* 3823187767Sluigi * clear the counters or the log counters. 3824187767Sluigi */ 3825187767Sluigivoid 3826187767Sluigiipfw_zero(int ac, char *av[], int optname /* 0 = IP_FW_ZERO, 1 = IP_FW_RESETLOG */) 382798943Sluigi{ 3828170923Smaxim uint32_t arg, saved_arg; 382998943Sluigi int failed = EX_OK; 3830170923Smaxim char const *errstr; 3831187767Sluigi char const *name = optname ? "RESETLOG" : "ZERO"; 383298943Sluigi 3833187767Sluigi optname = optname ? IP_FW_RESETLOG : IP_FW_ZERO; 3834187767Sluigi 383598943Sluigi av++; ac--; 383698943Sluigi 383798943Sluigi if (!ac) { 383898943Sluigi /* clear all entries */ 3839117328Sluigi if (do_cmd(optname, NULL, 0) < 0) 3840117328Sluigi err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name); 3841187764Sluigi if (!co.do_quiet) 3842117328Sluigi printf("%s.\n", optname == IP_FW_ZERO ? 3843117328Sluigi "Accounting cleared":"Logging counts reset"); 384498943Sluigi 384598943Sluigi return; 384698943Sluigi } 384798943Sluigi 384898943Sluigi while (ac) { 384998943Sluigi /* Rule number */ 385098943Sluigi if (isdigit(**av)) { 3851170923Smaxim arg = strtonum(*av, 0, 0xffff, &errstr); 3852170923Smaxim if (errstr) 3853170923Smaxim errx(EX_DATAERR, 3854170923Smaxim "invalid rule number %s\n", *av); 3855170923Smaxim saved_arg = arg; 3856187764Sluigi if (co.use_set) 3857187764Sluigi arg |= (1 << 24) | ((co.use_set - 1) << 16); 385898943Sluigi av++; 385998943Sluigi ac--; 3860170923Smaxim if (do_cmd(optname, &arg, sizeof(arg))) { 3861117328Sluigi warn("rule %u: setsockopt(IP_FW_%s)", 3862170923Smaxim saved_arg, name); 386398943Sluigi failed = EX_UNAVAILABLE; 3864187764Sluigi } else if (!co.do_quiet) 3865170923Smaxim printf("Entry %d %s.\n", saved_arg, 3866117328Sluigi optname == IP_FW_ZERO ? 3867117328Sluigi "cleared" : "logging count reset"); 386898943Sluigi } else { 386998943Sluigi errx(EX_USAGE, "invalid rule number ``%s''", *av); 387098943Sluigi } 387198943Sluigi } 387298943Sluigi if (failed != EX_OK) 387398943Sluigi exit(failed); 387498943Sluigi} 387598943Sluigi 3876187767Sluigivoid 3877187767Sluigiipfw_flush(int force) 387898943Sluigi{ 3879187764Sluigi int cmd = co.do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH; 388098943Sluigi 3881187764Sluigi if (!force && !co.do_quiet) { /* need to ask user */ 388298943Sluigi int c; 388398943Sluigi 388498943Sluigi printf("Are you sure? [yn] "); 388598943Sluigi fflush(stdout); 388698943Sluigi do { 388798943Sluigi c = toupper(getc(stdin)); 388898943Sluigi while (c != '\n' && getc(stdin) != '\n') 388998943Sluigi if (feof(stdin)) 389098943Sluigi return; /* and do not flush */ 389198943Sluigi } while (c != 'Y' && c != 'N'); 389298943Sluigi printf("\n"); 389398943Sluigi if (c == 'N') /* user said no */ 389498943Sluigi return; 389598943Sluigi } 3896204591Sluigi if (co.do_pipe) { 3897204591Sluigi dummynet_flush(); 3898204591Sluigi return; 3899204591Sluigi } 3900170923Smaxim /* `ipfw set N flush` - is the same that `ipfw delete set N` */ 3901187764Sluigi if (co.use_set) { 3902187764Sluigi uint32_t arg = ((co.use_set - 1) & 0xffff) | (1 << 24); 3903170923Smaxim if (do_cmd(IP_FW_DEL, &arg, sizeof(arg)) < 0) 3904170923Smaxim err(EX_UNAVAILABLE, "setsockopt(IP_FW_DEL)"); 3905170923Smaxim } else if (do_cmd(cmd, NULL, 0) < 0) 390698943Sluigi err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)", 3907187764Sluigi co.do_pipe ? "DUMMYNET" : "FW"); 3908187764Sluigi if (!co.do_quiet) 3909187764Sluigi printf("Flushed all %s.\n", co.do_pipe ? "pipes" : "rules"); 391098943Sluigi} 391198943Sluigi 3912117544Sluigi 3913234597Smelifarostatic void table_list(uint16_t num, int need_header); 3914183228Srik 3915117544Sluigi/* 3916130281Sru * This one handles all table-related commands 3917130281Sru * ipfw table N add addr[/masklen] [value] 3918130281Sru * ipfw table N delete addr[/masklen] 3919183407Srik * ipfw table {N | all} flush 3920183407Srik * ipfw table {N | all} list 3921130281Sru */ 3922187767Sluigivoid 3923187767Sluigiipfw_table_handler(int ac, char *av[]) 3924130281Sru{ 3925234597Smelifaro ipfw_table_xentry xent; 3926130281Sru int do_add; 3927183407Srik int is_all; 3928183241Srik size_t len; 3929130281Sru char *p; 3930234597Smelifaro uint32_t a, type, mask, addrlen; 3931183241Srik uint32_t tables_max; 3932130281Sru 3933183263Skeramida len = sizeof(tables_max); 3934183241Srik if (sysctlbyname("net.inet.ip.fw.tables_max", &tables_max, &len, 3935234597Smelifaro NULL, 0) == -1) 3936234597Smelifaro errx(1, "Can't determine maximum number of ipfw tables. " 3937234597Smelifaro "Perhaps you forgot to load ipfw module?"); 3938183241Srik 3939234597Smelifaro memset(&xent, 0, sizeof(xent)); 3940234597Smelifaro 3941130281Sru ac--; av++; 3942130281Sru if (ac && isdigit(**av)) { 3943234597Smelifaro xent.tbl = atoi(*av); 3944183407Srik is_all = 0; 3945130281Sru ac--; av++; 3946183407Srik } else if (ac && _substrcmp(*av, "all") == 0) { 3947234597Smelifaro xent.tbl = 0; 3948183407Srik is_all = 1; 3949183407Srik ac--; av++; 3950130281Sru } else 3951183407Srik errx(EX_USAGE, "table number or 'all' keyword required"); 3952234597Smelifaro if (xent.tbl >= tables_max) 3953183241Srik errx(EX_USAGE, "The table number exceeds the maximum allowed " 3954183241Srik "value (%d)", tables_max - 1); 3955130281Sru NEED1("table needs command"); 3956183407Srik if (is_all && _substrcmp(*av, "list") != 0 3957183407Srik && _substrcmp(*av, "flush") != 0) 3958183407Srik errx(EX_USAGE, "table number required"); 3959183407Srik 3960140271Sbrooks if (_substrcmp(*av, "add") == 0 || 3961140271Sbrooks _substrcmp(*av, "delete") == 0) { 3962130281Sru do_add = **av == 'a'; 3963130281Sru ac--; av++; 3964130281Sru if (!ac) 3965234597Smelifaro errx(EX_USAGE, "address required"); 3966234597Smelifaro /* 3967234597Smelifaro * Let's try to guess type by agrument. 3968234597Smelifaro * Possible types: 3969234597Smelifaro * 1) IPv4[/mask] 3970234597Smelifaro * 2) IPv6[/mask] 3971234597Smelifaro * 3) interface name 3972234597Smelifaro * 4) port ? 3973234597Smelifaro */ 3974234597Smelifaro type = 0; 3975234597Smelifaro if (ishexnumber(*av[0])) { 3976234597Smelifaro /* Remove / if exists */ 3977234597Smelifaro if ((p = strchr(*av, '/')) != NULL) { 3978234597Smelifaro *p = '\0'; 3979234597Smelifaro mask = atoi(p + 1); 3980234597Smelifaro } 3981234597Smelifaro 3982234597Smelifaro if (inet_pton(AF_INET, *av, &xent.k.addr6) == 1) { 3983234597Smelifaro type = IPFW_TABLE_CIDR; 3984234597Smelifaro if ((p != NULL) && (mask > 32)) 3985234597Smelifaro errx(EX_DATAERR, "bad IPv4 mask width: %s", p + 1); 3986234597Smelifaro xent.masklen = p ? mask : 32; 3987234597Smelifaro addrlen = sizeof(struct in_addr); 3988234597Smelifaro } else if (inet_pton(AF_INET6, *av, &xent.k.addr6) == 1) { 3989234597Smelifaro type = IPFW_TABLE_CIDR; 3990234597Smelifaro if ((p != NULL) && (mask > 128)) 3991234597Smelifaro errx(EX_DATAERR, "bad IPv6 mask width: %s", p + 1); 3992234597Smelifaro xent.masklen = p ? mask : 128; 3993234597Smelifaro addrlen = sizeof(struct in6_addr); 3994234597Smelifaro } 3995234597Smelifaro } 3996234597Smelifaro 3997234597Smelifaro if ((type == 0) && (strchr(*av, '.') == NULL)) { 3998234597Smelifaro /* Assume interface name. Copy significant data only */ 3999234597Smelifaro mask = MIN(strlen(*av), IF_NAMESIZE - 1); 4000234597Smelifaro memcpy(xent.k.iface, *av, mask); 4001234597Smelifaro /* Set mask to exact match */ 4002234597Smelifaro xent.masklen = 8 * IF_NAMESIZE; 4003234597Smelifaro type = IPFW_TABLE_INTERFACE; 4004234597Smelifaro addrlen = IF_NAMESIZE; 4005234597Smelifaro } 4006234597Smelifaro 4007234597Smelifaro if (type == 0) { 4008234597Smelifaro if (lookup_host(*av, (struct in_addr *)&xent.k.addr6) != 0) 4009234597Smelifaro errx(EX_NOHOST, "hostname ``%s'' unknown", *av); 4010234597Smelifaro xent.masklen = 32; 4011234597Smelifaro type = IPFW_TABLE_CIDR; 4012234597Smelifaro addrlen = sizeof(struct in_addr); 4013234597Smelifaro } 4014234597Smelifaro 4015234597Smelifaro xent.type = type; 4016234597Smelifaro xent.len = offsetof(ipfw_table_xentry, k) + addrlen; 4017234597Smelifaro 4018130281Sru ac--; av++; 4019161424Sjulian if (do_add && ac) { 4020161424Sjulian unsigned int tval; 4021161424Sjulian /* isdigit is a bit of a hack here.. */ 4022161424Sjulian if (strchr(*av, (int)'.') == NULL && isdigit(**av)) { 4023234597Smelifaro xent.value = strtoul(*av, NULL, 0); 4024161424Sjulian } else { 4025220802Sglebius if (lookup_host(*av, (struct in_addr *)&tval) == 0) { 4026161424Sjulian /* The value must be stored in host order * 4027161424Sjulian * so that the values < 65k can be distinguished */ 4028234597Smelifaro xent.value = ntohl(tval); 4029161424Sjulian } else { 4030161424Sjulian errx(EX_NOHOST, "hostname ``%s'' unknown", *av); 4031161424Sjulian } 4032161424Sjulian } 4033161424Sjulian } else 4034234597Smelifaro xent.value = 0; 4035234597Smelifaro if (do_setcmd3(do_add ? IP_FW_TABLE_XADD : IP_FW_TABLE_XDEL, 4036234597Smelifaro &xent, xent.len) < 0) { 4037155639Sjulian /* If running silent, don't bomb out on these errors. */ 4038187764Sluigi if (!(co.do_quiet && (errno == (do_add ? EEXIST : ESRCH)))) 4039155639Sjulian err(EX_OSERR, "setsockopt(IP_FW_TABLE_%s)", 4040234597Smelifaro do_add ? "XADD" : "XDEL"); 4041155639Sjulian /* In silent mode, react to a failed add by deleting */ 4042157332Sjulian if (do_add) { 4043234597Smelifaro do_setcmd3(IP_FW_TABLE_XDEL, &xent, xent.len); 4044234597Smelifaro if (do_setcmd3(IP_FW_TABLE_XADD, &xent, xent.len) < 0) 4045155639Sjulian err(EX_OSERR, 4046234597Smelifaro "setsockopt(IP_FW_TABLE_XADD)"); 4047157332Sjulian } 4048157335Sjulian } 4049140271Sbrooks } else if (_substrcmp(*av, "flush") == 0) { 4050234597Smelifaro a = is_all ? tables_max : (uint32_t)(xent.tbl + 1); 4051183407Srik do { 4052234597Smelifaro if (do_cmd(IP_FW_TABLE_FLUSH, &xent.tbl, 4053234597Smelifaro sizeof(xent.tbl)) < 0) 4054183407Srik err(EX_OSERR, "setsockopt(IP_FW_TABLE_FLUSH)"); 4055234597Smelifaro } while (++xent.tbl < a); 4056140271Sbrooks } else if (_substrcmp(*av, "list") == 0) { 4057234597Smelifaro a = is_all ? tables_max : (uint32_t)(xent.tbl + 1); 4058183407Srik do { 4059234597Smelifaro table_list(xent.tbl, is_all); 4060234597Smelifaro } while (++xent.tbl < a); 4061183228Srik } else 4062183228Srik errx(EX_USAGE, "invalid table command %s", *av); 4063183228Srik} 4064183205Srik 4065183228Srikstatic void 4066234597Smelifarotable_list(uint16_t num, int need_header) 4067183228Srik{ 4068234597Smelifaro ipfw_xtable *tbl; 4069234597Smelifaro ipfw_table_xentry *xent; 4070183228Srik socklen_t l; 4071234597Smelifaro uint32_t *a, sz, tval; 4072234597Smelifaro char tbuf[128]; 4073234597Smelifaro struct in6_addr *addr6; 4074234597Smelifaro ip_fw3_opheader *op3; 4075183205Srik 4076234597Smelifaro /* Prepend value with IP_FW3 header */ 4077234597Smelifaro l = sizeof(ip_fw3_opheader) + sizeof(uint32_t); 4078234597Smelifaro op3 = alloca(l); 4079234597Smelifaro /* Zero reserved fields */ 4080234597Smelifaro memset(op3, 0, sizeof(ip_fw3_opheader)); 4081234597Smelifaro a = (uint32_t *)(op3 + 1); 4082234597Smelifaro *a = num; 4083234597Smelifaro op3->opcode = IP_FW_TABLE_XGETSIZE; 4084234597Smelifaro if (do_cmd(IP_FW3, op3, (uintptr_t)&l) < 0) 4085234597Smelifaro err(EX_OSERR, "getsockopt(IP_FW_TABLE_XGETSIZE)"); 4086183228Srik 4087183228Srik /* If a is zero we have nothing to do, the table is empty. */ 4088234597Smelifaro if (*a == 0) 4089183228Srik return; 4090183228Srik 4091234597Smelifaro l = *a; 4092187716Sluigi tbl = safe_calloc(1, l); 4093234597Smelifaro tbl->opheader.opcode = IP_FW_TABLE_XLIST; 4094234597Smelifaro tbl->tbl = num; 4095234597Smelifaro if (do_cmd(IP_FW3, tbl, (uintptr_t)&l) < 0) 4096234597Smelifaro err(EX_OSERR, "getsockopt(IP_FW_TABLE_XLIST)"); 4097183407Srik if (tbl->cnt && need_header) 4098183407Srik printf("---table(%d)---\n", tbl->tbl); 4099234597Smelifaro sz = tbl->size - sizeof(ipfw_xtable); 4100234597Smelifaro xent = &tbl->xent[0]; 4101234597Smelifaro while (sz > 0) { 4102234597Smelifaro switch (tbl->type) { 4103234597Smelifaro case IPFW_TABLE_CIDR: 4104234597Smelifaro /* IPv4 or IPv6 prefixes */ 4105234597Smelifaro tval = xent->value; 4106234597Smelifaro addr6 = &xent->k.addr6; 4107234597Smelifaro 4108234597Smelifaro if ((addr6->s6_addr32[0] == 0) && (addr6->s6_addr32[1] == 0) && 4109234597Smelifaro (addr6->s6_addr32[2] == 0)) { 4110234597Smelifaro /* IPv4 address */ 4111234597Smelifaro inet_ntop(AF_INET, &addr6->s6_addr32[3], tbuf, sizeof(tbuf)); 4112234597Smelifaro } else { 4113234597Smelifaro /* IPv6 address */ 4114234597Smelifaro inet_ntop(AF_INET6, addr6, tbuf, sizeof(tbuf)); 4115234597Smelifaro } 4116234597Smelifaro 4117234597Smelifaro if (co.do_value_as_ip) { 4118234597Smelifaro tval = htonl(tval); 4119234597Smelifaro printf("%s/%u %s\n", tbuf, xent->masklen, 4120234597Smelifaro inet_ntoa(*(struct in_addr *)&tval)); 4121234597Smelifaro } else 4122234597Smelifaro printf("%s/%u %u\n", tbuf, xent->masklen, tval); 4123234597Smelifaro break; 4124234597Smelifaro case IPFW_TABLE_INTERFACE: 4125234597Smelifaro /* Interface names */ 4126234597Smelifaro tval = xent->value; 4127234597Smelifaro if (co.do_value_as_ip) { 4128234597Smelifaro tval = htonl(tval); 4129234597Smelifaro printf("%s %s\n", xent->k.iface, 4130234597Smelifaro inet_ntoa(*(struct in_addr *)&tval)); 4131234597Smelifaro } else 4132234597Smelifaro printf("%s %u\n", xent->k.iface, tval); 4133130281Sru } 4134234597Smelifaro 4135234597Smelifaro if (sz < xent->len) 4136234597Smelifaro break; 4137234597Smelifaro sz -= xent->len; 4138234597Smelifaro xent = (void *)xent + xent->len; 4139183228Srik } 4140234597Smelifaro 4141183228Srik free(tbl); 4142130281Sru} 4143