ipfw2.c revision 129058
1226046Sdes/* 2224638Sbrooks * Copyright (c) 2002-2003 Luigi Rizzo 357429Smarkm * Copyright (c) 1996 Alex Nash, Paul Traina, Poul-Henning Kamp 457429Smarkm * Copyright (c) 1994 Ugen J.S.Antsilevich 557429Smarkm * 657429Smarkm * Idea and grammar partially left from: 757429Smarkm * Copyright (c) 1993 Daniel Boulet 857429Smarkm * 957429Smarkm * Redistribution and use in source forms, with and without modification, 1060573Skris * are permitted provided that this entire comment appears intact. 1165668Skris * 1265668Skris * Redistribution in binary form may occur without any restrictions. 1365668Skris * Obviously, it would be nice if you gave credit where credit is due 1465668Skris * but requiring it would be too onerous. 1565668Skris * 1665668Skris * This software is provided ``AS IS'' without any warranties of any kind. 1760573Skris * 1892559Sdes * NEW command line interface for IP firewall facility 1965668Skris * 2065668Skris * $FreeBSD: head/sbin/ipfw/ipfw2.c 129058 2004-05-09 01:53:31Z csjp $ 2165668Skris */ 2265668Skris 2365668Skris#include <sys/param.h> 2465668Skris#include <sys/mbuf.h> 2565668Skris#include <sys/socket.h> 2665668Skris#include <sys/sockio.h> 2765668Skris#include <sys/sysctl.h> 2865668Skris#include <sys/time.h> 2965668Skris#include <sys/wait.h> 3065668Skris 3165668Skris#include <ctype.h> 3265668Skris#include <err.h> 3365668Skris#include <errno.h> 3465668Skris#include <grp.h> 3565668Skris#include <limits.h> 3665668Skris#include <netdb.h> 3765668Skris#include <pwd.h> 3865668Skris#include <signal.h> 3965668Skris#include <stdio.h> 4065668Skris#include <stdlib.h> 4157429Smarkm#include <stdarg.h> 4257429Smarkm#include <string.h> 4357429Smarkm#include <timeconv.h> /* XXX do we need this ? */ 4457429Smarkm#include <unistd.h> 45162856Sdes#include <sysexits.h> 46162856Sdes 47162856Sdes#include <net/if.h> 48162856Sdes#include <netinet/in.h> 49162856Sdes#include <netinet/in_systm.h> 50162856Sdes#include <netinet/ip.h> 51162856Sdes#include <netinet/ip_icmp.h> 52162856Sdes#include <netinet/ip_fw.h> 53162856Sdes#include <netinet/ip_dummynet.h> 54162856Sdes#include <netinet/tcp.h> 55162856Sdes#include <arpa/inet.h> 56162856Sdes 57204917Sdesint 58162856Sdes do_resolv, /* Would try to resolve all */ 59162856Sdes do_time, /* Show time stamps */ 60162856Sdes do_quiet, /* Be quiet in add and flush */ 61162856Sdes do_pipe, /* this cmd refers to a pipe */ 62162856Sdes do_sort, /* field to sort results (0 = no) */ 63162856Sdes do_dynamic, /* display dynamic rules */ 64162856Sdes do_expired, /* display expired dynamic rules */ 65162856Sdes do_compact, /* show rules in compact mode */ 66181111Sdes do_force, /* do not ask for confirmation */ 67162856Sdes show_sets, /* display rule sets */ 6857429Smarkm test_only, /* only check syntax */ 6976262Sgreen comment_only, /* only print action and comment */ 7076262Sgreen verbose; 7157429Smarkm 7276262Sgreen#define IP_MASK_ALL 0xffffffff 7376262Sgreen 74162856Sdes/* 7557429Smarkm * _s_x is a structure that stores a string <-> token pairs, used in 7657429Smarkm * various places in the parser. Entries are stored in arrays, 7776262Sgreen * with an entry with s=NULL as terminator. 7865668Skris * The search routines are match_token() and match_value(). 7965668Skris * Often, an element with x=0 contains an error string. 8092559Sdes * 8165668Skris */ 8292559Sdesstruct _s_x { 8357429Smarkm char const *s; 8457429Smarkm int x; 8557429Smarkm}; 8657429Smarkm 8757429Smarkmstatic struct _s_x f_tcpflags[] = { 8892559Sdes { "syn", TH_SYN }, 8957429Smarkm { "fin", TH_FIN }, 9057429Smarkm { "ack", TH_ACK }, 9157429Smarkm { "psh", TH_PUSH }, 9292559Sdes { "rst", TH_RST }, 9357429Smarkm { "urg", TH_URG }, 94137019Sdes { "tcp flag", 0 }, 9557429Smarkm { NULL, 0 } 9657429Smarkm}; 9757429Smarkm 9892559Sdesstatic struct _s_x f_tcpopts[] = { 9957429Smarkm { "mss", IP_FW_TCPOPT_MSS }, 10076262Sgreen { "maxseg", IP_FW_TCPOPT_MSS }, 10157429Smarkm { "window", IP_FW_TCPOPT_WINDOW }, 10257429Smarkm { "sack", IP_FW_TCPOPT_SACK }, 10392559Sdes { "ts", IP_FW_TCPOPT_TS }, 10457429Smarkm { "timestamp", IP_FW_TCPOPT_TS }, 10557429Smarkm { "cc", IP_FW_TCPOPT_CC }, 10657429Smarkm { "tcp option", 0 }, 10757429Smarkm { NULL, 0 } 10857429Smarkm}; 10957429Smarkm 11057429Smarkm/* 11157429Smarkm * IP options span the range 0 to 255 so we need to remap them 11260573Skris * (though in fact only the low 5 bits are significant). 11360573Skris */ 11460573Skrisstatic struct _s_x f_ipopts[] = { 11557429Smarkm { "ssrr", IP_FW_IPOPT_SSRR}, 11657429Smarkm { "lsrr", IP_FW_IPOPT_LSRR}, 117162856Sdes { "rr", IP_FW_IPOPT_RR}, 118215116Sdes { "ts", IP_FW_IPOPT_TS}, 11992559Sdes { "ip option", 0 }, 120162856Sdes { NULL, 0 } 121215116Sdes}; 122162856Sdes 123162856Sdesstatic struct _s_x f_iptos[] = { 12457429Smarkm { "lowdelay", IPTOS_LOWDELAY}, 125162856Sdes { "throughput", IPTOS_THROUGHPUT}, 126162856Sdes { "reliability", IPTOS_RELIABILITY}, 127162856Sdes { "mincost", IPTOS_MINCOST}, 128162856Sdes { "congestion", IPTOS_CE}, 12957429Smarkm { "ecntransport", IPTOS_ECT}, 13057429Smarkm { "ip tos option", 0}, 13157429Smarkm { NULL, 0 } 13257429Smarkm}; 13357429Smarkm 13457429Smarkmstatic struct _s_x limit_masks[] = { 13557429Smarkm {"all", DYN_SRC_ADDR|DYN_SRC_PORT|DYN_DST_ADDR|DYN_DST_PORT}, 13657429Smarkm {"src-addr", DYN_SRC_ADDR}, 13792559Sdes {"src-port", DYN_SRC_PORT}, 13892559Sdes {"dst-addr", DYN_DST_ADDR}, 13992559Sdes {"dst-port", DYN_DST_PORT}, 14092559Sdes {NULL, 0} 14192559Sdes}; 142149753Sdes 143149753Sdes/* 144149753Sdes * we use IPPROTO_ETHERTYPE as a fake protocol id to call the print routines 14592559Sdes * This is only used in this code. 14692559Sdes */ 14792559Sdes#define IPPROTO_ETHERTYPE 0x1000 14892559Sdesstatic struct _s_x ether_types[] = { 14992559Sdes /* 15092559Sdes * Note, we cannot use "-:&/" in the names because they are field 15192559Sdes * separators in the type specifications. Also, we use s = NULL as 15292559Sdes * end-delimiter, because a type of 0 can be legal. 15392559Sdes */ 15492559Sdes { "ip", 0x0800 }, 15592559Sdes { "ipv4", 0x0800 }, 156162856Sdes { "ipv6", 0x86dd }, 15792559Sdes { "arp", 0x0806 }, 15892559Sdes { "rarp", 0x8035 }, 15992559Sdes { "vlan", 0x8100 }, 16092559Sdes { "loop", 0x9000 }, 16192559Sdes { "trail", 0x1000 }, 16292559Sdes { "at", 0x809b }, 16392559Sdes { "atalk", 0x809b }, 16476262Sgreen { "aarp", 0x80f3 }, 16598941Sdes { "pppoe_disc", 0x8863 }, 16676262Sgreen { "pppoe_sess", 0x8864 }, 16792559Sdes { "ipx_8022", 0x00E0 }, 16892559Sdes { "ipx_8023", 0x0000 }, 16976262Sgreen { "ipx_ii", 0x8137 }, 170181111Sdes { "ipx_snap", 0x8137 }, 171181111Sdes { "ipx", 0x8137 }, 172181111Sdes { "ns", 0x0600 }, 173181111Sdes { NULL, 0 } 174224638Sbrooks}; 175224638Sbrooks 176224638Sbrooksstatic void show_usage(void); 177224638Sbrooks 178224638Sbrooksenum tokens { 17992559Sdes TOK_NULL=0, 18057429Smarkm 18160573Skris TOK_OR, 182157019Sdes TOK_NOT, 18360573Skris TOK_STARTBRACE, 18460573Skris TOK_ENDBRACE, 18592559Sdes 186137019Sdes TOK_ACCEPT, 187157019Sdes TOK_COUNT, 18860573Skris TOK_PIPE, 18960573Skris TOK_QUEUE, 19092559Sdes TOK_DIVERT, 19192559Sdes TOK_TEE, 192157019Sdes TOK_FORWARD, 19360573Skris TOK_SKIPTO, 19460573Skris TOK_DENY, 19560573Skris TOK_REJECT, 19660573Skris TOK_RESET, 19760573Skris TOK_UNREACH, 19857429Smarkm TOK_CHECKSTATE, 199157019Sdes 200157019Sdes TOK_UID, 201157019Sdes TOK_GID, 202157019Sdes TOK_IN, 203157019Sdes TOK_LIMIT, 204157019Sdes TOK_KEEPSTATE, 205157019Sdes TOK_LAYER2, 206157019Sdes TOK_OUT, 207157019Sdes TOK_XMIT, 208157019Sdes TOK_RECV, 209157019Sdes TOK_VIA, 210162856Sdes TOK_FRAG, 211157019Sdes TOK_IPOPTS, 212157019Sdes TOK_IPLEN, 213157019Sdes TOK_IPID, 214157019Sdes TOK_IPPRECEDENCE, 215157019Sdes TOK_IPTOS, 216157019Sdes TOK_IPTTL, 217157019Sdes TOK_IPVER, 218157019Sdes TOK_ESTAB, 219157019Sdes TOK_SETUP, 220157019Sdes TOK_TCPFLAGS, 221157019Sdes TOK_TCPOPTS, 222157019Sdes TOK_TCPSEQ, 223157019Sdes TOK_TCPACK, 224157019Sdes TOK_TCPWIN, 225157019Sdes TOK_ICMPTYPES, 22660573Skris TOK_MAC, 22760573Skris TOK_MACTYPE, 22860573Skris TOK_VERREVPATH, 22992559Sdes TOK_VERSRCREACH, 23069587Sgreen TOK_IPSEC, 231181111Sdes TOK_COMMENT, 23260573Skris 23360573Skris TOK_PLR, 23476262Sgreen TOK_NOERROR, 23576262Sgreen TOK_BUCKETS, 23676262Sgreen TOK_DSTIP, 23776262Sgreen TOK_SRCIP, 238204917Sdes TOK_DSTPORT, 239204917Sdes TOK_SRCPORT, 240204917Sdes TOK_ALL, 241204917Sdes TOK_MASK, 242204917Sdes TOK_BW, 243204917Sdes TOK_DELAY, 24460573Skris TOK_RED, 24560573Skris TOK_GRED, 24660573Skris TOK_DROPTAIL, 24760573Skris TOK_PROTO, 24860573Skris TOK_WEIGHT, 24960573Skris}; 25069587Sgreen 251181111Sdesstruct _s_x dummynet_params[] = { 252124207Sdes { "plr", TOK_PLR }, 253181111Sdes { "noerror", TOK_NOERROR }, 25474500Sgreen { "buckets", TOK_BUCKETS }, 25569587Sgreen { "dst-ip", TOK_DSTIP }, 25669587Sgreen { "src-ip", TOK_SRCIP }, 25769587Sgreen { "dst-port", TOK_DSTPORT }, 25869587Sgreen { "src-port", TOK_SRCPORT }, 25969587Sgreen { "proto", TOK_PROTO }, 26069587Sgreen { "weight", TOK_WEIGHT }, 26169587Sgreen { "all", TOK_ALL }, 26269587Sgreen { "mask", TOK_MASK }, 26369587Sgreen { "droptail", TOK_DROPTAIL }, 26460573Skris { "red", TOK_RED }, 26560573Skris { "gred", TOK_GRED }, 26660573Skris { "bw", TOK_BW }, 26757429Smarkm { "bandwidth", TOK_BW }, 26857429Smarkm { "delay", TOK_DELAY }, 26957429Smarkm { "pipe", TOK_PIPE }, 27092559Sdes { "queue", TOK_QUEUE }, 27160573Skris { "dummynet-params", TOK_NULL }, 27299063Sdes { NULL, 0 } /* terminator */ 27357429Smarkm}; 274137019Sdes 275137019Sdesstruct _s_x rule_actions[] = { 27657429Smarkm { "accept", TOK_ACCEPT }, 27757429Smarkm { "pass", TOK_ACCEPT }, 27857429Smarkm { "allow", TOK_ACCEPT }, 27957429Smarkm { "permit", TOK_ACCEPT }, 28057429Smarkm { "count", TOK_COUNT }, 281162856Sdes { "pipe", TOK_PIPE }, 28257429Smarkm { "queue", TOK_QUEUE }, 28392559Sdes { "divert", TOK_DIVERT }, 28457429Smarkm { "tee", TOK_TEE }, 28557429Smarkm { "fwd", TOK_FORWARD }, 28657429Smarkm { "forward", TOK_FORWARD }, 28792559Sdes { "skipto", TOK_SKIPTO }, 28857429Smarkm { "deny", TOK_DENY }, 289137019Sdes { "drop", TOK_DENY }, 29057429Smarkm { "reject", TOK_REJECT }, 29157429Smarkm { "reset", TOK_RESET }, 292137019Sdes { "unreach", TOK_UNREACH }, 29357429Smarkm { "check-state", TOK_CHECKSTATE }, 29457429Smarkm { "//", TOK_COMMENT }, 29599063Sdes { NULL, 0 } /* terminator */ 29699063Sdes}; 29799063Sdes 298162856Sdesstruct _s_x rule_options[] = { 299162856Sdes { "uid", TOK_UID }, 300120489Sjoe { "gid", TOK_GID }, 30169587Sgreen { "in", TOK_IN }, 30257429Smarkm { "limit", TOK_LIMIT }, 30392559Sdes { "keep-state", TOK_KEEPSTATE }, 30457429Smarkm { "bridged", TOK_LAYER2 }, 30592559Sdes { "layer2", TOK_LAYER2 }, 306162856Sdes { "out", TOK_OUT }, 30757429Smarkm { "xmit", TOK_XMIT }, 30857429Smarkm { "recv", TOK_RECV }, 30960573Skris { "via", TOK_VIA }, 310192595Sdes { "fragment", TOK_FRAG }, 31192559Sdes { "frag", TOK_FRAG }, 31292559Sdes { "ipoptions", TOK_IPOPTS }, 31392559Sdes { "ipopts", TOK_IPOPTS }, 314181111Sdes { "iplen", TOK_IPLEN }, 31557429Smarkm { "ipid", TOK_IPID }, 31657429Smarkm { "ipprecedence", TOK_IPPRECEDENCE }, 31760573Skris { "iptos", TOK_IPTOS }, 318224638Sbrooks { "ipttl", TOK_IPTTL }, 31960573Skris { "ipversion", TOK_IPVER }, 32060573Skris { "ipver", TOK_IPVER }, 32160573Skris { "estab", TOK_ESTAB }, 32260573Skris { "established", TOK_ESTAB }, 32357429Smarkm { "setup", TOK_SETUP }, 324124207Sdes { "tcpflags", TOK_TCPFLAGS }, 32560573Skris { "tcpflgs", TOK_TCPFLAGS }, 32660573Skris { "tcpoptions", TOK_TCPOPTS }, 32792559Sdes { "tcpopts", TOK_TCPOPTS }, 32892559Sdes { "tcpseq", TOK_TCPSEQ }, 32992559Sdes { "tcpack", TOK_TCPACK }, 330157019Sdes { "tcpwin", TOK_TCPWIN }, 331181111Sdes { "icmptype", TOK_ICMPTYPES }, 332181111Sdes { "icmptypes", TOK_ICMPTYPES }, 33365668Skris { "dst-ip", TOK_DSTIP }, 334157019Sdes { "src-ip", TOK_SRCIP }, 335181111Sdes { "dst-port", TOK_DSTPORT }, 336181111Sdes { "src-port", TOK_SRCPORT }, 337204917Sdes { "proto", TOK_PROTO }, 338204917Sdes { "MAC", TOK_MAC }, 339204917Sdes { "mac", TOK_MAC }, 340215116Sdes { "mac-type", TOK_MACTYPE }, 341204917Sdes { "verrevpath", TOK_VERREVPATH }, 342181111Sdes { "versrcreach", TOK_VERSRCREACH }, 34357429Smarkm { "ipsec", TOK_IPSEC }, 34492559Sdes { "//", TOK_COMMENT }, 34557429Smarkm 34692559Sdes { "not", TOK_NOT }, /* pseudo option */ 34792559Sdes { "!", /* escape ? */ TOK_NOT }, /* pseudo option */ 34892559Sdes { "or", TOK_OR }, /* pseudo option */ 34992559Sdes { "|", /* escape */ TOK_OR }, /* pseudo option */ 350137019Sdes { "{", TOK_STARTBRACE }, /* pseudo option */ 351137019Sdes { "(", TOK_STARTBRACE }, /* pseudo option */ 35292559Sdes { "}", TOK_ENDBRACE }, /* pseudo option */ 35392559Sdes { ")", TOK_ENDBRACE }, /* pseudo option */ 35492559Sdes { NULL, 0 } /* terminator */ 35592559Sdes}; 35692559Sdes 35792559Sdesstatic __inline uint64_t 35892559Sdesalign_uint64(uint64_t *pll) { 35992559Sdes uint64_t ret; 36092559Sdes 36192559Sdes bcopy (pll, &ret, sizeof(ret)); 36292559Sdes return ret; 36392559Sdes}; 36492559Sdes 36560573Skris/* 36692559Sdes * conditionally runs the command. 36760573Skris */ 36892559Sdesstatic int 36992559Sdesdo_cmd(int optname, void *optval, uintptr_t optlen) 37092559Sdes{ 37192559Sdes static int s = -1; /* the socket */ 37292559Sdes int i; 37392559Sdes 37492559Sdes if (test_only) 37592559Sdes return 0; 37692559Sdes 37760573Skris if (s == -1) 37857429Smarkm s = socket(AF_INET, SOCK_RAW, IPPROTO_RAW); 37960573Skris if (s < 0) 38092559Sdes err(EX_UNAVAILABLE, "socket"); 38160573Skris 38257429Smarkm if (optname == IP_FW_GET || optname == IP_DUMMYNET_GET || 38392559Sdes optname == IP_FW_ADD) 38492559Sdes i = getsockopt(s, IPPROTO_IP, optname, optval, 38592559Sdes (socklen_t *)optlen); 38692559Sdes else 38760573Skris i = setsockopt(s, IPPROTO_IP, optname, optval, optlen); 38857429Smarkm return i; 38960573Skris} 39060573Skris 39192559Sdes/** 39260573Skris * match_token takes a table and a string, returns the value associated 39392559Sdes * with the string (-1 in case of failure). 394137019Sdes */ 395181111Sdesstatic int 39676262Sgreenmatch_token(struct _s_x *table, char *string) 39792559Sdes{ 39892559Sdes struct _s_x *pt; 39992559Sdes uint i = strlen(string); 400137019Sdes 40192559Sdes for (pt = table ; i && pt->s != NULL ; pt++) 40292559Sdes if (strlen(pt->s) == i && !bcmp(string, pt->s, i)) 40392559Sdes return pt->x; 404124207Sdes return -1; 40576262Sgreen}; 40676262Sgreen 40760573Skris/** 40860573Skris * match_value takes a table and a value, returns the string associated 40960573Skris * with the value (NULL in case of failure). 41060573Skris */ 41160573Skrisstatic char const * 41260573Skrismatch_value(struct _s_x *p, int value) 41360573Skris{ 41460573Skris for (; p->s != NULL; p++) 41560573Skris if (p->x == value) 41660573Skris return p->s; 417192595Sdes return NULL; 418192595Sdes} 419192595Sdes 420192595Sdes/* 421181111Sdes * prints one port, symbolic or numeric 422181111Sdes */ 423181111Sdesstatic void 424181111Sdesprint_port(int proto, uint16_t port) 425181111Sdes{ 426181111Sdes 427181111Sdes if (proto == IPPROTO_ETHERTYPE) { 428181111Sdes char const *s; 429181111Sdes 43092559Sdes if (do_resolv && (s = match_value(ether_types, port)) ) 43192559Sdes printf("%s", s); 43257429Smarkm else 43357429Smarkm printf("0x%04x", port); 43492559Sdes } else { 43592559Sdes struct servent *se = NULL; 43692559Sdes if (do_resolv) { 437137019Sdes struct protoent *pe = getprotobynumber(proto); 43892559Sdes 43992559Sdes se = getservbyport(htons(port), pe ? pe->p_name : NULL); 44092559Sdes } 44192559Sdes if (se) 44292559Sdes printf("%s", se->s_name); 44392559Sdes else 44457429Smarkm printf("%d", port); 44592559Sdes } 44692559Sdes} 44792559Sdes 44892559Sdesstruct _s_x _port_name[] = { 44992559Sdes {"dst-port", O_IP_DSTPORT}, 45092559Sdes {"src-port", O_IP_SRCPORT}, 451137019Sdes {"ipid", O_IPID}, 45292559Sdes {"iplen", O_IPLEN}, 45392559Sdes {"ipttl", O_IPTTL}, 45492559Sdes {"mac-type", O_MAC_TYPE}, 45592559Sdes {NULL, 0} 45692559Sdes}; 45792559Sdes 45892559Sdes/* 45992559Sdes * Print the values in a list 16-bit items of the types above. 46092559Sdes * XXX todo: add support for mask. 46192559Sdes */ 46292559Sdesstatic void 46392559Sdesprint_newports(ipfw_insn_u16 *cmd, int proto, int opcode) 464137019Sdes{ 46592559Sdes uint16_t *p = cmd->ports; 46692559Sdes int i; 46792559Sdes char const *sep; 46892559Sdes 46992559Sdes if (cmd->o.len & F_NOT) 47092559Sdes printf(" not"); 47192559Sdes if (opcode != 0) { 47292559Sdes sep = match_value(_port_name, opcode); 47392559Sdes if (sep == NULL) 47492559Sdes sep = "???"; 47592559Sdes printf (" %s", sep); 47692559Sdes } 47792559Sdes sep = " "; 47892559Sdes for (i = F_LEN((ipfw_insn *)cmd) - 1; i > 0; i--, p += 2) { 47992559Sdes printf(sep); 48092559Sdes print_port(proto, p[0]); 48192559Sdes if (p[0] != p[1]) { 48292559Sdes printf("-"); 48392559Sdes print_port(proto, p[1]); 48492559Sdes } 48592559Sdes sep = ","; 48692559Sdes } 48792559Sdes} 48892559Sdes 48992559Sdes/* 49092559Sdes * Like strtol, but also translates service names into port numbers 49192559Sdes * for some protocols. 49292559Sdes * In particular: 49392559Sdes * proto == -1 disables the protocol check; 49492559Sdes * proto == IPPROTO_ETHERTYPE looks up an internal table 49592559Sdes * proto == <some value in /etc/protocols> matches the values there. 49692559Sdes * Returns *end == s in case the parameter is not found. 49792559Sdes */ 49892559Sdesstatic int 499113911Sdesstrtoport(char *s, char **end, int base, int proto) 50092559Sdes{ 50192559Sdes char *p, *buf; 50292559Sdes char *s1; 50392559Sdes int i; 50492559Sdes 505124207Sdes *end = s; /* default - not found */ 50692559Sdes if (*s == '\0') 50792559Sdes return 0; /* not found */ 50892559Sdes 50992559Sdes if (isdigit(*s)) 51092559Sdes return strtol(s, end, base); 51192559Sdes 51292559Sdes /* 51392559Sdes * find separator. '\\' escapes the next char. 51492559Sdes */ 51592559Sdes for (s1 = s; *s1 && (isalnum(*s1) || *s1 == '\\') ; s1++) 51692559Sdes if (*s1 == '\\' && s1[1] != '\0') 51792559Sdes s1++; 51892559Sdes 519137019Sdes buf = malloc(s1 - s + 1); 52092559Sdes if (buf == NULL) 52192559Sdes return 0; 52292559Sdes 52392559Sdes /* 52492559Sdes * copy into a buffer skipping backslashes 52592559Sdes */ 52692559Sdes for (p = s, i = 0; p != s1 ; p++) 52792559Sdes if (*p != '\\') 52892559Sdes buf[i++] = *p; 52992559Sdes buf[i++] = '\0'; 530204917Sdes 53192559Sdes if (proto == IPPROTO_ETHERTYPE) { 53292559Sdes i = match_token(ether_types, buf); 53392559Sdes free(buf); 53492559Sdes if (i != -1) { /* found */ 53592559Sdes *end = s1; 53692559Sdes return i; 53792559Sdes } 53892559Sdes } else { 53992559Sdes struct protoent *pe = NULL; 54092559Sdes struct servent *se; 54192559Sdes 54292559Sdes if (proto != 0) 54392559Sdes pe = getprotobynumber(proto); 544204917Sdes setservent(1); 54592559Sdes se = getservbyname(buf, pe ? pe->p_name : NULL); 54692559Sdes free(buf); 54792559Sdes if (se != NULL) { 54892559Sdes *end = s1; 54992559Sdes return ntohs(se->s_port); 55092559Sdes } 55192559Sdes } 55292559Sdes return 0; /* not found */ 55392559Sdes} 55492559Sdes 55592559Sdes/* 55692559Sdes * Fill the body of the command with the list of port ranges. 55792559Sdes */ 55892559Sdesstatic int 55992559Sdesfill_newports(ipfw_insn_u16 *cmd, char *av, int proto) 56092559Sdes{ 56192559Sdes uint16_t a, b, *p = cmd->ports; 56292559Sdes int i = 0; 563137019Sdes char *s = av; 56492559Sdes 56592559Sdes while (*s) { 56692559Sdes a = strtoport(av, &s, 0, proto); 56792559Sdes if (s == av) /* no parameter */ 568137019Sdes break; 56992559Sdes if (*s == '-') { /* a range */ 57092559Sdes av = s+1; 57192559Sdes b = strtoport(av, &s, 0, proto); 57292559Sdes if (s == av) /* no parameter */ 57392559Sdes break; 57492559Sdes p[0] = a; 57592559Sdes p[1] = b; 576204917Sdes } else if (*s == ',' || *s == '\0' ) 577204917Sdes p[0] = p[1] = a; 57892559Sdes else /* invalid separator */ 57992559Sdes errx(EX_DATAERR, "invalid separator <%c> in <%s>\n", 58092559Sdes *s, av); 58192559Sdes i++; 58292559Sdes p += 2; 58392559Sdes av = s+1; 58492559Sdes } 58592559Sdes if (i > 0) { 58692559Sdes if (i+1 > F_LEN_MASK) 58792559Sdes errx(EX_DATAERR, "too many ports/ranges\n"); 58892559Sdes cmd->o.len |= i+1; /* leave F_NOT and F_OR untouched */ 58992559Sdes } 59092559Sdes return i; 59192559Sdes} 59292559Sdes 59392559Sdesstatic struct _s_x icmpcodes[] = { 59492559Sdes { "net", ICMP_UNREACH_NET }, 59592559Sdes { "host", ICMP_UNREACH_HOST }, 59692559Sdes { "protocol", ICMP_UNREACH_PROTOCOL }, 59792559Sdes { "port", ICMP_UNREACH_PORT }, 59892559Sdes { "needfrag", ICMP_UNREACH_NEEDFRAG }, 59992559Sdes { "srcfail", ICMP_UNREACH_SRCFAIL }, 60092559Sdes { "net-unknown", ICMP_UNREACH_NET_UNKNOWN }, 60192559Sdes { "host-unknown", ICMP_UNREACH_HOST_UNKNOWN }, 60292559Sdes { "isolated", ICMP_UNREACH_ISOLATED }, 60392559Sdes { "net-prohib", ICMP_UNREACH_NET_PROHIB }, 60492559Sdes { "host-prohib", ICMP_UNREACH_HOST_PROHIB }, 60592559Sdes { "tosnet", ICMP_UNREACH_TOSNET }, 60692559Sdes { "toshost", ICMP_UNREACH_TOSHOST }, 60792559Sdes { "filter-prohib", ICMP_UNREACH_FILTER_PROHIB }, 60892559Sdes { "host-precedence", ICMP_UNREACH_HOST_PRECEDENCE }, 60992559Sdes { "precedence-cutoff", ICMP_UNREACH_PRECEDENCE_CUTOFF }, 61092559Sdes { NULL, 0 } 61192559Sdes}; 612137019Sdes 61392559Sdesstatic void 61492559Sdesfill_reject_code(u_short *codep, char *str) 61592559Sdes{ 61692559Sdes int val; 61792559Sdes char *s; 61892559Sdes 61992559Sdes val = strtoul(str, &s, 0); 62092559Sdes if (s == str || *s != '\0' || val >= 0x100) 62192559Sdes val = match_token(icmpcodes, str); 62292559Sdes if (val < 0) 62392559Sdes errx(EX_DATAERR, "unknown ICMP unreachable code ``%s''", str); 62492559Sdes *codep = val; 62592559Sdes return; 62692559Sdes} 62792559Sdes 628204917Sdesstatic void 629204917Sdesprint_reject_code(uint16_t code) 63092559Sdes{ 63192559Sdes char const *s = match_value(icmpcodes, code); 63292559Sdes 63392559Sdes if (s != NULL) 63492559Sdes printf("unreach %s", s); 63592559Sdes else 63692559Sdes printf("unreach %u", code); 63792559Sdes} 63892559Sdes 639137019Sdes/* 640204917Sdes * Returns the number of bits set (from left) in a contiguous bitmask, 64192559Sdes * or -1 if the mask is not contiguous. 64292559Sdes * XXX this needs a proper fix. 64392559Sdes * This effectively works on masks in big-endian (network) format. 64492559Sdes * when compiled on little endian architectures. 645204917Sdes * 64692559Sdes * First bit is bit 7 of the first byte -- note, for MAC addresses, 64792559Sdes * the first bit on the wire is bit 0 of the first byte. 64892559Sdes * len is the max length in bits. 64992559Sdes */ 65092559Sdesstatic int 65192559Sdescontigmask(uint8_t *p, int len) 65292559Sdes{ 65392559Sdes int i, n; 65492559Sdes 65592559Sdes for (i=0; i<len ; i++) 65692559Sdes if ( (p[i/8] & (1 << (7 - (i%8)))) == 0) /* first bit unset */ 65792559Sdes break; 65892559Sdes for (n=i+1; n < len; n++) 65992559Sdes if ( (p[n/8] & (1 << (7 - (n%8)))) != 0) 66092559Sdes return -1; /* mask not contiguous */ 66192559Sdes return i; 66292559Sdes} 663106130Sdes 66492559Sdes/* 665124207Sdes * print flags set/clear in the two bitmasks passed as parameters. 66692559Sdes * There is a specialized check for f_tcpflags. 66792559Sdes */ 668113911Sdesstatic void 66992559Sdesprint_flags(char const *name, ipfw_insn *cmd, struct _s_x *list) 67092559Sdes{ 67192559Sdes char const *comma = ""; 67292559Sdes int i; 67392559Sdes uint8_t set = cmd->arg1 & 0xff; 67492559Sdes uint8_t clear = (cmd->arg1 >> 8) & 0xff; 67592559Sdes 67692559Sdes if (list == f_tcpflags && set == TH_SYN && clear == TH_ACK) { 67792559Sdes printf(" setup"); 678113911Sdes return; 67992559Sdes } 680113911Sdes 681106130Sdes printf(" %s ", name); 68292559Sdes for (i=0; list[i].x != 0; i++) { 683124207Sdes if (set & list[i].x) { 68492559Sdes set &= ~list[i].x; 68592559Sdes printf("%s%s", comma, list[i].s); 686137019Sdes comma = ","; 68792559Sdes } 68892559Sdes if (clear & list[i].x) { 68992559Sdes clear &= ~list[i].x; 69092559Sdes printf("%s!%s", comma, list[i].s); 69192559Sdes comma = ","; 692162856Sdes } 69392559Sdes } 694181111Sdes} 695181111Sdes 69692559Sdes/* 697181111Sdes * Print the ip address contained in a command. 698181111Sdes */ 699181111Sdesstatic void 700181111Sdesprint_ip(ipfw_insn_ip *cmd, char const *s) 701181111Sdes{ 702181111Sdes struct hostent *he = NULL; 703181111Sdes int len = F_LEN((ipfw_insn *)cmd); 704181111Sdes uint32_t *a = ((ipfw_insn_u32 *)cmd)->d; 705181111Sdes 706181111Sdes printf("%s%s ", cmd->o.len & F_NOT ? " not": "", s); 707181111Sdes 708181111Sdes if (cmd->o.opcode == O_IP_SRC_ME || cmd->o.opcode == O_IP_DST_ME) { 709181111Sdes printf("me"); 710181111Sdes return; 711215116Sdes } 712181111Sdes if (cmd->o.opcode == O_IP_SRC_SET || cmd->o.opcode == O_IP_DST_SET) { 71392559Sdes uint32_t x, *map = (uint32_t *)&(cmd->mask); 714106130Sdes int i, j; 71592559Sdes char comma = '{'; 716192595Sdes 71792559Sdes x = cmd->o.arg1 - 1; 71892559Sdes x = htonl( ~x ); 719181111Sdes cmd->addr.s_addr = htonl(cmd->addr.s_addr); 720181111Sdes printf("%s/%d", inet_ntoa(cmd->addr), 72192559Sdes contigmask((uint8_t *)&x, 32)); 722162856Sdes x = cmd->addr.s_addr = htonl(cmd->addr.s_addr); 72392559Sdes x &= 0xff; /* base */ 724157019Sdes /* 72592559Sdes * Print bits and ranges. 726157019Sdes * Locate first bit set (i), then locate first bit unset (j). 727106130Sdes * If we have 3+ consecutive bits set, then print them as a 72892559Sdes * range, otherwise only print the initial bit and rescan. 729124207Sdes */ 73092559Sdes for (i=0; i < cmd->o.arg1; i++) 73192559Sdes if (map[i/32] & (1<<(i & 31))) { 73292559Sdes for (j=i+1; j < cmd->o.arg1; j++) 733157019Sdes if (!(map[ j/32] & (1<<(j & 31)))) 73492559Sdes break; 735162856Sdes printf("%c%d", comma, i+x); 73692559Sdes if (j>i+2) { /* range has at least 3 elements */ 73792559Sdes printf("-%d", j-1+x); 73892559Sdes i = j-1; 739157019Sdes } 740106130Sdes comma = ','; 74192559Sdes } 742124207Sdes printf("}"); 74392559Sdes return; 74492559Sdes } 74592559Sdes /* 746157019Sdes * len == 2 indicates a single IP, whereas lists of 1 or more 74792559Sdes * addr/mask pairs have len = (2n+1). We convert len to n so we 748162856Sdes * use that to count the number of entries. 74992559Sdes */ 750157019Sdes for (len = len / 2; len > 0; len--, a += 2) { 751181111Sdes int mb = /* mask length */ 75292559Sdes (cmd->o.opcode == O_IP_SRC || cmd->o.opcode == O_IP_DST) ? 75392559Sdes 32 : contigmask((uint8_t *)&(a[1]), 32); 754106130Sdes if (mb == 32 && do_resolv) 75592559Sdes he = gethostbyaddr((char *)&(a[0]), sizeof(u_long), AF_INET); 756124207Sdes if (he != NULL) /* resolved to name */ 75792559Sdes printf("%s", he->h_name); 75892559Sdes else if (mb == 0) /* any */ 759157019Sdes printf("any"); 760157019Sdes else { /* numeric IP followed by some kind of mask */ 761181111Sdes printf("%s", inet_ntoa( *((struct in_addr *)&a[0]) ) ); 762181111Sdes if (mb < 0) 76392559Sdes printf(":%s", inet_ntoa( *((struct in_addr *)&a[1]) ) ); 76492559Sdes else if (mb < 32) 76592559Sdes printf("/%d", mb); 76692559Sdes } 767181111Sdes if (len > 1) 76892559Sdes printf(","); 76992559Sdes } 770106130Sdes} 77192559Sdes 77292559Sdes/* 773181111Sdes * prints a MAC address/mask pair 77492559Sdes */ 77592559Sdesstatic void 77692559Sdesprint_mac(uint8_t *addr, uint8_t *mask) 77792559Sdes{ 77892559Sdes int l = contigmask(mask, 48); 77992559Sdes 78092559Sdes if (l == 0) 78192559Sdes printf(" any"); 78292559Sdes else { 78360573Skris printf(" %02x:%02x:%02x:%02x:%02x:%02x", 78460573Skris addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); 78557429Smarkm if (l == -1) 78660573Skris printf("&%02x:%02x:%02x:%02x:%02x:%02x", 78760573Skris mask[0], mask[1], mask[2], 78860573Skris mask[3], mask[4], mask[5]); 78960573Skris else if (l < 48) 790162856Sdes printf("/%d", l); 79160573Skris } 79260573Skris} 79357429Smarkm 794162856Sdesstatic void 79592559Sdesfill_icmptypes(ipfw_insn_u32 *cmd, char *av) 796162856Sdes{ 79757429Smarkm uint8_t type; 79860573Skris 79960573Skris cmd->d[0] = 0; 80060573Skris while (*av) { 801162856Sdes if (*av == ',') 80292559Sdes av++; 803162856Sdes 80476262Sgreen type = strtoul(av, &av, 0); 80576262Sgreen 80676262Sgreen if (*av != ',' && *av != '\0') 80776262Sgreen errx(EX_DATAERR, "invalid ICMP type"); 80876262Sgreen 80992559Sdes if (type > 31) 810162856Sdes errx(EX_DATAERR, "ICMP type out of range"); 81160573Skris 81260573Skris cmd->d[0] |= 1 << type; 81360573Skris } 81460573Skris cmd->o.opcode = O_ICMPTYPE; 81560573Skris cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 81660573Skris} 81760573Skris 818224638Sbrooksstatic void 819224638Sbrooksprint_icmptypes(ipfw_insn_u32 *cmd) 820224638Sbrooks{ 821224638Sbrooks int i; 822224638Sbrooks char sep= ' '; 823224638Sbrooks 824224638Sbrooks printf(" icmptypes"); 825224638Sbrooks for (i = 0; i < 32; i++) { 826224638Sbrooks if ( (cmd->d[0] & (1 << (i))) == 0) 827224638Sbrooks continue; 828224638Sbrooks printf("%c%d", sep, i); 829224638Sbrooks sep = ','; 830224638Sbrooks } 831224638Sbrooks} 832224638Sbrooks 833224638Sbrooks/* 834224638Sbrooks * show_ipfw() prints the body of an ipfw rule. 835224638Sbrooks * Because the standard rule has at least proto src_ip dst_ip, we use 836224638Sbrooks * a helper function to produce these entries if not provided explicitly. 837224638Sbrooks * The first argument is the list of fields we have, the second is 838224638Sbrooks * the list of fields we want to be printed. 839224638Sbrooks * 840224638Sbrooks * Special cases if we have provided a MAC header: 841224638Sbrooks * + if the rule does not contain IP addresses/ports, do not print them; 842224638Sbrooks * + if the rule does not contain an IP proto, print "all" instead of "ip"; 843224638Sbrooks * 844224638Sbrooks * Once we have 'have_options', IP header fields are printed as options. 845224638Sbrooks */ 84692559Sdes#define HAVE_PROTO 0x0001 847162856Sdes#define HAVE_SRCIP 0x0002 84860573Skris#define HAVE_DSTIP 0x0004 849224638Sbrooks#define HAVE_MAC 0x0008 85060573Skris#define HAVE_MACTYPE 0x0010 851224638Sbrooks#define HAVE_OPTIONS 0x8000 852224638Sbrooks 853224638Sbrooks#define HAVE_IP (HAVE_PROTO | HAVE_SRCIP | HAVE_DSTIP) 854224638Sbrooksstatic void 855224638Sbrooksshow_prerequisites(int *flags, int want, int cmd) 856224638Sbrooks{ 857224638Sbrooks if (comment_only) 85860573Skris return; 85992559Sdes if ( (*flags & HAVE_IP) == HAVE_IP) 860162856Sdes *flags |= HAVE_OPTIONS; 861162856Sdes 86260573Skris if ( (*flags & (HAVE_MAC|HAVE_MACTYPE|HAVE_OPTIONS)) == HAVE_MAC && 86360573Skris cmd != O_MAC_TYPE) { 86460573Skris /* 86560573Skris * mac-type was optimized out by the compiler, 86660573Skris * restore it 86760573Skris */ 86898684Sdes printf(" any"); 869149753Sdes *flags |= HAVE_MACTYPE | HAVE_OPTIONS; 870149753Sdes return; 87198684Sdes } 87298684Sdes if ( !(*flags & HAVE_OPTIONS)) { 87360573Skris if ( !(*flags & HAVE_PROTO) && (want & HAVE_PROTO)) 87460573Skris printf(" ip"); 87560573Skris if ( !(*flags & HAVE_SRCIP) && (want & HAVE_SRCIP)) 876181111Sdes printf(" from any"); 877181111Sdes if ( !(*flags & HAVE_DSTIP) && (want & HAVE_DSTIP)) 87860573Skris printf(" to any"); 87960573Skris } 88060573Skris *flags |= want; 881215116Sdes} 882215116Sdes 883215116Sdesstatic void 88460573Skrisshow_ipfw(struct ip_fw *rule, int pcwidth, int bcwidth) 88560573Skris{ 88660573Skris static int twidth = 0; 887137019Sdes int l; 88860573Skris ipfw_insn *cmd; 88960573Skris char *comment = NULL; /* ptr to comment if we have one */ 890162856Sdes int proto = 0; /* default */ 89192559Sdes int flags = 0; /* prerequisites */ 892162856Sdes ipfw_insn_log *logptr = NULL; /* set if we find an O_LOG */ 89360573Skris int or_block = 0; /* we are in an or block */ 89460573Skris uint32_t set_disable; 89560573Skris 89660573Skris bcopy(&rule->next_rule, &set_disable, sizeof(set_disable)); 89760573Skris 89860573Skris if (set_disable & (1 << rule->set)) { /* disabled */ 899124207Sdes if (!show_sets) 90060573Skris return; 90160573Skris else 90260573Skris printf("# DISABLED "); 903162856Sdes } 90492559Sdes printf("%05u ", rule->rulenum); 905162856Sdes 90660573Skris if (pcwidth>0 || bcwidth>0) 90760573Skris printf("%*llu %*llu ", pcwidth, align_uint64(&rule->pcnt), 90892559Sdes bcwidth, align_uint64(&rule->bcnt)); 90960573Skris 91060573Skris if (do_time == 2) 91160573Skris printf("%10u ", rule->timestamp); 91260573Skris else if (do_time == 1) { 91360573Skris char timestr[30]; 91460573Skris time_t t = (time_t)0; 91560573Skris 91660573Skris if (twidth == 0) { 91760573Skris strcpy(timestr, ctime(&t)); 91860573Skris *strchr(timestr, '\n') = '\0'; 91960573Skris twidth = strlen(timestr); 92092559Sdes } 92160573Skris if (rule->timestamp) { 92292559Sdes#if _FreeBSD_version < 500000 /* XXX check */ 92392559Sdes#define _long_to_time(x) (time_t)(x) 92460573Skris#endif 92576262Sgreen t = _long_to_time(rule->timestamp); 92676262Sgreen 92757429Smarkm strcpy(timestr, ctime(&t)); 92860573Skris *strchr(timestr, '\n') = '\0'; 92992559Sdes printf("%s ", timestr); 93060573Skris } else { 93157429Smarkm printf("%*s", twidth, " "); 93260573Skris } 93392559Sdes } 93460573Skris 93560573Skris if (show_sets) 93660573Skris printf("set %d ", rule->set); 93760573Skris 93860573Skris /* 93960573Skris * print the optional "match probability" 94060573Skris */ 941124207Sdes if (rule->cmd_len > 0) { 94292559Sdes cmd = rule->cmd ; 94360573Skris if (cmd->opcode == O_PROB) { 94460573Skris ipfw_insn_u32 *p = (ipfw_insn_u32 *)cmd; 94557429Smarkm double d = 1.0 * p->d[0]; 94660573Skris 94792559Sdes d = (d / 0x7fffffff); 94860573Skris printf("prob %f ", d); 94960573Skris } 95057429Smarkm } 95160573Skris 95260573Skris /* 95360573Skris * first print actions 954124207Sdes */ 95560573Skris for (l = rule->cmd_len - rule->act_ofs, cmd = ACTION_PTR(rule); 95660573Skris l > 0 ; l -= F_LEN(cmd), cmd += F_LEN(cmd)) { 95760573Skris switch(cmd->opcode) { 95860573Skris case O_CHECK_STATE: 959215116Sdes printf("check-state"); 96060573Skris flags = HAVE_IP; /* avoid printing anything else */ 961124207Sdes break; 96260573Skris 96360573Skris case O_ACCEPT: 96460573Skris printf("allow"); 96560573Skris break; 96660573Skris 96760573Skris case O_COUNT: 96860573Skris printf("count"); 96960573Skris break; 97060573Skris 97160573Skris case O_DENY: 97260573Skris printf("deny"); 97360573Skris break; 97460573Skris 97560573Skris case O_REJECT: 97660573Skris if (cmd->arg1 == ICMP_REJECT_RST) 97760573Skris printf("reset"); 97860573Skris else if (cmd->arg1 == ICMP_UNREACH_HOST) 97957429Smarkm printf("reject"); 98092559Sdes else 981162856Sdes print_reject_code(cmd->arg1); 98260573Skris break; 98392559Sdes 984106130Sdes case O_SKIPTO: 98560573Skris printf("skipto %u", cmd->arg1); 98660573Skris break; 98760573Skris 98860573Skris case O_PIPE: 98960573Skris printf("pipe %u", cmd->arg1); 99060573Skris break; 99160573Skris 99260573Skris case O_QUEUE: 99360573Skris printf("queue %u", cmd->arg1); 994124207Sdes break; 99560573Skris 99660573Skris case O_DIVERT: 99792559Sdes printf("divert %u", cmd->arg1); 99860573Skris break; 99960573Skris 100060573Skris case O_TEE: 100160573Skris printf("tee %u", cmd->arg1); 100260573Skris break; 100360573Skris 100460573Skris case O_FORWARD_IP: 100557429Smarkm { 100692559Sdes ipfw_insn_sa *s = (ipfw_insn_sa *)cmd; 1007162856Sdes 100860573Skris printf("fwd %s", inet_ntoa(s->sa.sin_addr)); 100992559Sdes if (s->sa.sin_port) 101092559Sdes printf(",%d", s->sa.sin_port); 101192559Sdes } 101292559Sdes break; 101360573Skris 101460573Skris case O_LOG: /* O_LOG is printed last */ 101592559Sdes logptr = (ipfw_insn_log *)cmd; 101692559Sdes break; 1017124207Sdes 1018124207Sdes default: 101992559Sdes printf("** unrecognized action %d len %d", 102092559Sdes cmd->opcode, cmd->len); 102192559Sdes } 102292559Sdes } 102392559Sdes if (logptr) { 102460573Skris if (logptr->max_log > 0) 102592559Sdes printf(" log logamount %d", logptr->max_log); 102660573Skris else 102792559Sdes printf(" log"); 1028124207Sdes } 102960573Skris 103060573Skris /* 103157429Smarkm * then print the body. 1032204917Sdes */ 1033204917Sdes if (rule->_pad & 1) { /* empty rules before options */ 1034204917Sdes if (!do_compact) 1035215116Sdes printf(" ip from any to any"); 1036204917Sdes flags |= HAVE_IP | HAVE_OPTIONS; 1037204917Sdes } 1038204917Sdes 1039204917Sdes if (comment_only) 1040204917Sdes comment = "..."; 1041204917Sdes 1042204917Sdes for (l = rule->act_ofs, cmd = rule->cmd ; 1043204917Sdes l > 0 ; l -= F_LEN(cmd) , cmd += F_LEN(cmd)) { 1044204917Sdes /* useful alias */ 1045204917Sdes ipfw_insn_u32 *cmd32 = (ipfw_insn_u32 *)cmd; 1046204917Sdes 1047204917Sdes if (comment_only) { 1048204917Sdes if (cmd->opcode != O_NOP) 1049204917Sdes continue; 1050204917Sdes printf(" // %s\n", (char *)(cmd + 1)); 1051204917Sdes return; 1052204917Sdes } 1053204917Sdes 105476262Sgreen show_prerequisites(&flags, 0, cmd->opcode); 1055162856Sdes 105692559Sdes switch(cmd->opcode) { 1057162856Sdes case O_PROB: 105876262Sgreen break; /* done already */ 1059106130Sdes 1060192595Sdes case O_PROBE_STATE: 106192559Sdes break; /* no need to print anything here */ 106276262Sgreen 106376262Sgreen case O_MACADDR2: { 106476262Sgreen ipfw_insn_mac *m = (ipfw_insn_mac *)cmd; 106576262Sgreen 106676262Sgreen if ((cmd->len & F_OR) && !or_block) 106776262Sgreen printf(" {"); 106876262Sgreen if (cmd->len & F_NOT) 106976262Sgreen printf(" not"); 107076262Sgreen printf(" MAC"); 107176262Sgreen flags |= HAVE_MAC; 107276262Sgreen print_mac(m->addr, m->mask); 107376262Sgreen print_mac(m->addr + 6, m->mask + 6); 107476262Sgreen } 107576262Sgreen break; 1076192595Sdes 1077192595Sdes case O_MAC_TYPE: 1078192595Sdes if ((cmd->len & F_OR) && !or_block) 1079192595Sdes printf(" {"); 1080192595Sdes print_newports((ipfw_insn_u16 *)cmd, IPPROTO_ETHERTYPE, 1081192595Sdes (flags & HAVE_OPTIONS) ? cmd->opcode : 0); 1082192595Sdes flags |= HAVE_MAC | HAVE_MACTYPE | HAVE_OPTIONS; 1083192595Sdes break; 1084192595Sdes 108576262Sgreen case O_IP_SRC: 108676262Sgreen case O_IP_SRC_MASK: 1087192595Sdes case O_IP_SRC_ME: 1088192595Sdes case O_IP_SRC_SET: 1089192595Sdes show_prerequisites(&flags, HAVE_PROTO, 0); 109076262Sgreen if (!(flags & HAVE_SRCIP)) 109176262Sgreen printf(" from"); 109276262Sgreen if ((cmd->len & F_OR) && !or_block) 109376262Sgreen printf(" {"); 109476262Sgreen print_ip((ipfw_insn_ip *)cmd, 109576262Sgreen (flags & HAVE_OPTIONS) ? " src-ip" : ""); 109676262Sgreen flags |= HAVE_SRCIP; 109776262Sgreen break; 1098192595Sdes 109976262Sgreen case O_IP_DST: 110076262Sgreen case O_IP_DST_MASK: 110176262Sgreen case O_IP_DST_ME: 110276262Sgreen case O_IP_DST_SET: 110376262Sgreen show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); 110476262Sgreen if (!(flags & HAVE_DSTIP)) 110576262Sgreen printf(" to"); 110676262Sgreen if ((cmd->len & F_OR) && !or_block) 110776262Sgreen printf(" {"); 1108192595Sdes print_ip((ipfw_insn_ip *)cmd, 110976262Sgreen (flags & HAVE_OPTIONS) ? " dst-ip" : ""); 111076262Sgreen flags |= HAVE_DSTIP; 111176262Sgreen break; 111276262Sgreen 111376262Sgreen case O_IP_DSTPORT: 111476262Sgreen show_prerequisites(&flags, HAVE_IP, 0); 1115192595Sdes case O_IP_SRCPORT: 1116192595Sdes show_prerequisites(&flags, HAVE_PROTO|HAVE_SRCIP, 0); 1117192595Sdes if ((cmd->len & F_OR) && !or_block) 1118192595Sdes printf(" {"); 1119192595Sdes print_newports((ipfw_insn_u16 *)cmd, proto, 1120192595Sdes (flags & HAVE_OPTIONS) ? cmd->opcode : 0); 1121192595Sdes break; 1122192595Sdes 1123192595Sdes case O_PROTO: { 1124192595Sdes struct protoent *pe; 1125192595Sdes 1126192595Sdes if ((cmd->len & F_OR) && !or_block) 1127192595Sdes printf(" {"); 1128192595Sdes if (cmd->len & F_NOT) 1129192595Sdes printf(" not"); 1130192595Sdes proto = cmd->arg1; 1131192595Sdes pe = getprotobynumber(cmd->arg1); 1132192595Sdes if (flags & HAVE_OPTIONS) 1133192595Sdes printf(" proto"); 1134192595Sdes if (pe) 1135192595Sdes printf(" %s", pe->p_name); 1136192595Sdes else 1137192595Sdes printf(" %u", cmd->arg1); 1138192595Sdes } 1139192595Sdes flags |= HAVE_PROTO; 114076262Sgreen break; 114192559Sdes 1142124207Sdes default: /*options ... */ 1143192595Sdes show_prerequisites(&flags, HAVE_IP | HAVE_OPTIONS, 0); 114476262Sgreen if ((cmd->len & F_OR) && !or_block) 114576262Sgreen printf(" {"); 1146192595Sdes if (cmd->len & F_NOT && cmd->opcode != O_IN) 1147192595Sdes printf(" not"); 114876262Sgreen switch(cmd->opcode) { 114976262Sgreen case O_FRAG: 115076262Sgreen printf(" frag"); 115176262Sgreen break; 115276262Sgreen 115376262Sgreen case O_IN: 1154162856Sdes printf(cmd->len & F_NOT ? " out" : " in"); 115576262Sgreen break; 115676262Sgreen 115776262Sgreen case O_LAYER2: 1158124207Sdes printf(" layer2"); 1159124207Sdes break; 1160124207Sdes case O_XMIT: 1161124207Sdes case O_RECV: 1162124207Sdes case O_VIA: { 1163124207Sdes char const *s; 1164124207Sdes ipfw_insn_if *cmdif = (ipfw_insn_if *)cmd; 1165124207Sdes 1166124207Sdes if (cmd->opcode == O_XMIT) 1167162856Sdes s = "xmit"; 1168124207Sdes else if (cmd->opcode == O_RECV) 1169162856Sdes s = "recv"; 1170124207Sdes else /* if (cmd->opcode == O_VIA) */ 1171124207Sdes s = "via"; 1172124207Sdes if (cmdif->name[0] == '\0') 1173124207Sdes printf(" %s %s", s, 1174124207Sdes inet_ntoa(cmdif->p.ip)); 1175124207Sdes printf(" %s %s", s, cmdif->name); 1176124207Sdes } 1177124207Sdes break; 1178192595Sdes 1179162856Sdes case O_IPID: 1180124207Sdes if (F_LEN(cmd) == 1) 1181124207Sdes printf(" ipid %u", cmd->arg1 ); 1182124207Sdes else 1183124207Sdes print_newports((ipfw_insn_u16 *)cmd, 0, 1184124207Sdes O_IPID); 1185124207Sdes break; 1186124207Sdes 1187124207Sdes case O_IPTTL: 1188126273Sdes if (F_LEN(cmd) == 1) 1189124207Sdes printf(" ipttl %u", cmd->arg1 ); 1190124207Sdes else 1191124207Sdes print_newports((ipfw_insn_u16 *)cmd, 0, 1192124207Sdes O_IPTTL); 1193124207Sdes break; 1194181111Sdes 1195162856Sdes case O_IPVER: 1196124207Sdes printf(" ipver %u", cmd->arg1 ); 1197124207Sdes break; 1198124207Sdes 1199124207Sdes case O_IPPRECEDENCE: 1200124207Sdes printf(" ipprecedence %u", (cmd->arg1) >> 5 ); 1201124207Sdes break; 1202124207Sdes 1203124207Sdes case O_IPLEN: 1204124207Sdes if (F_LEN(cmd) == 1) 1205124207Sdes printf(" iplen %u", cmd->arg1 ); 1206124207Sdes else 1207124207Sdes print_newports((ipfw_insn_u16 *)cmd, 0, 1208124207Sdes O_IPLEN); 1209124207Sdes break; 1210124207Sdes 1211124207Sdes case O_IPOPT: 1212124207Sdes print_flags("ipoptions", cmd, f_ipopts); 1213124207Sdes break; 1214124207Sdes 1215124207Sdes case O_IPTOS: 1216162856Sdes print_flags("iptos", cmd, f_iptos); 1217124207Sdes break; 1218124207Sdes 1219124207Sdes case O_ICMPTYPE: 1220124207Sdes print_icmptypes((ipfw_insn_u32 *)cmd); 1221124207Sdes break; 1222124207Sdes 1223147005Sdes case O_ESTAB: 1224124207Sdes printf(" established"); 1225124207Sdes break; 1226124207Sdes 1227124207Sdes case O_TCPFLAGS: 1228124207Sdes print_flags("tcpflags", cmd, f_tcpflags); 1229124207Sdes break; 1230124207Sdes 1231124207Sdes case O_TCPOPTS: 1232124207Sdes print_flags("tcpoptions", cmd, f_tcpopts); 1233124207Sdes break; 1234124207Sdes 1235124207Sdes case O_TCPWIN: 1236124207Sdes printf(" tcpwin %d", ntohs(cmd->arg1)); 1237124207Sdes break; 1238124207Sdes 1239124207Sdes case O_TCPACK: 1240162856Sdes printf(" tcpack %d", ntohl(cmd32->d[0])); 1241162856Sdes break; 1242162856Sdes 1243162856Sdes case O_TCPSEQ: 1244124207Sdes printf(" tcpseq %d", ntohl(cmd32->d[0])); 1245124207Sdes break; 1246124207Sdes 1247124207Sdes case O_UID: 1248124207Sdes { 1249124207Sdes struct passwd *pwd = getpwuid(cmd32->d[0]); 1250124207Sdes 1251192595Sdes if (pwd) 1252192595Sdes printf(" uid %s", pwd->pw_name); 1253192595Sdes else 1254192595Sdes printf(" uid %u", cmd32->d[0]); 1255192595Sdes } 1256192595Sdes break; 1257192595Sdes 1258192595Sdes case O_GID: 1259192595Sdes { 1260192595Sdes struct group *grp = getgrgid(cmd32->d[0]); 1261192595Sdes 1262192595Sdes if (grp) 1263192595Sdes printf(" gid %s", grp->gr_name); 1264192595Sdes else 1265192595Sdes printf(" gid %u", cmd32->d[0]); 1266192595Sdes } 1267124207Sdes break; 1268126273Sdes 1269124207Sdes case O_VERREVPATH: 1270124207Sdes printf(" verrevpath"); 1271124207Sdes break; 1272124207Sdes 1273124207Sdes case O_VERSRCREACH: 1274124207Sdes printf(" versrcreach"); 1275124207Sdes break; 1276124207Sdes 1277124207Sdes case O_IPSEC: 1278124207Sdes printf(" ipsec"); 1279162856Sdes break; 1280162856Sdes 1281162856Sdes case O_NOP: 1282124207Sdes comment = (char *)(cmd + 1); 1283124207Sdes break; 1284124207Sdes 1285204917Sdes case O_KEEP_STATE: 1286204917Sdes printf(" keep-state"); 1287204917Sdes break; 1288204917Sdes 1289204917Sdes case O_LIMIT: 1290204917Sdes { 1291204917Sdes struct _s_x *p = limit_masks; 1292204917Sdes ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; 1293204917Sdes uint8_t x = c->limit_mask; 1294204917Sdes char const *comma = " "; 1295204917Sdes 1296204917Sdes printf(" limit"); 1297204917Sdes for (; p->x != 0 ; p++) 1298204917Sdes if ((x & p->x) == p->x) { 1299204917Sdes x &= ~p->x; 1300204917Sdes printf("%s%s", comma, p->s); 1301204917Sdes comma = ","; 1302204917Sdes } 1303204917Sdes printf(" %d", c->conn_limit); 1304204917Sdes } 1305204917Sdes break; 1306204917Sdes 1307204917Sdes default: 1308204917Sdes printf(" [opcode %d len %d]", 130976262Sgreen cmd->opcode, cmd->len); 131092559Sdes } 1311162856Sdes } 131276262Sgreen if (cmd->len & F_OR) { 131376262Sgreen printf(" or"); 1314149753Sdes or_block = 1; 1315149753Sdes } else if (or_block) { 131676262Sgreen printf(" }"); 131776262Sgreen or_block = 0; 131876262Sgreen } 131976262Sgreen } 132076262Sgreen show_prerequisites(&flags, HAVE_IP, 0); 1321124207Sdes if (comment) 132276262Sgreen printf(" // %s", comment); 132376262Sgreen printf("\n"); 132476262Sgreen} 132576262Sgreen 132676262Sgreenstatic void 132776262Sgreenshow_dyn_ipfw(ipfw_dyn_rule *d, int pcwidth, int bcwidth) 132876262Sgreen{ 132976262Sgreen struct protoent *pe; 133076262Sgreen struct in_addr a; 133176262Sgreen uint16_t rulenum; 1332124207Sdes 1333124207Sdes if (!do_expired) { 1334124207Sdes if (!d->expire && !(d->dyn_type == O_LIMIT_PARENT)) 133576262Sgreen return; 133676262Sgreen } 133776262Sgreen bcopy(&d->rule, &rulenum, sizeof(rulenum)); 133876262Sgreen printf("%05d", rulenum); 133976262Sgreen if (pcwidth>0 || bcwidth>0) 134092559Sdes printf(" %*llu %*llu (%ds)", pcwidth, 134176262Sgreen align_uint64(&d->pcnt), bcwidth, 134276262Sgreen align_uint64(&d->bcnt), d->expire); 134376262Sgreen switch (d->dyn_type) { 134476262Sgreen case O_LIMIT_PARENT: 134576262Sgreen printf(" PARENT %d", d->count); 134676262Sgreen break; 134776262Sgreen case O_LIMIT: 134876262Sgreen printf(" LIMIT"); 134976262Sgreen break; 135076262Sgreen case O_KEEP_STATE: /* bidir, no mask */ 135176262Sgreen printf(" STATE"); 135260573Skris break; 1353162856Sdes } 135492559Sdes 1355162856Sdes if ((pe = getprotobynumber(d->id.proto)) != NULL) 135660573Skris printf(" %s", pe->p_name); 135792559Sdes else 1358181111Sdes printf(" proto %u", d->id.proto); 135992559Sdes 136060573Skris a.s_addr = htonl(d->id.src_ip); 136176262Sgreen printf(" %s %d", inet_ntoa(a), d->id.src_port); 136260573Skris 136357429Smarkm a.s_addr = htonl(d->id.dst_ip); 136460573Skris printf(" <-> %s %d", inet_ntoa(a), d->id.dst_port); 136560573Skris printf("\n"); 136660573Skris} 1367181111Sdes 136892559Sdesstatic int 1369124207Sdessort_q(const void *pa, const void *pb) 137092559Sdes{ 137192559Sdes int rev = (do_sort < 0); 137292559Sdes int field = rev ? -do_sort : do_sort; 137360573Skris long long res = 0; 137460573Skris const struct dn_flow_queue *a = pa; 137560573Skris const struct dn_flow_queue *b = pb; 137660573Skris 137792559Sdes switch (field) { 137876262Sgreen case 1: /* pkts */ 137960573Skris res = a->len - b->len; 138060573Skris break; 138176262Sgreen case 2: /* bytes */ 138257429Smarkm res = a->len_bytes - b->len_bytes; 138392559Sdes break; 138460573Skris 1385124207Sdes case 3: /* tot pkts */ 138660573Skris res = a->tot_pkts - b->tot_pkts; 138760573Skris break; 138860573Skris 138992559Sdes case 4: /* tot bytes */ 139092559Sdes res = a->tot_bytes - b->tot_bytes; 139192559Sdes break; 139276262Sgreen } 139376262Sgreen if (res < 0) 139460573Skris res = -1; 1395124207Sdes if (res > 0) 139657429Smarkm res = 1; 139760573Skris return (int)(rev ? res : -res); 139857429Smarkm} 139960573Skris 140060573Skrisstatic void 140160573Skrislist_queues(struct dn_flow_set *fs, struct dn_flow_queue *q) 140292559Sdes{ 140392559Sdes int l; 140492559Sdes 140592559Sdes printf(" mask: 0x%02x 0x%08x/0x%04x -> 0x%08x/0x%04x\n", 140660573Skris fs->flow_mask.proto, 140757429Smarkm fs->flow_mask.src_ip, fs->flow_mask.src_port, 140876262Sgreen fs->flow_mask.dst_ip, fs->flow_mask.dst_port); 140957429Smarkm if (fs->rq_elements == 0) 141057429Smarkm return; 141157429Smarkm 141292559Sdes printf("BKT Prot ___Source IP/port____ " 141376262Sgreen "____Dest. IP/port____ Tot_pkt/bytes Pkt/Byte Drp\n"); 141476262Sgreen if (do_sort != 0) 141576262Sgreen heapsort(q, fs->rq_elements, sizeof *q, sort_q); 141676262Sgreen for (l = 0; l < fs->rq_elements; l++) { 141776262Sgreen struct in_addr ina; 1418149753Sdes struct protoent *pe; 141976262Sgreen 1420204917Sdes ina.s_addr = htonl(q[l].id.src_ip); 1421204917Sdes printf("%3d ", q[l].hash_slot); 1422204917Sdes pe = getprotobynumber(q[l].id.proto); 1423204917Sdes if (pe) 1424204917Sdes printf("%-4s ", pe->p_name); 1425204917Sdes else 1426204917Sdes printf("%4u ", q[l].id.proto); 142776262Sgreen printf("%15s/%-5d ", 142876262Sgreen inet_ntoa(ina), q[l].id.src_port); 142976262Sgreen ina.s_addr = htonl(q[l].id.dst_ip); 143076262Sgreen printf("%15s/%-5d ", 143176262Sgreen inet_ntoa(ina), q[l].id.dst_port); 143276262Sgreen printf("%4qu %8qu %2u %4u %3u\n", 143376262Sgreen q[l].tot_pkts, q[l].tot_bytes, 143476262Sgreen q[l].len, q[l].len_bytes, q[l].drops); 143576262Sgreen if (verbose) 143676262Sgreen printf(" S %20qd F %20qd\n", 143776262Sgreen q[l].S, q[l].F); 143876262Sgreen } 143976262Sgreen} 144076262Sgreen 144176262Sgreenstatic void 144276262Sgreenprint_flowset_parms(struct dn_flow_set *fs, char *prefix) 144376262Sgreen{ 144476262Sgreen int l; 144576262Sgreen char qs[30]; 144676262Sgreen char plr[30]; 144776262Sgreen char red[90]; /* Display RED parameters */ 144876262Sgreen 144976262Sgreen l = fs->qsize; 145076262Sgreen if (fs->flags_fs & DN_QSIZE_IS_BYTES) { 145176262Sgreen if (l >= 8192) 145276262Sgreen sprintf(qs, "%d KB", l / 1024); 145376262Sgreen else 145476262Sgreen sprintf(qs, "%d B", l); 1455149753Sdes } else 145676262Sgreen sprintf(qs, "%3d sl.", l); 145776262Sgreen if (fs->plr) 145876262Sgreen sprintf(plr, "plr %f", 1.0 * fs->plr / (double)(0x7fffffff)); 145976262Sgreen else 146076262Sgreen plr[0] = '\0'; 146176262Sgreen if (fs->flags_fs & DN_IS_RED) /* RED parameters */ 146292559Sdes sprintf(red, 146392559Sdes "\n\t %cRED w_q %f min_th %d max_th %d max_p %f", 146476262Sgreen (fs->flags_fs & DN_IS_GENTLE_RED) ? 'G' : ' ', 146576262Sgreen 1.0 * fs->w_q / (double)(1 << SCALE_RED), 146676262Sgreen SCALE_VAL(fs->min_th), 146776262Sgreen SCALE_VAL(fs->max_th), 146876262Sgreen 1.0 * fs->max_p / (double)(1 << SCALE_RED)); 146976262Sgreen else 1470157019Sdes sprintf(red, "droptail"); 1471157019Sdes 1472157019Sdes printf("%s %s%s %d queues (%d buckets) %s\n", 1473157019Sdes prefix, qs, plr, fs->rq_elements, fs->rq_size, red); 1474157019Sdes} 1475157019Sdes 1476157019Sdesstatic void 1477157019Sdeslist_pipes(void *data, uint nbytes, int ac, char *av[]) 1478157019Sdes{ 1479157019Sdes int rulenum; 1480157019Sdes void *next = data; 1481157019Sdes struct dn_pipe *p = (struct dn_pipe *) data; 1482157019Sdes struct dn_flow_set *fs; 148357429Smarkm struct dn_flow_queue *q; 148460573Skris int l; 148557429Smarkm 1486162856Sdes if (ac > 0) 148792559Sdes rulenum = strtoul(*av++, NULL, 10); 1488162856Sdes else 148957429Smarkm rulenum = 0; 149076262Sgreen for (; nbytes >= sizeof *p; p = (struct dn_pipe *)next) { 1491181111Sdes double b = p->bandwidth; 149292559Sdes char buf[30]; 149357429Smarkm char prefix[80]; 149476262Sgreen 149557429Smarkm if (p->next != (struct dn_pipe *)DN_IS_PIPE) 149660573Skris break; /* done with pipes, now queues */ 149760573Skris 149860573Skris /* 149960573Skris * compute length, as pipe have variable size 150076262Sgreen */ 150192559Sdes l = sizeof(*p) + p->fs.rq_elements * sizeof(*q); 150292559Sdes next = (char *)p + l; 150392559Sdes nbytes -= l; 150492559Sdes 150592559Sdes if (rulenum != 0 && rulenum != p->pipe_nr) 150692559Sdes continue; 150792559Sdes 150892559Sdes /* 150992559Sdes * Print rate (or clocking interface) 151092559Sdes */ 151192559Sdes if (p->if_name[0] != '\0') 151292559Sdes sprintf(buf, "%s", p->if_name); 151376262Sgreen else if (b == 0) 151460573Skris sprintf(buf, "unlimited"); 1515181111Sdes else if (b >= 1000000) 151660573Skris sprintf(buf, "%7.3f Mbit/s", b/1000000); 151760573Skris else if (b >= 1000) 151860573Skris sprintf(buf, "%7.3f Kbit/s", b/1000); 151960573Skris else 152092559Sdes sprintf(buf, "%7.3f bit/s ", b); 1521124207Sdes 1522124207Sdes sprintf(prefix, "%05d: %s %4d ms ", 152376262Sgreen p->pipe_nr, buf, p->delay); 152476262Sgreen print_flowset_parms(&(p->fs), prefix); 1525192595Sdes if (verbose) 1526192595Sdes printf(" V %20qd\n", p->V >> MY_M); 152776262Sgreen 1528204917Sdes q = (struct dn_flow_queue *)(p+1); 152976262Sgreen list_queues(&(p->fs), q); 153060573Skris } 153160573Skris for (fs = next; nbytes >= sizeof *fs; fs = next) { 153257429Smarkm char prefix[80]; 153360573Skris 153460573Skris if (fs->next != (struct dn_flow_set *)DN_IS_QUEUE) 153560573Skris break; 153660573Skris l = sizeof(*fs) + fs->rq_elements * sizeof(*q); 1537162856Sdes next = (char *)fs + l; 153892559Sdes nbytes -= l; 1539162856Sdes q = (struct dn_flow_queue *)(fs+1); 154060573Skris sprintf(prefix, "q%05d: weight %d pipe %d ", 154192559Sdes fs->fs_nr, fs->weight, fs->parent_nr); 154292559Sdes print_flowset_parms(fs, prefix); 1543181111Sdes list_queues(fs, q); 154460573Skris } 154557429Smarkm} 154660573Skris 154760573Skris/* 1548181111Sdes * This one handles all set-related commands 154960573Skris * ipfw set { show | enable | disable } 155060573Skris * ipfw set swap X Y 155160573Skris * ipfw set move X to Y 155260573Skris * ipfw set move rule X to Y 155392559Sdes */ 155476262Sgreenstatic void 155576262Sgreensets_handler(int ac, char *av[]) 1556124207Sdes{ 155776262Sgreen uint32_t set_disable, masks[2]; 155876262Sgreen int i, nbytes; 155976262Sgreen uint16_t rulenum; 156092559Sdes uint8_t cmd, new_set; 156176262Sgreen 156276262Sgreen ac--; 156376262Sgreen av++; 156476262Sgreen 156592559Sdes if (!ac) 156676262Sgreen errx(EX_USAGE, "set needs command"); 156760573Skris if (!strncmp(*av, "show", strlen(*av)) ) { 156860573Skris void *data; 156960573Skris char const *msg; 157057429Smarkm 1571162856Sdes nbytes = sizeof(struct ip_fw); 157292559Sdes if ((data = calloc(1, nbytes)) == NULL) 1573162856Sdes err(EX_OSERR, "calloc"); 157476262Sgreen if (do_cmd(IP_FW_GET, data, (uintptr_t)&nbytes) < 0) 1575181111Sdes err(EX_OSERR, "getsockopt(IP_FW_GET)"); 157692559Sdes bcopy(&((struct ip_fw *)data)->next_rule, 157792559Sdes &set_disable, sizeof(set_disable)); 157876262Sgreen 157992559Sdes for (i = 0, msg = "disable" ; i < RESVD_SET; i++) 158092559Sdes if ((set_disable & (1<<i))) { 158192559Sdes printf("%s %d", msg, i); 158292559Sdes msg = ""; 158392559Sdes } 1584181111Sdes msg = (set_disable) ? " enable" : "enable"; 1585181111Sdes for (i = 0; i < RESVD_SET; i++) 1586181111Sdes if (!(set_disable & (1<<i))) { 158792559Sdes printf("%s %d", msg, i); 158892559Sdes msg = ""; 158992559Sdes } 159092559Sdes printf("\n"); 159192559Sdes } else if (!strncmp(*av, "swap", strlen(*av))) { 159292559Sdes ac--; av++; 159392559Sdes if (ac != 2) 159492559Sdes errx(EX_USAGE, "set swap needs 2 set numbers\n"); 159592559Sdes rulenum = atoi(av[0]); 159692559Sdes new_set = atoi(av[1]); 159792559Sdes if (!isdigit(*(av[0])) || rulenum > RESVD_SET) 159892559Sdes errx(EX_DATAERR, "invalid set number %s\n", av[0]); 159976262Sgreen if (!isdigit(*(av[1])) || new_set > RESVD_SET) 1600181111Sdes errx(EX_DATAERR, "invalid set number %s\n", av[1]); 160192559Sdes masks[0] = (4 << 24) | (new_set << 16) | (rulenum); 1602181111Sdes i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); 1603181111Sdes } else if (!strncmp(*av, "move", strlen(*av))) { 1604181111Sdes ac--; av++; 1605181111Sdes if (ac && !strncmp(*av, "rule", strlen(*av))) { 1606181111Sdes cmd = 2; 1607181111Sdes ac--; av++; 1608181111Sdes } else 1609181111Sdes cmd = 3; 1610181111Sdes if (ac != 3 || strncmp(av[1], "to", strlen(*av))) 1611181111Sdes errx(EX_USAGE, "syntax: set move [rule] X to Y\n"); 1612181111Sdes rulenum = atoi(av[0]); 161392559Sdes new_set = atoi(av[2]); 161492559Sdes if (!isdigit(*(av[0])) || (cmd == 3 && rulenum > RESVD_SET) || 161592559Sdes (cmd == 2 && rulenum == 65535) ) 161692559Sdes errx(EX_DATAERR, "invalid source number %s\n", av[0]); 161792559Sdes if (!isdigit(*(av[2])) || new_set > RESVD_SET) 161892559Sdes errx(EX_DATAERR, "invalid dest. set %s\n", av[1]); 161992559Sdes masks[0] = (cmd << 24) | (new_set << 16) | (rulenum); 162092559Sdes i = do_cmd(IP_FW_DEL, masks, sizeof(uint32_t)); 162176262Sgreen } else if (!strncmp(*av, "disable", strlen(*av)) || 162292559Sdes !strncmp(*av, "enable", strlen(*av)) ) { 162392559Sdes int which = !strncmp(*av, "enable", strlen(*av)) ? 1 : 0; 162476262Sgreen 162592559Sdes ac--; av++; 162676262Sgreen masks[0] = masks[1] = 0; 162792559Sdes 162876262Sgreen while (ac) { 162976262Sgreen if (isdigit(**av)) { 163076262Sgreen i = atoi(*av); 1631162856Sdes if (i < 0 || i > RESVD_SET) 163292559Sdes errx(EX_DATAERR, 1633162856Sdes "invalid set number %d\n", i); 163460573Skris masks[which] |= (1<<i); 1635147005Sdes } else if (!strncmp(*av, "disable", strlen(*av))) 1636181111Sdes which = 0; 163757429Smarkm else if (!strncmp(*av, "enable", strlen(*av))) 1638181111Sdes which = 1; 1639181111Sdes else 1640162856Sdes errx(EX_DATAERR, 164160573Skris "invalid set command %s\n", *av); 1642181111Sdes av++; ac--; 1643181111Sdes } 164460573Skris if ( (masks[0] & masks[1]) != 0 ) 1645162856Sdes errx(EX_DATAERR, 164678827Sgreen "cannot enable and disable the same set\n"); 1647162856Sdes 1648162856Sdes i = do_cmd(IP_FW_DEL, masks, sizeof(masks)); 1649162856Sdes if (i) 1650162856Sdes warn("set enable/disable: setsockopt(IP_FW_DEL)"); 1651124207Sdes } else 165260573Skris errx(EX_USAGE, "invalid set command %s\n", *av); 165376262Sgreen} 1654124207Sdes 165592559Sdesstatic void 165676262Sgreensysctl_handler(int ac, char *av[], int which) 165776262Sgreen{ 165892559Sdes ac--; 165960573Skris av++; 1660124207Sdes 166160573Skris if (ac == 0) { 166260573Skris warnx("missing keyword to enable/disable\n"); 166357429Smarkm } else if (strncmp(*av, "firewall", strlen(*av)) == 0) { 166460573Skris sysctlbyname("net.inet.ip.fw.enable", NULL, 0, 166560573Skris &which, sizeof(which)); 166692559Sdes } else if (strncmp(*av, "one_pass", strlen(*av)) == 0) { 166765668Skris sysctlbyname("net.inet.ip.fw.one_pass", NULL, 0, 1668124207Sdes &which, sizeof(which)); 166965668Skris } else if (strncmp(*av, "debug", strlen(*av)) == 0) { 167065668Skris sysctlbyname("net.inet.ip.fw.debug", NULL, 0, 1671157019Sdes &which, sizeof(which)); 1672157019Sdes } else if (strncmp(*av, "verbose", strlen(*av)) == 0) { 167365668Skris sysctlbyname("net.inet.ip.fw.verbose", NULL, 0, 167465668Skris &which, sizeof(which)); 167565668Skris } else if (strncmp(*av, "dyn_keepalive", strlen(*av)) == 0) { 167660573Skris sysctlbyname("net.inet.ip.fw.dyn_keepalive", NULL, 0, 167760573Skris &which, sizeof(which)); 167860573Skris } else { 1679162856Sdes warnx("unrecognize enable/disable keyword: %s\n", *av); 1680162856Sdes } 168192559Sdes} 1682162856Sdes 168360573Skrisstatic void 168476262Sgreenlist(int ac, char *av[], int show_counters) 1685157019Sdes{ 1686215116Sdes struct ip_fw *r; 168760573Skris ipfw_dyn_rule *dynrules, *d; 168860573Skris 168960573Skris#define NEXT(r) ((struct ip_fw *)((char *)r + RULESIZE(r))) 169060573Skris char *lim; 169160573Skris void *data = NULL; 169260573Skris int bcwidth, n, nbytes, nstat, ndyn, pcwidth, width; 1693215116Sdes int exitval = EX_OK; 1694157019Sdes int lac; 1695157019Sdes char **lav; 1696157019Sdes u_long rnum, last; 1697157019Sdes char *endptr; 1698157019Sdes int seen = 0; 1699157019Sdes 1700157019Sdes const int ocmd = do_pipe ? IP_DUMMYNET_GET : IP_FW_GET; 1701157019Sdes int nalloc = 1024; /* start somewhere... */ 1702157019Sdes 1703157019Sdes if (test_only) { 1704157019Sdes fprintf(stderr, "Testing only, list disabled\n"); 1705157019Sdes return; 1706157019Sdes } 1707157019Sdes 1708157019Sdes ac--; 1709157019Sdes av++; 1710157019Sdes 1711157019Sdes /* get rules or pipes from kernel, resizing array as necessary */ 1712157019Sdes nbytes = nalloc; 1713157019Sdes 1714181111Sdes while (nbytes >= nalloc) { 1715181111Sdes nalloc = nalloc * 2 + 200; 1716157019Sdes nbytes = nalloc; 1717157019Sdes if ((data = realloc(data, nbytes)) == NULL) 1718157019Sdes err(EX_OSERR, "realloc"); 1719157019Sdes if (do_cmd(ocmd, data, (uintptr_t)&nbytes) < 0) 1720157019Sdes err(EX_OSERR, "getsockopt(IP_%s_GET)", 1721157019Sdes do_pipe ? "DUMMYNET" : "FW"); 1722157019Sdes } 1723157019Sdes 1724215116Sdes if (do_pipe) { 1725157019Sdes list_pipes(data, nbytes, ac, av); 1726106130Sdes goto done; 1727126273Sdes } 1728126273Sdes 1729126273Sdes /* 1730106130Sdes * Count static rules. They have variable size so we 1731157019Sdes * need to scan the list to count them. 1732157019Sdes */ 1733181111Sdes for (nstat = 1, r = data, lim = (char *)data + nbytes; 1734181111Sdes r->rulenum < 65535 && (char *)r < lim; 173560573Skris ++nstat, r = NEXT(r) ) 173660573Skris ; /* nothing */ 173776262Sgreen 1738124207Sdes /* 173992559Sdes * Count dynamic rules. This is easier as they have 174076262Sgreen * fixed size. 174176262Sgreen */ 174292559Sdes r = NEXT(r); 1743124207Sdes dynrules = (ipfw_dyn_rule *)r ; 174460573Skris n = (char *)r - (char *)data; 174560573Skris ndyn = (nbytes - n) / sizeof *dynrules; 174660573Skris 174757429Smarkm /* if showing stats, figure out column widths ahead of time */ 174860573Skris bcwidth = pcwidth = 0; 174960573Skris if (show_counters) { 1750197679Sdes for (n = 0, r = data; n < nstat; n++, r = NEXT(r)) { 1751157019Sdes /* packet counter */ 175274500Sgreen width = snprintf(NULL, 0, "%llu", 175374500Sgreen align_uint64(&r->pcnt)); 175474500Sgreen if (width > pcwidth) 175574500Sgreen pcwidth = width; 175676262Sgreen 175776262Sgreen /* byte counter */ 1758157019Sdes width = snprintf(NULL, 0, "%llu", 175974500Sgreen align_uint64(&r->bcnt)); 176076262Sgreen if (width > bcwidth) 176174500Sgreen bcwidth = width; 176274500Sgreen } 176374500Sgreen } 1764197679Sdes if (do_dynamic && ndyn) { 176560573Skris for (n = 0, d = dynrules; n < ndyn; n++, d++) { 176660573Skris width = snprintf(NULL, 0, "%llu", 1767215116Sdes align_uint64(&d->pcnt)); 1768215116Sdes if (width > pcwidth) 1769215116Sdes pcwidth = width; 177060573Skris 177160573Skris width = snprintf(NULL, 0, "%llu", 1772162856Sdes align_uint64(&d->bcnt)); 177392559Sdes if (width > bcwidth) 1774162856Sdes bcwidth = width; 177560573Skris } 1776147005Sdes } 177760573Skris /* if no rule numbers were specified, list all rules */ 177857429Smarkm if (ac == 0) { 177960573Skris for (n = 0, r = data; n < nstat; n++, r = NEXT(r) ) 178060573Skris show_ipfw(r, pcwidth, bcwidth); 178160573Skris 178260573Skris if (do_dynamic && ndyn) { 178360573Skris printf("## Dynamic rules (%d):\n", ndyn); 178460573Skris for (n = 0, d = dynrules; n < ndyn; n++, d++) 178560573Skris show_dyn_ipfw(d, pcwidth, bcwidth); 178669587Sgreen } 178760573Skris goto done; 1788181111Sdes } 1789181111Sdes 179076262Sgreen /* display specific rules requested on command line */ 179176262Sgreen 179276262Sgreen for (lac = ac, lav = av; lac != 0; lac--) { 179376262Sgreen /* convert command line rule # */ 179492559Sdes last = rnum = strtoul(*lav++, &endptr, 10); 179576262Sgreen if (*endptr == '-') 179660573Skris last = strtoul(endptr+1, &endptr, 10); 179760573Skris if (*endptr) { 179857429Smarkm exitval = EX_USAGE; 1799215116Sdes warnx("invalid rule number: %s", *(lav - 1)); 1800215116Sdes continue; 1801215116Sdes } 1802181111Sdes for (n = seen = 0, r = data; n < nstat; n++, r = NEXT(r) ) { 180360573Skris if (r->rulenum > last) 180469587Sgreen break; 180592559Sdes if (r->rulenum >= rnum && r->rulenum <= last) { 1806181111Sdes show_ipfw(r, pcwidth, bcwidth); 1807181111Sdes seen = 1; 180876262Sgreen } 180976262Sgreen } 181076262Sgreen if (!seen) { 181160573Skris /* give precedence to other error(s) */ 181292559Sdes if (exitval == EX_OK) 181376262Sgreen exitval = EX_UNAVAILABLE; 1814215116Sdes warnx("rule %lu does not exist", rnum); 1815215116Sdes } 1816215116Sdes } 1817215116Sdes 1818215116Sdes if (do_dynamic && ndyn) { 181976262Sgreen printf("## Dynamic rules:\n"); 182060573Skris for (lac = ac, lav = av; lac != 0; lac--) { 182160573Skris rnum = strtoul(*lav++, &endptr, 10); 182260573Skris if (*endptr == '-') 182360573Skris last = strtoul(endptr+1, &endptr, 10); 1824162856Sdes if (*endptr) 182592559Sdes /* already warned */ 182676262Sgreen continue; 182760573Skris for (n = 0, d = dynrules; n < ndyn; n++, d++) { 182876262Sgreen uint16_t rulenum; 182976262Sgreen 1830181111Sdes bcopy(&d->rule, &rulenum, sizeof(rulenum)); 1831181111Sdes if (rulenum > rnum) 1832181111Sdes break; 183360573Skris if (r->rulenum >= rnum && r->rulenum <= last) 1834224638Sbrooks show_dyn_ipfw(d, pcwidth, bcwidth); 1835224638Sbrooks } 1836224638Sbrooks } 1837224638Sbrooks } 1838224638Sbrooks 1839224638Sbrooks ac = 0; 1840224638Sbrooks 1841224638Sbrooksdone: 1842224638Sbrooks free(data); 1843224638Sbrooks 1844224638Sbrooks if (exitval != EX_OK) 184560573Skris exit(exitval); 184660573Skris#undef NEXT 1847224638Sbrooks} 184860573Skris 184969587Sgreenstatic void 185060573Skrisshow_usage(void) 185160573Skris{ 1852224638Sbrooks fprintf(stderr, "usage: ipfw [options]\n" 185360573Skris"do \"ipfw -h\" or see ipfw manpage for details\n" 185460573Skris); 185560573Skris exit(EX_USAGE); 185660573Skris} 185757429Smarkm 185892559Sdesstatic void 1859162856Sdeshelp(void) 186060573Skris{ 186160573Skris fprintf(stderr, 186260573Skris"ipfw syntax summary (but please do read the ipfw(8) manpage):\n" 186392559Sdes"ipfw [-abcdefhnNqStTv] <command> where <command> is one of:\n" 186492559Sdes"add [num] [set N] [prob x] RULE-BODY\n" 186560573Skris"{pipe|queue} N config PIPE-BODY\n" 186676262Sgreen"[pipe|queue] {zero|delete|show} [N{,N}]\n" 186760573Skris"set [disable N... enable N...] | move [rule] X to Y | swap X Y | show\n" 186860573Skris"\n" 1869204917Sdes"RULE-BODY: check-state [LOG] | ACTION [LOG] ADDR [OPTION_LIST]\n" 1870204917Sdes"ACTION: check-state | allow | count | deny | reject | skipto N |\n" 1871204917Sdes" {divert|tee} PORT | forward ADDR | pipe N | queue N\n" 1872204917Sdes"ADDR: [ MAC dst src ether_type ] \n" 1873204917Sdes" [ from IPADDR [ PORT ] to IPADDR [ PORTLIST ] ]\n" 1874204917Sdes"IPADDR: [not] { any | me | ip/bits{x,y,z} | IPLIST }\n" 1875204917Sdes"IPLIST: { ip | ip/bits | ip:mask }[,IPLIST]\n" 1876204917Sdes"OPTION_LIST: OPTION [OPTION_LIST]\n" 1877204917Sdes"OPTION: bridged | {dst-ip|src-ip} ADDR | {dst-port|src-port} LIST |\n" 1878204917Sdes" estab | frag | {gid|uid} N | icmptypes LIST | in | out | ipid LIST |\n" 1879204917Sdes" iplen LIST | ipoptions SPEC | ipprecedence | ipsec | iptos SPEC |\n" 1880204917Sdes" ipttl LIST | ipversion VER | keep-state | layer2 | limit ... |\n" 1881204917Sdes" mac ... | mac-type LIST | proto LIST | {recv|xmit|via} {IF|IPADDR} |\n" 1882204917Sdes" setup | {tcpack|tcpseq|tcpwin} NN | tcpflags SPEC | tcpoptions SPEC |\n" 1883204917Sdes" verrevpath | versrcreach\n" 1884204917Sdes); 1885204917Sdesexit(0); 1886204917Sdes} 1887204917Sdes 1888204917Sdes 1889204917Sdesstatic int 1890204917Sdeslookup_host (char *host, struct in_addr *ipaddr) 1891204917Sdes{ 1892204917Sdes struct hostent *he; 1893204917Sdes 1894204917Sdes if (!inet_aton(host, ipaddr)) { 1895204917Sdes if ((he = gethostbyname(host)) == NULL) 1896204917Sdes return(-1); 1897204917Sdes *ipaddr = *(struct in_addr *)he->h_addr_list[0]; 1898204917Sdes } 1899204917Sdes return(0); 1900204917Sdes} 1901215116Sdes 1902204917Sdes/* 1903204917Sdes * fills the addr and mask fields in the instruction as appropriate from av. 1904204917Sdes * Update length as appropriate. 1905204917Sdes * The following formats are allowed: 1906204917Sdes * any matches any IP. Actually returns an empty instruction. 1907204917Sdes * me returns O_IP_*_ME 1908204917Sdes * 1.2.3.4 single IP address 1909204917Sdes * 1.2.3.4:5.6.7.8 address:mask 1910204917Sdes * 1.2.3.4/24 address/mask 1911204917Sdes * 1.2.3.4/26{1,6,5,4,23} set of addresses in a subnet 1912204917Sdes * We can have multiple comma-separated address/mask entries. 1913204917Sdes */ 1914204917Sdesstatic void 1915204917Sdesfill_ip(ipfw_insn_ip *cmd, char *av) 1916204917Sdes{ 1917204917Sdes int len = 0; 1918204917Sdes uint32_t *d = ((ipfw_insn_u32 *)cmd)->d; 1919204917Sdes 1920204917Sdes cmd->o.len &= ~F_LEN_MASK; /* zero len */ 1921204917Sdes 1922204917Sdes if (!strncmp(av, "any", strlen(av))) 1923204917Sdes return; 1924204917Sdes 1925204917Sdes if (!strncmp(av, "me", strlen(av))) { 1926204917Sdes cmd->o.len |= F_INSN_SIZE(ipfw_insn); 1927204917Sdes return; 1928204917Sdes } 1929204917Sdes 1930204917Sdes while (av) { 1931204917Sdes /* 1932204917Sdes * After the address we can have '/' or ':' indicating a mask, 1933204917Sdes * ',' indicating another address follows, '{' indicating a 1934204917Sdes * set of addresses of unspecified size. 1935204917Sdes */ 1936204917Sdes char *p = strpbrk(av, "/:,{"); 1937204917Sdes int masklen; 1938204917Sdes char md; 1939204917Sdes 1940204917Sdes if (p) { 1941204917Sdes md = *p; 1942204917Sdes *p++ = '\0'; 1943204917Sdes } else 1944204917Sdes md = '\0'; 1945204917Sdes 1946204917Sdes if (lookup_host(av, (struct in_addr *)&d[0]) != 0) 1947204917Sdes errx(EX_NOHOST, "hostname ``%s'' unknown", av); 1948204917Sdes switch (md) { 1949204917Sdes case ':': 1950204917Sdes if (!inet_aton(p, (struct in_addr *)&d[1])) 1951204917Sdes errx(EX_DATAERR, "bad netmask ``%s''", p); 1952204917Sdes break; 1953204917Sdes case '/': 1954204917Sdes masklen = atoi(p); 1955204917Sdes if (masklen == 0) 1956204917Sdes d[1] = htonl(0); /* mask */ 1957204917Sdes else if (masklen > 32) 1958204917Sdes errx(EX_DATAERR, "bad width ``%s''", p); 1959204917Sdes else 1960204917Sdes d[1] = htonl(~0 << (32 - masklen)); 1961204917Sdes break; 1962204917Sdes case '{': /* no mask, assume /24 and put back the '{' */ 1963204917Sdes d[1] = htonl(~0 << (32 - 24)); 1964204917Sdes *(--p) = md; 1965204917Sdes break; 1966204917Sdes 1967204917Sdes case ',': /* single address plus continuation */ 1968204917Sdes *(--p) = md; 1969204917Sdes /* FALLTHROUGH */ 1970204917Sdes case 0: /* initialization value */ 1971204917Sdes default: 1972204917Sdes d[1] = htonl(~0); /* force /32 */ 1973204917Sdes break; 1974204917Sdes } 1975204917Sdes d[0] &= d[1]; /* mask base address with mask */ 1976204917Sdes /* find next separator */ 1977204917Sdes if (p) 1978204917Sdes p = strpbrk(p, ",{"); 1979204917Sdes if (p && *p == '{') { 1980204917Sdes /* 1981204917Sdes * We have a set of addresses. They are stored as follows: 1982204917Sdes * arg1 is the set size (powers of 2, 2..256) 1983204917Sdes * addr is the base address IN HOST FORMAT 1984204917Sdes * mask.. is an array of arg1 bits (rounded up to 1985204917Sdes * the next multiple of 32) with bits set 1986204917Sdes * for each host in the map. 1987204917Sdes */ 1988204917Sdes uint32_t *map = (uint32_t *)&cmd->mask; 1989204917Sdes int low, high; 1990204917Sdes int i = contigmask((uint8_t *)&(d[1]), 32); 1991162856Sdes 199292559Sdes if (len > 0) 1993162856Sdes errx(EX_DATAERR, "address set cannot be in a list"); 199460573Skris if (i < 24 || i > 31) 199560573Skris errx(EX_DATAERR, "invalid set with mask %d\n", i); 1996106130Sdes cmd->o.arg1 = 1<<(32-i); /* map length */ 199760573Skris d[0] = ntohl(d[0]); /* base addr in host format */ 199860573Skris cmd->o.opcode = O_IP_DST_SET; /* default */ 199960573Skris cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32) + (cmd->o.arg1+31)/32; 200060573Skris for (i = 0; i < (cmd->o.arg1+31)/32 ; i++) 200160573Skris map[i] = 0; /* clear map */ 200292559Sdes 200360573Skris av = p + 1; 200460573Skris low = d[0] & 0xff; 200560573Skris high = low + cmd->o.arg1 - 1; 200660573Skris /* 200760573Skris * Here, i stores the previous value when we specify a range 200892559Sdes * of addresses within a mask, e.g. 45-63. i = -1 means we 200960573Skris * have no previous value. 201060573Skris */ 201192559Sdes i = -1; /* previous value in a range */ 201260573Skris while (isdigit(*av)) { 201360573Skris char *s; 201476262Sgreen int a = strtol(av, &s, 0); 201560573Skris 201676262Sgreen if (s == av) { /* no parameter */ 201776262Sgreen if (*av != '}') 201876262Sgreen errx(EX_DATAERR, "set not closed\n"); 2019204917Sdes if (i != -1) 2020204917Sdes errx(EX_DATAERR, "incomplete range %d-", i); 202160573Skris break; 202292559Sdes } 202360573Skris if (a < low || a > high) 202476262Sgreen errx(EX_DATAERR, "addr %d out of range [%d-%d]\n", 202560573Skris a, low, high); 202676262Sgreen a -= low; 202776262Sgreen if (i == -1) /* no previous in range */ 202892559Sdes i = a; 2029204917Sdes else { /* check that range is valid */ 2030204917Sdes if (i > a) 203160573Skris errx(EX_DATAERR, "invalid range %d-%d", 203260573Skris i+low, a+low); 203392559Sdes if (*s == '-') 203460573Skris errx(EX_DATAERR, "double '-' in range"); 203560573Skris } 203660573Skris for (; i <= a; i++) 203760573Skris map[i/32] |= 1<<(i & 31); 203860573Skris i = -1; 203960573Skris if (*s == '-') 204060573Skris i = a; 204160573Skris else if (*s == '}') 204260573Skris break; 204376262Sgreen av = s+1; 204476262Sgreen } 204560573Skris return; 204692559Sdes } 204760573Skris av = p; 204860573Skris if (av) /* then *av must be a ',' */ 204960573Skris av++; 205060573Skris 205176262Sgreen /* Check this entry */ 205292559Sdes if (d[1] == 0) { /* "any", specified as x.x.x.x/0 */ 205360573Skris /* 205460573Skris * 'any' turns the entire list into a NOP. 205592559Sdes * 'not any' never matches, so it is removed from the 205660573Skris * list unless it is the only item, in which case we 205760573Skris * report an error. 205892559Sdes */ 205960573Skris if (cmd->o.len & F_NOT) { /* "not any" never matches */ 206060573Skris if (av == NULL && len == 0) /* only this entry */ 206160573Skris errx(EX_DATAERR, "not any never matches"); 206260573Skris } 206376262Sgreen /* else do nothing and skip this entry */ 206476262Sgreen return; 206560573Skris } 206660573Skris /* A single IP can be stored in an optimized format */ 206760573Skris if (d[1] == IP_MASK_ALL && av == NULL && len == 0) { 206860573Skris cmd->o.len |= F_INSN_SIZE(ipfw_insn_u32); 206992559Sdes return; 207076262Sgreen } 207192559Sdes len += 2; /* two words... */ 207260573Skris d += 2; 207360573Skris } /* end while */ 207492559Sdes cmd->o.len |= len+1; 207560573Skris} 207660573Skris 207760573Skris 2078106130Sdes/* 207992559Sdes * helper function to process a set of flags and set bits in the 208060573Skris * appropriate masks. 208160573Skris */ 208260573Skrisstatic void 208360573Skrisfill_flags(ipfw_insn *cmd, enum ipfw_opcodes opcode, 208460573Skris struct _s_x *flags, char *p) 208560573Skris{ 208660573Skris uint8_t set=0, clear=0; 208760573Skris 208860573Skris while (p && *p) { 208960573Skris char *q; /* points to the separator */ 209060573Skris int val; 209192559Sdes uint8_t *which; /* mask we are working on */ 209292559Sdes 209392559Sdes if (*p == '!') { 209492559Sdes p++; 209592559Sdes which = &clear; 209692559Sdes } else 209792559Sdes which = &set; 2098157019Sdes q = strchr(p, ','); 209992559Sdes if (q) 2100124207Sdes *q++ = '\0'; 210192559Sdes val = match_token(flags, p); 210292559Sdes if (val <= 0) 210392559Sdes errx(EX_DATAERR, "invalid flag %s", p); 210492559Sdes *which |= (uint8_t)val; 2105124207Sdes p = q; 210692559Sdes } 210792559Sdes cmd->opcode = opcode; 210892559Sdes cmd->len = (cmd->len & (F_NOT | F_OR)) | 1; 2109124207Sdes cmd->arg1 = (set & 0xff) | ( (clear & 0xff) << 8); 211092559Sdes} 211192559Sdes 211292559Sdes 211392559Sdesstatic void 2114162856Sdesdelete(int ac, char *av[]) 211560573Skris{ 211660573Skris uint32_t rulenum; 2117204917Sdes struct dn_pipe p; 211860573Skris int i; 211960573Skris int exitval = EX_OK; 212060573Skris int do_set = 0; 212160573Skris 212260573Skris memset(&p, 0, sizeof p); 212360573Skris 2124204917Sdes av++; ac--; 212592559Sdes if (ac > 0 && !strncmp(*av, "set", strlen(*av))) { 212692559Sdes do_set = 1; /* delete set */ 212757429Smarkm ac--; av++; 2128204917Sdes } 2129204917Sdes 2130204917Sdes /* Rule number */ 2131204917Sdes while (ac && isdigit(**av)) { 2132204917Sdes i = atoi(*av); av++; ac--; 2133204917Sdes if (do_pipe) { 213492559Sdes if (do_pipe == 1) 213592559Sdes p.pipe_nr = i; 213692559Sdes else 213757429Smarkm p.fs.fs_nr = i; 213857429Smarkm i = do_cmd(IP_DUMMYNET_DEL, &p, sizeof p); 213957429Smarkm if (i) { 214092559Sdes exitval = 1; 214192559Sdes warn("rule %u: setsockopt(IP_DUMMYNET_DEL)", 214292559Sdes do_pipe == 1 ? p.pipe_nr : p.fs.fs_nr); 214392559Sdes } 214460573Skris } else { 214576262Sgreen rulenum = (i & 0xffff) | (do_set << 24); 2146137019Sdes i = do_cmd(IP_FW_DEL, &rulenum, sizeof rulenum); 214760573Skris if (i) { 2148162856Sdes exitval = EX_UNAVAILABLE; 214976262Sgreen warn("rule %u: setsockopt(IP_FW_DEL)", 215076262Sgreen rulenum); 215176262Sgreen } 2152162856Sdes } 2153162856Sdes } 2154162856Sdes if (exitval != EX_OK) 2155162856Sdes exit(exitval); 2156162856Sdes} 2157162856Sdes 215892559Sdes 215992559Sdes/* 2160162856Sdes * fill the interface structure. We do not check the name as we can 2161162856Sdes * create interfaces dynamically, so checking them at insert time 216292559Sdes * makes relatively little sense. 216376262Sgreen * Interface names containing '*', '?', or '[' are assumed to be shell 216492559Sdes * patterns which match interfaces. 216576262Sgreen */ 216676262Sgreenstatic void 216776262Sgreenfill_iface(ipfw_insn_if *cmd, char *arg) 216876262Sgreen{ 216976262Sgreen cmd->name[0] = '\0'; 217060573Skris cmd->o.len |= F_INSN_SIZE(ipfw_insn_if); 217160573Skris 217292559Sdes /* Parse the interface or address */ 217392559Sdes if (!strcmp(arg, "any")) 217492559Sdes cmd->o.len = 0; /* effectively ignore this command */ 217592559Sdes else if (!isdigit(*arg)) { 217660573Skris strlcpy(cmd->name, arg, sizeof(cmd->name)); 2177162856Sdes cmd->p.glob = strpbrk(arg, "*?[") != NULL ? 1 : 0; 217860573Skris } else if (!inet_aton(arg, &cmd->p.ip)) 217960573Skris errx(EX_DATAERR, "bad ip address ``%s''", arg); 218060573Skris} 218160573Skris 218292559Sdes/* 218376262Sgreen * the following macro returns an error message if we run out of 218460573Skris * arguments. 218592559Sdes */ 218657429Smarkm#define NEED1(msg) {if (!ac) errx(EX_USAGE, msg);} 218760573Skris 2188137019Sdesstatic void 218957429Smarkmconfig_pipe(int ac, char **av) 219057429Smarkm{ 219192559Sdes struct dn_pipe p; 219292559Sdes int i; 219392559Sdes char *end; 219457429Smarkm uint32_t a; 219592559Sdes void *par = NULL; 219692559Sdes 219792559Sdes memset(&p, 0, sizeof p); 219892559Sdes 219957429Smarkm av++; ac--; 220060573Skris /* Pipe number */ 220160573Skris if (ac && isdigit(**av)) { 220257429Smarkm i = atoi(*av); av++; ac--; 220357429Smarkm if (do_pipe == 1) 220460573Skris p.pipe_nr = i; 220557429Smarkm else 220657429Smarkm p.fs.fs_nr = i; 220760573Skris } 220860573Skris while (ac > 0) { 220976262Sgreen double d; 221092559Sdes int tok = match_token(dummynet_params, *av); 221160573Skris ac--; av++; 221260573Skris 221357429Smarkm switch(tok) { 221457429Smarkm case TOK_NOERROR: 221576262Sgreen p.fs.flags_fs |= DN_NOERROR; 221676262Sgreen break; 221776262Sgreen 2218157019Sdes case TOK_PLR: 2219157019Sdes NEED1("plr needs argument 0..1\n"); 2220157019Sdes d = strtod(av[0], NULL); 2221157019Sdes if (d > 1) 2222157019Sdes d = 1; 2223157019Sdes else if (d < 0) 2224157019Sdes d = 0; 2225215116Sdes p.fs.plr = (int)(d*0x7fffffff); 2226215116Sdes ac--; av++; 2227215116Sdes break; 2228215116Sdes 2229215116Sdes case TOK_QUEUE: 2230215116Sdes NEED1("queue needs queue size\n"); 2231215116Sdes end = NULL; 2232215116Sdes p.fs.qsize = strtoul(av[0], &end, 0); 2233157019Sdes if (*end == 'K' || *end == 'k') { 2234157019Sdes p.fs.flags_fs |= DN_QSIZE_IS_BYTES; 2235157019Sdes p.fs.qsize *= 1024; 2236157019Sdes } else if (*end == 'B' || !strncmp(end, "by", 2)) { 2237157019Sdes p.fs.flags_fs |= DN_QSIZE_IS_BYTES; 2238157019Sdes } 2239157019Sdes ac--; av++; 2240157019Sdes break; 2241157019Sdes 224292559Sdes case TOK_BUCKETS: 224392559Sdes NEED1("buckets needs argument\n"); 224492559Sdes p.fs.rq_size = strtoul(av[0], NULL, 0); 224592559Sdes ac--; av++; 224660573Skris break; 224760573Skris 224860573Skris case TOK_MASK: 224960573Skris NEED1("mask needs mask specifier\n"); 225060573Skris /* 225157429Smarkm * per-flow queue, mask is dst_ip, dst_port, 225260573Skris * src_ip, src_port, proto measured in bits 225360573Skris */ 225460573Skris par = NULL; 225560573Skris 225660573Skris p.fs.flow_mask.dst_ip = 0; 225760573Skris p.fs.flow_mask.src_ip = 0; 225860573Skris p.fs.flow_mask.dst_port = 0; 225960573Skris p.fs.flow_mask.src_port = 0; 226057429Smarkm p.fs.flow_mask.proto = 0; 226160573Skris end = NULL; 226260573Skris 226360573Skris while (ac >= 1) { 226460573Skris uint32_t *p32 = NULL; 226560573Skris uint16_t *p16 = NULL; 226660573Skris 226760573Skris tok = match_token(dummynet_params, *av); 226860573Skris ac--; av++; 226960573Skris switch(tok) { 227060573Skris case TOK_ALL: 227157429Smarkm /* 227257429Smarkm * special case, all bits significant 227357429Smarkm */ 227457429Smarkm p.fs.flow_mask.dst_ip = ~0; 227598684Sdes p.fs.flow_mask.src_ip = ~0; 227698684Sdes p.fs.flow_mask.dst_port = ~0; 227757429Smarkm p.fs.flow_mask.src_port = ~0; 227898684Sdes p.fs.flow_mask.proto = ~0; 2279149753Sdes p.fs.flags_fs |= DN_HAVE_FLOW_MASK; 2280149753Sdes goto end_mask; 228198684Sdes 228298684Sdes case TOK_DSTIP: 228357429Smarkm p32 = &p.fs.flow_mask.dst_ip; 228460573Skris break; 228560573Skris 228698684Sdes case TOK_SRCIP: 228760573Skris p32 = &p.fs.flow_mask.src_ip; 228860573Skris break; 228960573Skris 229099063Sdes case TOK_DSTPORT: 229176262Sgreen p16 = &p.fs.flow_mask.dst_port; 229276262Sgreen break; 229360573Skris 229460573Skris case TOK_SRCPORT: 229560573Skris p16 = &p.fs.flow_mask.src_port; 229660573Skris break; 229760573Skris 229860573Skris case TOK_PROTO: 229960573Skris break; 230060573Skris 230160573Skris default: 230260573Skris ac++; av--; /* backtrack */ 230360573Skris goto end_mask; 230476262Sgreen } 230560573Skris if (ac < 1) 230657429Smarkm errx(EX_USAGE, "mask: value missing"); 230757429Smarkm if (*av[0] == '/') { 230857429Smarkm a = strtoul(av[0]+1, &end, 0); 230957429Smarkm a = (a == 32) ? ~0 : (1 << a) - 1; 231092559Sdes } else 231192559Sdes a = strtoul(av[0], &end, 0); 2312162856Sdes if (p32 != NULL) 231360573Skris *p32 = a; 231492559Sdes else if (p16 != NULL) { 231557429Smarkm if (a > 65535) 231657429Smarkm errx(EX_DATAERR, 231757429Smarkm "mask: must be 16 bit"); 2318215116Sdes *p16 = (uint16_t)a; 231960573Skris } else { 232057429Smarkm if (a > 255) 232157429Smarkm errx(EX_DATAERR, 232257429Smarkm "mask: must be 8 bit"); 232360573Skris p.fs.flow_mask.proto = (uint8_t)a; 232460573Skris } 232557429Smarkm if (a != 0) 232657429Smarkm p.fs.flags_fs |= DN_HAVE_FLOW_MASK; 232757429Smarkm ac--; av++; 232860573Skris } /* end while, config masks */ 232960573Skrisend_mask: 233057429Smarkm break; 233157429Smarkm 233257429Smarkm case TOK_RED: 2333181111Sdes case TOK_GRED: 2334215116Sdes NEED1("red/gred needs w_q/min_th/max_th/max_p\n"); 2335215116Sdes p.fs.flags_fs |= DN_IS_RED; 2336215116Sdes if (tok == TOK_GRED) 233760573Skris p.fs.flags_fs |= DN_IS_GENTLE_RED; 2338126273Sdes /* 2339126273Sdes * the format for parameters is w_q/min_th/max_th/max_p 2340126273Sdes */ 2341126273Sdes if ((end = strsep(&av[0], "/"))) { 2342126273Sdes double w_q = strtod(end, NULL); 2343126273Sdes if (w_q > 1 || w_q <= 0) 2344126273Sdes errx(EX_DATAERR, "0 < w_q <= 1"); 2345126273Sdes p.fs.w_q = (int) (w_q * (1 << SCALE_RED)); 2346126273Sdes } 2347215116Sdes if ((end = strsep(&av[0], "/"))) { 2348215116Sdes p.fs.min_th = strtoul(end, &end, 0); 2349126273Sdes if (*end == 'K' || *end == 'k') 2350126273Sdes p.fs.min_th *= 1024; 2351126273Sdes } 2352126273Sdes if ((end = strsep(&av[0], "/"))) { 235392559Sdes p.fs.max_th = strtoul(end, &end, 0); 2354215116Sdes if (*end == 'K' || *end == 'k') 2355124207Sdes p.fs.max_th *= 1024; 2356215116Sdes } 235760573Skris if ((end = strsep(&av[0], "/"))) { 2358215116Sdes double max_p = strtod(end, NULL); 2359124207Sdes if (max_p > 1 || max_p <= 0) 2360215116Sdes errx(EX_DATAERR, "0 < max_p <= 1"); 236160573Skris p.fs.max_p = (int)(max_p * (1 << SCALE_RED)); 236260573Skris } 2363215116Sdes ac--; av++; 236460573Skris break; 2365157019Sdes 2366157019Sdes case TOK_DROPTAIL: 2367157019Sdes p.fs.flags_fs &= ~(DN_IS_RED|DN_IS_GENTLE_RED); 2368157019Sdes break; 2369181111Sdes 237057429Smarkm case TOK_BW: 237192559Sdes NEED1("bw needs bandwidth or interface\n"); 2372162856Sdes if (do_pipe != 1) 237360573Skris errx(EX_DATAERR, "bandwidth only valid for pipes"); 237492559Sdes /* 237560573Skris * set clocking interface or bandwidth value 237660573Skris */ 237760573Skris if (av[0][0] >= 'a' && av[0][0] <= 'z') { 237899063Sdes int l = sizeof(p.if_name)-1; 237960573Skris /* interface name */ 238057429Smarkm strncpy(p.if_name, av[0], l); 238160573Skris p.if_name[l] = '\0'; 238260573Skris p.bandwidth = 0; 238360573Skris } else { 238460573Skris p.if_name[0] = '\0'; 238560573Skris p.bandwidth = strtoul(av[0], &end, 0); 238660573Skris if (*end == 'K' || *end == 'k') { 238760573Skris end++; 2388124207Sdes p.bandwidth *= 1000; 238960573Skris } else if (*end == 'M') { 239060573Skris end++; 239198684Sdes p.bandwidth *= 1000000; 239298684Sdes } 239398684Sdes if (*end == 'B' || !strncmp(end, "by", 2)) 239498684Sdes p.bandwidth *= 8; 239598684Sdes if (p.bandwidth < 0) 239698684Sdes errx(EX_DATAERR, "bandwidth too large"); 239798684Sdes } 239860573Skris ac--; av++; 239960573Skris break; 240060573Skris 240160573Skris case TOK_DELAY: 2402124207Sdes if (do_pipe != 1) 240360573Skris errx(EX_DATAERR, "delay only valid for pipes"); 240460573Skris NEED1("delay needs argument 0..10000ms\n"); 240560573Skris p.delay = strtoul(av[0], NULL, 0); 240692559Sdes ac--; av++; 240760573Skris break; 2408124207Sdes 240960573Skris case TOK_WEIGHT: 241060573Skris if (do_pipe == 1) 241160573Skris errx(EX_DATAERR,"weight only valid for queues"); 241260573Skris NEED1("weight needs argument 0..100\n"); 241369587Sgreen p.fs.weight = strtoul(av[0], &end, 0); 241460573Skris ac--; av++; 241560573Skris break; 241660573Skris 241760573Skris case TOK_PIPE: 241860573Skris if (do_pipe == 1) 2419162856Sdes errx(EX_DATAERR,"pipe only valid for queues"); 242060573Skris NEED1("pipe needs pipe_number\n"); 242192559Sdes p.fs.parent_nr = strtoul(av[0], &end, 0); 242260573Skris ac--; av++; 242360573Skris break; 242460573Skris 242557429Smarkm default: 242660573Skris errx(EX_DATAERR, "unrecognised option ``%s''", av[-1]); 242792559Sdes } 242860573Skris } 242960573Skris if (do_pipe == 1) { 243060573Skris if (p.pipe_nr == 0) 243160573Skris errx(EX_DATAERR, "pipe_nr must be > 0"); 243292559Sdes if (p.delay > 10000) 243392559Sdes errx(EX_DATAERR, "delay must be < 10000"); 243492559Sdes } else { /* do_pipe == 2, queue */ 243592559Sdes if (p.fs.parent_nr == 0) 243692559Sdes errx(EX_DATAERR, "pipe must be > 0"); 243792559Sdes if (p.fs.weight >100) 243892559Sdes errx(EX_DATAERR, "weight must be <= 100"); 243992559Sdes } 244092559Sdes if (p.fs.flags_fs & DN_QSIZE_IS_BYTES) { 244160573Skris if (p.fs.qsize > 1024*1024) 244260573Skris errx(EX_DATAERR, "queue size must be < 1MB"); 2443162856Sdes } else { 244460573Skris if (p.fs.qsize > 100) 244592559Sdes errx(EX_DATAERR, "2 <= queue size <= 100"); 244657429Smarkm } 244760573Skris if (p.fs.flags_fs & DN_IS_RED) { 244860573Skris size_t len; 244957429Smarkm int lookup_depth, avg_pkt_size; 245060573Skris double s, idle, weight, w_q; 245192559Sdes struct clockinfo ck; 245260573Skris int t; 245360573Skris 245460573Skris if (p.fs.min_th >= p.fs.max_th) 245557429Smarkm errx(EX_DATAERR, "min_th %d must be < than max_th %d", 245657429Smarkm p.fs.min_th, p.fs.max_th); 245757429Smarkm if (p.fs.max_th == 0) 245857429Smarkm errx(EX_DATAERR, "max_th must be > 0"); 245957429Smarkm 246057429Smarkm len = sizeof(int); 246160573Skris if (sysctlbyname("net.inet.ip.dummynet.red_lookup_depth", 246257429Smarkm &lookup_depth, &len, NULL, 0) == -1) 246357429Smarkm 246457429Smarkm errx(1, "sysctlbyname(\"%s\")", 246557429Smarkm "net.inet.ip.dummynet.red_lookup_depth"); 246657429Smarkm if (lookup_depth == 0) 246757429Smarkm errx(EX_DATAERR, "net.inet.ip.dummynet.red_lookup_depth" 246857429Smarkm " must be greater than zero"); 246957429Smarkm 247057429Smarkm len = sizeof(int); 247160573Skris if (sysctlbyname("net.inet.ip.dummynet.red_avg_pkt_size", 247257429Smarkm &avg_pkt_size, &len, NULL, 0) == -1) 247357429Smarkm 247457429Smarkm errx(1, "sysctlbyname(\"%s\")", 247557429Smarkm "net.inet.ip.dummynet.red_avg_pkt_size"); 247692559Sdes if (avg_pkt_size == 0) 247760573Skris errx(EX_DATAERR, 247857429Smarkm "net.inet.ip.dummynet.red_avg_pkt_size must" 247957429Smarkm " be greater than zero"); 248057429Smarkm 248160573Skris len = sizeof(struct clockinfo); 2482162856Sdes if (sysctlbyname("kern.clockrate", &ck, &len, NULL, 0) == -1) 248360573Skris errx(1, "sysctlbyname(\"%s\")", "kern.clockrate"); 248492559Sdes 248560573Skris /* 248660573Skris * Ticks needed for sending a medium-sized packet. 248760573Skris * Unfortunately, when we are configuring a WF2Q+ queue, we 248892559Sdes * do not have bandwidth information, because that is stored 248992559Sdes * in the parent pipe, and also we have multiple queues 249060573Skris * competing for it. So we set s=0, which is not very 249160573Skris * correct. But on the other hand, why do we want RED with 249260573Skris * WF2Q+ ? 249360573Skris */ 249457429Smarkm if (p.bandwidth==0) /* this is a WF2Q+ queue */ 2495162856Sdes s = 0; 249660573Skris else 249792559Sdes s = ck.hz * avg_pkt_size * 8 / p.bandwidth; 249857429Smarkm 249960573Skris /* 250060573Skris * max idle time (in ticks) before avg queue size becomes 0. 250157429Smarkm * NOTA: (3/w_q) is approx the value x so that 250292559Sdes * (1-w_q)^x < 10^-3. 250360573Skris */ 250460573Skris w_q = ((double)p.fs.w_q) / (1 << SCALE_RED); 250560573Skris idle = s * 3. / w_q; 250660573Skris p.fs.lookup_step = (int)idle / lookup_depth; 250760573Skris if (!p.fs.lookup_step) 250860573Skris p.fs.lookup_step = 1; 250992559Sdes weight = 1 - w_q; 251060573Skris for (t = p.fs.lookup_step; t > 0; --t) 251157429Smarkm weight *= weight; 2512162856Sdes p.fs.lookup_weight = (int)(weight * (1 << SCALE_RED)); 251360573Skris } 251492559Sdes i = do_cmd(IP_DUMMYNET_CONFIGURE, &p, sizeof p); 251560573Skris if (i) 251660573Skris err(1, "setsockopt(%s)", "IP_DUMMYNET_CONFIGURE"); 251760573Skris} 251860573Skris 251960573Skrisstatic void 252060573Skrisget_mac_addr_mask(char *p, uint8_t *addr, uint8_t *mask) 252160573Skris{ 252260573Skris int i, l; 252360573Skris 252460573Skris for (i=0; i<6; i++) 252560573Skris addr[i] = mask[i] = 0; 252660573Skris if (!strcmp(p, "any")) 252760573Skris return; 252860573Skris 252960573Skris for (i=0; *p && i<6;i++, p++) { 253060573Skris addr[i] = strtol(p, &p, 16); 253160573Skris if (*p != ':') /* we start with the mask */ 253260573Skris break; 2533181111Sdes } 253469587Sgreen if (*p == '/') { /* mask len */ 2535215116Sdes l = strtol(p+1, &p, 0); 253669587Sgreen for (i=0; l>0; l -=8, i++) 253760573Skris mask[i] = (l >=8) ? 0xff : (~0) << (8-l); 2538124207Sdes } else if (*p == '&') { /* mask */ 253960573Skris for (i=0, p++; *p && i<6;i++, p++) { 254057429Smarkm mask[i] = strtol(p, &p, 16); 254192559Sdes if (*p != ':') 254257429Smarkm break; 254357429Smarkm } 254492559Sdes } else if (*p == '\0') { 254592559Sdes for (i=0; i<6; i++) 254692559Sdes mask[i] = 0xff; 254792559Sdes } 254892559Sdes for (i=0; i<6; i++) 254992559Sdes addr[i] &= mask[i]; 255092559Sdes} 255192559Sdes 255292559Sdes/* 255392559Sdes * helper function, updates the pointer to cmd with the length 255492559Sdes * of the current command, and also cleans up the first word of 255592559Sdes * the new command in case it has been clobbered before. 255692559Sdes */ 255792559Sdesstatic ipfw_insn * 255892559Sdesnext_cmd(ipfw_insn *cmd) 255992559Sdes{ 2560162856Sdes cmd += F_LEN(cmd); 256160573Skris bzero(cmd, sizeof(*cmd)); 256292559Sdes return cmd; 256357429Smarkm} 256476262Sgreen 256576262Sgreen/* 256660573Skris * Takes arguments and copies them into a comment 256757429Smarkm */ 256860573Skrisstatic void 256960573Skrisfill_comment(ipfw_insn *cmd, int ac, char **av) 257057429Smarkm{ 257160573Skris int i, l; 257260573Skris char *p = (char *)(cmd + 1); 257360573Skris 257460573Skris cmd->opcode = O_NOP; 257576262Sgreen cmd->len = (cmd->len & (F_NOT | F_OR)); 257692559Sdes 257776262Sgreen /* Compute length of comment string. */ 257876262Sgreen for (i = 0, l = 0; i < ac; i++) 257976262Sgreen l += strlen(av[i]) + 1; 2580124207Sdes if (l == 0) 258192559Sdes return; 258276262Sgreen if (l > 84) 258376262Sgreen errx(EX_DATAERR, 258476262Sgreen "comment too long (max 80 chars)"); 258576262Sgreen l = 1 + (l+3)/4; 2586215116Sdes cmd->len = (cmd->len & (F_NOT | F_OR)) | l; 2587215116Sdes for (i = 0; i < ac; i++) { 2588215116Sdes strcpy(p, av[i]); 2589215116Sdes p += strlen(av[i]); 2590215116Sdes *p++ = ' '; 259160573Skris } 259292559Sdes *(--p) = '\0'; 2593192595Sdes} 2594192595Sdes 259557429Smarkm/* 259657429Smarkm * A function to fill simple commands of size 1. 2597162856Sdes * Existing flags are preserved. 259860573Skris */ 259992559Sdesstatic void 260060573Skrisfill_cmd(ipfw_insn *cmd, enum ipfw_opcodes opcode, int flags, uint16_t arg) 260160573Skris{ 260299063Sdes cmd->opcode = opcode; 260399063Sdes cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | 1; 260457429Smarkm cmd->arg1 = arg; 260560573Skris} 260660573Skris 260760573Skris/* 260857429Smarkm * Fetch and add the MAC address and type, with masks. This generates one or 260960573Skris * two microinstructions, and returns the pointer to the last one. 261060573Skris */ 261157429Smarkmstatic ipfw_insn * 2612157019Sdesadd_mac(ipfw_insn *cmd, int ac, char *av[]) 2613157019Sdes{ 261460573Skris ipfw_insn_mac *mac; 261560573Skris 261660573Skris if (ac < 2) 261792559Sdes errx(EX_DATAERR, "MAC dst src"); 261899063Sdes 261960573Skris cmd->opcode = O_MACADDR2; 262057429Smarkm cmd->len = (cmd->len & (F_NOT | F_OR)) | F_INSN_SIZE(ipfw_insn_mac); 262157429Smarkm 2622162856Sdes mac = (ipfw_insn_mac *)cmd; 262360573Skris get_mac_addr_mask(av[0], mac->addr, mac->mask); /* dst */ 262492559Sdes get_mac_addr_mask(av[1], &(mac->addr[6]), &(mac->mask[6])); /* src */ 262557429Smarkm return cmd; 262692559Sdes} 262792559Sdes 262892559Sdesstatic ipfw_insn * 2629181111Sdesadd_mactype(ipfw_insn *cmd, int ac, char *av) 263057429Smarkm{ 263192559Sdes if (ac < 1) 263292559Sdes errx(EX_DATAERR, "missing MAC type"); 263392559Sdes if (strcmp(av, "any") != 0) { /* we have a non-null type */ 263457429Smarkm fill_newports((ipfw_insn_u16 *)cmd, av, IPPROTO_ETHERTYPE); 263592559Sdes cmd->opcode = O_MAC_TYPE; 263692559Sdes return cmd; 263792559Sdes } else 263892559Sdes return NULL; 263992559Sdes} 264092559Sdes 2641181111Sdesstatic ipfw_insn * 2642181111Sdesadd_proto(ipfw_insn *cmd, char *av) 2643124207Sdes{ 2644181111Sdes struct protoent *pe; 264592559Sdes u_char proto = 0; 264692559Sdes 264792559Sdes if (!strncmp(av, "all", strlen(av))) 264892559Sdes ; /* same as "ip" */ 2649181111Sdes else if ((proto = atoi(av)) > 0) 2650181111Sdes ; /* all done! */ 265157429Smarkm else if ((pe = getprotobyname(av)) != NULL) 265257429Smarkm proto = pe->p_proto; 2653181111Sdes else 2654181111Sdes return NULL; 2655181111Sdes if (proto != IPPROTO_IP) 2656181111Sdes fill_cmd(cmd, O_PROTO, 0, proto); 2657181111Sdes return cmd; 2658181111Sdes} 2659192595Sdes 266057429Smarkmstatic ipfw_insn * 2661181111Sdesadd_srcip(ipfw_insn *cmd, char *av) 2662197679Sdes{ 2663181111Sdes fill_ip((ipfw_insn_ip *)cmd, av); 2664192595Sdes if (cmd->opcode == O_IP_DST_SET) /* set */ 2665181111Sdes cmd->opcode = O_IP_SRC_SET; 2666181111Sdes else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 2667192595Sdes cmd->opcode = O_IP_SRC_ME; 2668181111Sdes else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 2669192595Sdes cmd->opcode = O_IP_SRC; 2670192595Sdes else /* addr/mask */ 2671181111Sdes cmd->opcode = O_IP_SRC_MASK; 2672181111Sdes return cmd; 2673181111Sdes} 2674181111Sdes 2675181111Sdesstatic ipfw_insn * 2676181111Sdesadd_dstip(ipfw_insn *cmd, char *av) 2677181111Sdes{ 2678181111Sdes fill_ip((ipfw_insn_ip *)cmd, av); 2679181111Sdes if (cmd->opcode == O_IP_DST_SET) /* set */ 2680181111Sdes ; 2681181111Sdes else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn)) /* me */ 268292559Sdes cmd->opcode = O_IP_DST_ME; 268357429Smarkm else if (F_LEN(cmd) == F_INSN_SIZE(ipfw_insn_u32)) /* one IP */ 268492559Sdes cmd->opcode = O_IP_DST; 268592559Sdes else /* addr/mask */ 268676262Sgreen cmd->opcode = O_IP_DST_MASK; 268792559Sdes return cmd; 268876262Sgreen} 268976262Sgreen 2690224638Sbrooksstatic ipfw_insn * 2691224638Sbrooksadd_ports(ipfw_insn *cmd, char *av, u_char proto, int opcode) 2692224638Sbrooks{ 2693224638Sbrooks if (!strncmp(av, "any", strlen(av))) { 2694224638Sbrooks return NULL; 2695224638Sbrooks } else if (fill_newports((ipfw_insn_u16 *)cmd, av, proto)) { 2696224638Sbrooks /* XXX todo: check that we have a protocol with ports */ 2697224638Sbrooks cmd->opcode = opcode; 2698224638Sbrooks return cmd; 269992559Sdes } 2700192595Sdes return NULL; 2701192595Sdes} 270292559Sdes 270357429Smarkm/* 270492559Sdes * Parse arguments and assemble the microinstructions which make up a rule. 2705157019Sdes * Rules are added into the 'rulebuf' and then copied in the correct order 270657429Smarkm * into the actual rule. 2707147005Sdes * 270857429Smarkm * The syntax for a rule starts with the action, followed by an 2709192595Sdes * optional log action, and the various match patterns. 271057429Smarkm * In the assembled microcode, the first opcode must be an O_PROBE_STATE 271192559Sdes * (generated if the rule includes a keep-state option), then the 271292559Sdes * various match patterns, the "log" action, and the actual action. 2713147005Sdes * 271457429Smarkm */ 271592559Sdesstatic void 271692559Sdesadd(int ac, char *av[]) 2717149753Sdes{ 271876262Sgreen /* 2719192595Sdes * rules are added into the 'rulebuf' and then copied in 272076262Sgreen * the correct order into the actual rule. 2721149753Sdes * Some things that need to go out of order (prob, action etc.) 272276262Sgreen * go into actbuf[]. 272376262Sgreen */ 272457429Smarkm static uint32_t rulebuf[255], actbuf[255], cmdbuf[255]; 2725147005Sdes 2726147005Sdes ipfw_insn *src, *dst, *cmd, *action, *prev=NULL; 2727147005Sdes ipfw_insn *first_cmd; /* first match pattern */ 2728147005Sdes 2729147005Sdes struct ip_fw *rule; 2730147005Sdes 2731147005Sdes /* 2732147005Sdes * various flags used to record that we entered some fields. 2733147005Sdes */ 2734147005Sdes ipfw_insn *have_state = NULL; /* check-state or keep-state */ 2735147005Sdes 2736147005Sdes int i; 2737147005Sdes 2738147005Sdes int open_par = 0; /* open parenthesis ( */ 2739147005Sdes 2740147005Sdes /* proto is here because it is used to fetch ports */ 2741147005Sdes u_char proto = IPPROTO_IP; /* default protocol */ 2742147005Sdes 2743147005Sdes double match_prob = 1; /* match probability, default is always match */ 2744147005Sdes 2745147005Sdes bzero(actbuf, sizeof(actbuf)); /* actions go here */ 2746181111Sdes bzero(cmdbuf, sizeof(cmdbuf)); 2747147005Sdes bzero(rulebuf, sizeof(rulebuf)); 2748147005Sdes 2749147005Sdes rule = (struct ip_fw *)rulebuf; 2750147005Sdes cmd = (ipfw_insn *)cmdbuf; 2751147005Sdes action = (ipfw_insn *)actbuf; 2752147005Sdes 2753147005Sdes av++; ac--; 2754147005Sdes 2755147005Sdes /* [rule N] -- Rule number optional */ 2756147005Sdes if (ac && isdigit(**av)) { 2757147005Sdes rule->rulenum = atoi(*av); 275857429Smarkm av++; 275957429Smarkm ac--; 276057429Smarkm } 276157429Smarkm 276257429Smarkm /* [set N] -- set number (0..RESVD_SET), optional */ 2763147005Sdes if (ac > 1 && !strncmp(*av, "set", strlen(*av))) { 276457429Smarkm int set = strtoul(av[1], NULL, 10); 276576262Sgreen if (set < 0 || set > RESVD_SET) 2766147005Sdes errx(EX_DATAERR, "illegal set %s", av[1]); 2767147005Sdes rule->set = set; 2768147005Sdes av += 2; ac -= 2; 2769147005Sdes } 2770181111Sdes 2771147005Sdes /* [prob D] -- match probability, optional */ 2772149753Sdes if (ac > 1 && !strncmp(*av, "prob", strlen(*av))) { 2773181111Sdes match_prob = strtod(av[1], NULL); 2774181111Sdes 2775147005Sdes if (match_prob <= 0 || match_prob > 1) 2776149753Sdes errx(EX_DATAERR, "illegal match prob. %s", av[1]); 2777147005Sdes av += 2; ac -= 2; 2778192595Sdes } 2779192595Sdes 278057429Smarkm /* action -- mandatory */ 2781192595Sdes NEED1("missing action"); 2782192595Sdes i = match_token(rule_actions, *av); 2783192595Sdes ac--; av++; 2784192595Sdes action->len = 1; /* default */ 2785192595Sdes switch(i) { 2786192595Sdes case TOK_CHECKSTATE: 2787192595Sdes have_state = action; 2788192595Sdes action->opcode = O_CHECK_STATE; 2789192595Sdes break; 2790192595Sdes 279157429Smarkm case TOK_ACCEPT: 2792192595Sdes action->opcode = O_ACCEPT; 2793192595Sdes break; 2794192595Sdes 2795192595Sdes case TOK_DENY: 2796192595Sdes action->opcode = O_DENY; 2797192595Sdes action->arg1 = 0; 2798192595Sdes break; 2799192595Sdes 2800192595Sdes case TOK_REJECT: 280157429Smarkm action->opcode = O_REJECT; 280257429Smarkm action->arg1 = ICMP_UNREACH_HOST; 280392559Sdes break; 280457429Smarkm 280557429Smarkm case TOK_RESET: 280657429Smarkm action->opcode = O_REJECT; 2807124207Sdes action->arg1 = ICMP_REJECT_RST; 280857429Smarkm break; 280957429Smarkm 281057429Smarkm case TOK_UNREACH: 281157429Smarkm action->opcode = O_REJECT; 281257429Smarkm NEED1("missing reject code"); 2813106130Sdes fill_reject_code(&action->arg1, *av); 2814157019Sdes ac--; av++; 2815204917Sdes break; 2816204917Sdes 2817157019Sdes case TOK_COUNT: 2818192595Sdes action->opcode = O_COUNT; 2819192595Sdes break; 282057429Smarkm 282157429Smarkm case TOK_QUEUE: 282257429Smarkm case TOK_PIPE: 282357429Smarkm action->len = F_INSN_SIZE(ipfw_insn_pipe); 282498941Sdes case TOK_SKIPTO: 282598941Sdes if (i == TOK_QUEUE) 282698941Sdes action->opcode = O_QUEUE; 282798941Sdes else if (i == TOK_PIPE) 282898941Sdes action->opcode = O_PIPE; 282957429Smarkm else if (i == TOK_SKIPTO) 283057429Smarkm action->opcode = O_SKIPTO; 283157429Smarkm NEED1("missing skipto/pipe/queue number"); 283257429Smarkm action->arg1 = strtoul(*av, NULL, 10); 2833126273Sdes av++; ac--; 283457429Smarkm break; 283557429Smarkm 283657429Smarkm case TOK_DIVERT: 283757429Smarkm case TOK_TEE: 2838192595Sdes action->opcode = (i == TOK_DIVERT) ? O_DIVERT : O_TEE; 2839192595Sdes NEED1("missing divert/tee port"); 2840192595Sdes action->arg1 = strtoul(*av, NULL, 0); 2841192595Sdes if (action->arg1 == 0) { 2842192595Sdes struct servent *s; 2843192595Sdes setservent(1); 2844192595Sdes s = getservbyname(av[0], "divert"); 2845192595Sdes if (s != NULL) 2846192595Sdes action->arg1 = ntohs(s->s_port); 2847192595Sdes else 2848192595Sdes errx(EX_DATAERR, "illegal divert/tee port"); 2849192595Sdes } 2850192595Sdes ac--; av++; 2851224638Sbrooks break; 2852224638Sbrooks 2853224638Sbrooks case TOK_FORWARD: { 2854224638Sbrooks ipfw_insn_sa *p = (ipfw_insn_sa *)action; 2855224638Sbrooks char *s, *end; 2856224638Sbrooks 2857224638Sbrooks NEED1("missing forward address[:port]"); 2858224638Sbrooks 2859224638Sbrooks action->opcode = O_FORWARD_IP; 2860224638Sbrooks action->len = F_INSN_SIZE(ipfw_insn_sa); 2861224638Sbrooks 2862224638Sbrooks p->sa.sin_len = sizeof(struct sockaddr_in); 2863192595Sdes p->sa.sin_family = AF_INET; 286492559Sdes p->sa.sin_port = 0; 286592559Sdes /* 286657429Smarkm * locate the address-port separator (':' or ',') 286757429Smarkm */ 286857429Smarkm s = strchr(*av, ':'); 286992559Sdes if (s == NULL) 287076262Sgreen s = strchr(*av, ','); 287157429Smarkm if (s != NULL) { 287276262Sgreen *(s++) = '\0'; 287357429Smarkm i = strtoport(s, &end, 0 /* base */, 0 /* proto */); 287457429Smarkm if (s == end) 2875137019Sdes errx(EX_DATAERR, 2876137019Sdes "illegal forwarding port ``%s''", s); 2877137019Sdes p->sa.sin_port = (u_short)i; 2878137019Sdes } 2879137019Sdes lookup_host(*av, &(p->sa.sin_addr)); 2880137019Sdes } 2881147005Sdes ac--; av++; 2882137019Sdes break; 2883137019Sdes 2884137019Sdes case TOK_COMMENT: 2885192595Sdes /* pretend it is a 'count' rule followed by the comment */ 2886147005Sdes action->opcode = O_COUNT; 2887137019Sdes ac++; av--; /* go back... */ 2888137019Sdes break; 2889137019Sdes 2890137019Sdes default: 2891137019Sdes errx(EX_DATAERR, "invalid action %s\n", av[-1]); 2892137019Sdes } 2893137019Sdes action = next_cmd(action); 2894137019Sdes 289592559Sdes /* 289692559Sdes * [log [logamount N]] -- log, optional 2897147005Sdes * 289892559Sdes * If exists, it goes first in the cmdbuf, but then it is 289992559Sdes * skipped in the copy section to the end of the buffer. 290092559Sdes */ 2901192595Sdes if (ac && !strncmp(*av, "log", strlen(*av))) { 2902147005Sdes ipfw_insn_log *c = (ipfw_insn_log *)cmd; 290392559Sdes int l; 290492559Sdes 290592559Sdes cmd->len = F_INSN_SIZE(ipfw_insn_log); 290692559Sdes cmd->opcode = O_LOG; 290792559Sdes av++; ac--; 2908192595Sdes if (ac && !strncmp(*av, "logamount", strlen(*av))) { 290992559Sdes ac--; av++; 291092559Sdes NEED1("logamount requires argument"); 2911192595Sdes l = atoi(*av); 2912192595Sdes if (l < 0) 291392559Sdes errx(EX_DATAERR, "logamount must be positive"); 291492559Sdes c->max_log = l; 291557429Smarkm ac--; av++; 291657429Smarkm } 291757429Smarkm cmd = next_cmd(cmd); 291857429Smarkm } 291957429Smarkm 2920162856Sdes if (have_state) /* must be a check-state, we are done */ 2921147005Sdes goto done; 292276262Sgreen 292357429Smarkm#define OR_START(target) \ 292492559Sdes if (ac && (*av[0] == '(' || *av[0] == '{')) { \ 292576262Sgreen if (open_par) \ 292657429Smarkm errx(EX_USAGE, "nested \"(\" not allowed\n"); \ 292760573Skris prev = NULL; \ 2928147005Sdes open_par = 1; \ 2929181111Sdes if ( (av[0])[1] == '\0') { \ 2930181111Sdes ac--; av++; \ 2931181111Sdes } else \ 2932181111Sdes (*av)++; \ 2933181111Sdes } \ 2934181111Sdes target: \ 2935181111Sdes 2936181111Sdes 2937181111Sdes#define CLOSE_PAR \ 2938181111Sdes if (open_par) { \ 2939181111Sdes if (ac && ( \ 2940181111Sdes !strncmp(*av, ")", strlen(*av)) || \ 2941147005Sdes !strncmp(*av, "}", strlen(*av)) )) { \ 2942147005Sdes prev = NULL; \ 294360573Skris open_par = 0; \ 294460573Skris ac--; av++; \ 294598684Sdes } else \ 294660573Skris errx(EX_USAGE, "missing \")\"\n"); \ 294760573Skris } 294876262Sgreen 294976262Sgreen#define NOT_BLOCK \ 295076262Sgreen if (ac && !strncmp(*av, "not", strlen(*av))) { \ 295176262Sgreen if (cmd->len & F_NOT) \ 295260573Skris errx(EX_USAGE, "double \"not\" not allowed\n"); \ 295360573Skris cmd->len |= F_NOT; \ 295460573Skris ac--; av++; \ 295560573Skris } 295660573Skris 295760573Skris#define OR_BLOCK(target) \ 295860573Skris if (ac && !strncmp(*av, "or", strlen(*av))) { \ 295976262Sgreen if (prev == NULL || open_par == 0) \ 296076262Sgreen errx(EX_DATAERR, "invalid OR block"); \ 296192559Sdes prev->len |= F_OR; \ 296276262Sgreen ac--; av++; \ 296376262Sgreen goto target; \ 296476262Sgreen } \ 296576262Sgreen CLOSE_PAR; 296676262Sgreen 296776262Sgreen first_cmd = cmd; 296876262Sgreen 296976262Sgreen#if 0 297076262Sgreen /* 297176262Sgreen * MAC addresses, optional. 297276262Sgreen * If we have this, we skip the part "proto from src to dst" 297360573Skris * and jump straight to the option parsing. 297476262Sgreen */ 2975215116Sdes NOT_BLOCK; 2976215116Sdes NEED1("missing protocol"); 2977215116Sdes if (!strncmp(*av, "MAC", strlen(*av)) || 297876262Sgreen !strncmp(*av, "mac", strlen(*av))) { 297976262Sgreen ac--; av++; /* the "MAC" keyword */ 298076262Sgreen add_mac(cmd, ac, av); /* exits in case of errors */ 298176262Sgreen cmd = next_cmd(cmd); 298276262Sgreen ac -= 2; av += 2; /* dst-mac and src-mac */ 2983162856Sdes NOT_BLOCK; 298457429Smarkm NEED1("missing mac type"); 298557429Smarkm if (add_mactype(cmd, ac, av[0])) 298657429Smarkm cmd = next_cmd(cmd); 2987137019Sdes ac--; av++; /* any or mac-type */ 2988137019Sdes goto read_options; 2989137019Sdes } 2990137019Sdes#endif 2991147005Sdes 2992137019Sdes /* 2993137019Sdes * protocol, mandatory 2994137019Sdes */ 2995137019Sdes OR_START(get_proto); 2996137019Sdes NOT_BLOCK; 2997137019Sdes NEED1("missing protocol"); 2998137019Sdes if (add_proto(cmd, *av)) { 2999137019Sdes av++; ac--; 3000137019Sdes if (F_LEN(cmd) == 0) /* plain IP */ 3001137019Sdes proto = 0; 3002137019Sdes else { 3003137019Sdes proto = cmd->arg1; 3004137019Sdes prev = cmd; 3005137019Sdes cmd = next_cmd(cmd); 3006137019Sdes } 3007137019Sdes } else if (first_cmd != cmd) { 3008137019Sdes errx(EX_DATAERR, "invalid protocol ``%s''", *av); 3009137019Sdes } else 3010147005Sdes goto read_options; 3011137019Sdes OR_BLOCK(get_proto); 3012137019Sdes 3013137019Sdes /* 3014137019Sdes * "from", mandatory 3015137019Sdes */ 3016157019Sdes if (!ac || strncmp(*av, "from", strlen(*av))) 3017137019Sdes errx(EX_USAGE, "missing ``from''"); 3018137019Sdes ac--; av++; 3019137019Sdes 3020137019Sdes /* 302157429Smarkm * source IP, mandatory 302257429Smarkm */ 3023162856Sdes OR_START(source_ip); 302457429Smarkm NOT_BLOCK; /* optional "not" */ 3025162856Sdes NEED1("missing source address"); 302660573Skris if (add_srcip(cmd, *av)) { 302757429Smarkm ac--; av++; 302857429Smarkm if (F_LEN(cmd) != 0) { /* ! any */ 3029162856Sdes prev = cmd; 303057429Smarkm cmd = next_cmd(cmd); 303157429Smarkm } 303257429Smarkm } 303357429Smarkm OR_BLOCK(source_ip); 303457429Smarkm 303557429Smarkm /* 303657429Smarkm * source ports, optional 303798941Sdes */ 303857429Smarkm NOT_BLOCK; /* optional "not" */ 303957429Smarkm if (ac) { 304057429Smarkm if (!strncmp(*av, "any", strlen(*av)) || 304157429Smarkm add_ports(cmd, *av, proto, O_IP_SRCPORT)) { 304257429Smarkm ac--; av++; 3043124207Sdes if (F_LEN(cmd) != 0) 3044124207Sdes cmd = next_cmd(cmd); 3045124207Sdes } 3046124207Sdes } 3047124207Sdes 304898941Sdes /* 3049124207Sdes * "to", mandatory 305076262Sgreen */ 3051162856Sdes if (!ac || strncmp(*av, "to", strlen(*av))) 3052147005Sdes errx(EX_USAGE, "missing ``to''"); 305357429Smarkm av++; ac--; 305457429Smarkm 305557429Smarkm /* 3056162856Sdes * destination, mandatory 3057162856Sdes */ 305857429Smarkm OR_START(dest_ip); 305957429Smarkm NOT_BLOCK; /* optional "not" */ 306076262Sgreen NEED1("missing dst address"); 306176262Sgreen if (add_dstip(cmd, *av)) { 306276262Sgreen ac--; av++; 306376262Sgreen if (F_LEN(cmd) != 0) { /* ! any */ 306476262Sgreen prev = cmd; 306576262Sgreen cmd = next_cmd(cmd); 306692559Sdes } 306776262Sgreen } 306876262Sgreen OR_BLOCK(dest_ip); 306976262Sgreen 307076262Sgreen /* 307176262Sgreen * dest. ports, optional 307276262Sgreen */ 307376262Sgreen NOT_BLOCK; /* optional "not" */ 307476262Sgreen if (ac) { 307576262Sgreen if (!strncmp(*av, "any", strlen(*av)) || 307676262Sgreen add_ports(cmd, *av, proto, O_IP_DSTPORT)) { 3077215116Sdes ac--; av++; 3078215116Sdes if (F_LEN(cmd) != 0) 307976262Sgreen cmd = next_cmd(cmd); 308076262Sgreen } 308176262Sgreen } 308276262Sgreen 308376262Sgreenread_options: 308476262Sgreen if (ac && first_cmd == cmd) { 308576262Sgreen /* 3086162856Sdes * nothing specified so far, store in the rule to ease 3087162856Sdes * printout later. 3088162856Sdes */ 3089162856Sdes rule->_pad = 1; 3090162856Sdes } 3091215116Sdes prev = NULL; 3092215116Sdes while (ac) { 3093162856Sdes char *s; 3094162856Sdes ipfw_insn_u32 *cmd32; /* alias for cmd */ 3095162856Sdes 3096162856Sdes s = *av; 3097162856Sdes cmd32 = (ipfw_insn_u32 *)cmd; 3098162856Sdes 309976262Sgreen if (*s == '!') { /* alternate syntax for NOT */ 310076262Sgreen if (cmd->len & F_NOT) 310176262Sgreen errx(EX_USAGE, "double \"not\" not allowed\n"); 310276262Sgreen cmd->len = F_NOT; 310376262Sgreen s++; 310476262Sgreen } 3105137019Sdes i = match_token(rule_options, s); 3106137019Sdes ac--; av++; 3107215116Sdes switch(i) { 3108215116Sdes case TOK_NOT: 3109215116Sdes if (cmd->len & F_NOT) 3110215116Sdes errx(EX_USAGE, "double \"not\" not allowed\n"); 311176262Sgreen cmd->len = F_NOT; 3112162856Sdes break; 311376262Sgreen 3114162856Sdes case TOK_OR: 3115162856Sdes if (open_par == 0 || prev == NULL) 3116162856Sdes errx(EX_USAGE, "invalid \"or\" block\n"); 3117162856Sdes prev->len |= F_OR; 3118162856Sdes break; 3119162856Sdes 3120162856Sdes case TOK_STARTBRACE: 3121162856Sdes if (open_par) 3122215116Sdes errx(EX_USAGE, "+nested \"(\" not allowed\n"); 3123215116Sdes open_par = 1; 3124215116Sdes break; 3125215116Sdes 3126162856Sdes case TOK_ENDBRACE: 312776262Sgreen if (!open_par) 312876262Sgreen errx(EX_USAGE, "+missing \")\"\n"); 3129181111Sdes open_par = 0; 3130181111Sdes prev = NULL; 3131181111Sdes break; 3132181111Sdes 3133181111Sdes case TOK_IN: 3134192595Sdes fill_cmd(cmd, O_IN, 0, 0); 3135192595Sdes break; 3136192595Sdes 3137192595Sdes case TOK_OUT: 3138192595Sdes cmd->len ^= F_NOT; /* toggle F_NOT */ 3139181111Sdes fill_cmd(cmd, O_IN, 0, 0); 3140181111Sdes break; 3141181111Sdes 3142181111Sdes case TOK_FRAG: 3143192595Sdes fill_cmd(cmd, O_FRAG, 0, 0); 3144181111Sdes break; 3145181111Sdes 3146181111Sdes case TOK_LAYER2: 314792559Sdes fill_cmd(cmd, O_LAYER2, 0, 0); 3148181111Sdes break; 314960573Skris 3150181111Sdes case TOK_XMIT: 315160573Skris case TOK_RECV: 315260573Skris case TOK_VIA: 3153181111Sdes NEED1("recv, xmit, via require interface name" 3154181111Sdes " or address"); 3155181111Sdes fill_iface((ipfw_insn_if *)cmd, av[0]); 315660573Skris ac--; av++; 3157181111Sdes if (F_LEN(cmd) == 0) /* not a valid address */ 3158181111Sdes break; 3159181111Sdes if (i == TOK_XMIT) 3160181111Sdes cmd->opcode = O_XMIT; 316160573Skris else if (i == TOK_RECV) 316260573Skris cmd->opcode = O_RECV; 3163181111Sdes else if (i == TOK_VIA) 3164181111Sdes cmd->opcode = O_VIA; 3165181111Sdes break; 3166113911Sdes 3167113911Sdes case TOK_ICMPTYPES: 3168113911Sdes NEED1("icmptypes requires list of types"); 316960573Skris fill_icmptypes((ipfw_insn_u32 *)cmd, *av); 317060573Skris av++; ac--; 3171137019Sdes break; 3172137019Sdes 3173181111Sdes case TOK_IPTTL: 3174181111Sdes NEED1("ipttl requires TTL"); 3175181111Sdes if (strpbrk(*av, "-,")) { 3176181111Sdes if (!add_ports(cmd, *av, 0, O_IPTTL)) 317760573Skris errx(EX_DATAERR, "invalid ipttl %s", *av); 3178181111Sdes } else 317960573Skris fill_cmd(cmd, O_IPTTL, 0, strtoul(*av, NULL, 0)); 3180181111Sdes ac--; av++; 318176262Sgreen break; 318260573Skris 3183181111Sdes case TOK_IPID: 3184181111Sdes NEED1("ipid requires id"); 3185181111Sdes if (strpbrk(*av, "-,")) { 3186181111Sdes if (!add_ports(cmd, *av, 0, O_IPID)) 3187181111Sdes errx(EX_DATAERR, "invalid ipid %s", *av); 3188181111Sdes } else 3189181111Sdes fill_cmd(cmd, O_IPID, 0, strtoul(*av, NULL, 0)); 3190181111Sdes ac--; av++; 319160573Skris break; 3192181111Sdes 3193181111Sdes case TOK_IPLEN: 3194181111Sdes NEED1("iplen requires length"); 3195181111Sdes if (strpbrk(*av, "-,")) { 3196181111Sdes if (!add_ports(cmd, *av, 0, O_IPLEN)) 3197181111Sdes errx(EX_DATAERR, "invalid ip len %s", *av); 3198181111Sdes } else 3199181111Sdes fill_cmd(cmd, O_IPLEN, 0, strtoul(*av, NULL, 0)); 3200181111Sdes ac--; av++; 3201181111Sdes break; 3202181111Sdes 3203181111Sdes case TOK_IPVER: 3204181111Sdes NEED1("ipver requires version"); 3205181111Sdes fill_cmd(cmd, O_IPVER, 0, strtoul(*av, NULL, 0)); 3206181111Sdes ac--; av++; 3207181111Sdes break; 3208181111Sdes 3209181111Sdes case TOK_IPPRECEDENCE: 3210181111Sdes NEED1("ipprecedence requires value"); 3211181111Sdes fill_cmd(cmd, O_IPPRECEDENCE, 0, 3212181111Sdes (strtoul(*av, NULL, 0) & 7) << 5); 3213181111Sdes ac--; av++; 3214181111Sdes break; 3215181111Sdes 3216181111Sdes case TOK_IPOPTS: 3217181111Sdes NEED1("missing argument for ipoptions"); 3218181111Sdes fill_flags(cmd, O_IPOPT, f_ipopts, *av); 3219181111Sdes ac--; av++; 3220181111Sdes break; 3221181111Sdes 3222181111Sdes case TOK_IPTOS: 322360573Skris NEED1("missing argument for iptos"); 3224181111Sdes fill_flags(cmd, O_IPTOS, f_iptos, *av); 3225181111Sdes ac--; av++; 3226181111Sdes break; 3227181111Sdes 3228181111Sdes case TOK_UID: 3229181111Sdes NEED1("uid requires argument"); 3230181111Sdes { 3231181111Sdes char *end; 3232181111Sdes uid_t uid; 3233181111Sdes struct passwd *pwd; 323460573Skris 3235181111Sdes cmd->opcode = O_UID; 3236181111Sdes uid = strtoul(*av, &end, 0); 3237181111Sdes pwd = (*end == '\0') ? getpwuid(uid) : getpwnam(*av); 3238181111Sdes if (pwd == NULL) 323960573Skris errx(EX_DATAERR, "uid \"%s\" nonexistent", *av); 324076262Sgreen cmd32->d[0] = pwd->pw_uid; 3241181111Sdes cmd->len = F_INSN_SIZE(ipfw_insn_u32); 3242181111Sdes ac--; av++; 324376262Sgreen } 324476262Sgreen break; 324576262Sgreen 3246181111Sdes case TOK_GID: 3247137019Sdes NEED1("gid requires argument"); 3248181111Sdes { 324976262Sgreen char *end; 325076262Sgreen gid_t gid; 3251181111Sdes struct group *grp; 3252181111Sdes 3253181111Sdes cmd->opcode = O_GID; 325476262Sgreen gid = strtoul(*av, &end, 0); 325576262Sgreen grp = (*end == '\0') ? getgrgid(gid) : getgrnam(*av); 3256181111Sdes if (grp == NULL) 325776262Sgreen errx(EX_DATAERR, "gid \"%s\" nonexistent", *av); 325876262Sgreen cmd32->d[0] = grp->gr_gid; 325976262Sgreen cmd->len = F_INSN_SIZE(ipfw_insn_u32); 3260181111Sdes ac--; av++; 3261181111Sdes } 326276262Sgreen break; 3263162856Sdes 326476262Sgreen case TOK_ESTAB: 326576262Sgreen fill_cmd(cmd, O_ESTAB, 0, 0); 326676262Sgreen break; 326776262Sgreen 3268137019Sdes case TOK_SETUP: 3269137019Sdes fill_cmd(cmd, O_TCPFLAGS, 0, 327076262Sgreen (TH_SYN) | ( (TH_ACK) & 0xff) <<8 ); 327176262Sgreen break; 3272162856Sdes 327376262Sgreen case TOK_TCPOPTS: 3274162856Sdes NEED1("missing argument for tcpoptions"); 3275162856Sdes fill_flags(cmd, O_TCPOPTS, f_tcpopts, *av); 3276162856Sdes ac--; av++; 3277162856Sdes break; 3278162856Sdes 3279162856Sdes case TOK_TCPSEQ: 3280162856Sdes case TOK_TCPACK: 3281162856Sdes NEED1("tcpseq/tcpack requires argument"); 328276262Sgreen cmd->len = F_INSN_SIZE(ipfw_insn_u32); 3283162856Sdes cmd->opcode = (i == TOK_TCPSEQ) ? O_TCPSEQ : O_TCPACK; 3284162856Sdes cmd32->d[0] = htonl(strtoul(*av, NULL, 0)); 3285124207Sdes ac--; av++; 328676262Sgreen break; 3287181111Sdes 328876262Sgreen case TOK_TCPWIN: 3289181111Sdes NEED1("tcpwin requires length"); 329076262Sgreen fill_cmd(cmd, O_TCPWIN, 0, 329176262Sgreen htons(strtoul(*av, NULL, 0))); 3292137019Sdes ac--; av++; 3293137019Sdes break; 3294137019Sdes 3295137019Sdes case TOK_TCPFLAGS: 3296137019Sdes NEED1("missing argument for tcpflags"); 3297137019Sdes cmd->opcode = O_TCPFLAGS; 3298137019Sdes fill_flags(cmd, O_TCPFLAGS, f_tcpflags, *av); 3299147005Sdes ac--; av++; 3300137019Sdes break; 3301137019Sdes 3302137019Sdes case TOK_KEEPSTATE: 3303137019Sdes if (open_par) 3304137019Sdes errx(EX_USAGE, "keep-state cannot be part " 3305162856Sdes "of an or block"); 3306162856Sdes if (have_state) 3307162856Sdes errx(EX_USAGE, "only one of keep-state " 3308162856Sdes "and limit is allowed"); 3309137019Sdes have_state = cmd; 3310137019Sdes fill_cmd(cmd, O_KEEP_STATE, 0, 0); 3311137019Sdes break; 3312137019Sdes 331392559Sdes case TOK_LIMIT: 331457429Smarkm if (open_par) 331557429Smarkm errx(EX_USAGE, "limit cannot be part " 331657429Smarkm "of an or block"); 331799063Sdes if (have_state) 331899063Sdes errx(EX_USAGE, "only one of keep-state " 331957429Smarkm "and limit is allowed"); 332092559Sdes NEED1("limit needs mask and # of connections"); 332192559Sdes have_state = cmd; 3322149753Sdes { 332357429Smarkm ipfw_insn_limit *c = (ipfw_insn_limit *)cmd; 332492559Sdes 332557429Smarkm cmd->len = F_INSN_SIZE(ipfw_insn_limit); 332657429Smarkm cmd->opcode = O_LIMIT; 332757429Smarkm c->limit_mask = 0; 332857429Smarkm c->conn_limit = 0; 332957429Smarkm for (; ac >1 ;) { 333057429Smarkm int val; 3331157019Sdes 3332157019Sdes val = match_token(limit_masks, *av); 3333157019Sdes if (val <= 0) 333457429Smarkm break; 333592559Sdes c->limit_mask |= val; 333692559Sdes ac--; av++; 333757429Smarkm } 333857429Smarkm c->conn_limit = atoi(*av); 333957429Smarkm if (c->conn_limit == 0) 334092559Sdes errx(EX_USAGE, "limit: limit must be >0"); 334157429Smarkm if (c->limit_mask == 0) 334257429Smarkm errx(EX_USAGE, "missing limit mask"); 334357429Smarkm ac--; av++; 3344181111Sdes } 334592559Sdes break; 334657429Smarkm 334757429Smarkm case TOK_PROTO: 334857429Smarkm NEED1("missing protocol"); 334957429Smarkm if (add_proto(cmd, *av)) { 3350124207Sdes proto = cmd->arg1; 3351124207Sdes ac--; av++; 335257429Smarkm } else 3353207319Sdes errx(EX_DATAERR, "invalid protocol ``%s''", 3354207319Sdes *av); 3355207319Sdes break; 3356207319Sdes 3357207319Sdes case TOK_SRCIP: 335898941Sdes NEED1("missing source IP"); 3359137019Sdes if (add_srcip(cmd, *av)) { 336098941Sdes ac--; av++; 336198941Sdes } 336298941Sdes break; 336398941Sdes 336498941Sdes case TOK_DSTIP: 336598941Sdes NEED1("missing destination IP"); 336657429Smarkm if (add_dstip(cmd, *av)) { 3367204917Sdes ac--; av++; 3368204917Sdes } 3369181111Sdes break; 3370181111Sdes 337157429Smarkm case TOK_SRCPORT: 3372124207Sdes NEED1("missing source port"); 337357429Smarkm if (!strncmp(*av, "any", strlen(*av)) || 337498941Sdes add_ports(cmd, *av, proto, O_IP_SRCPORT)) { 337557429Smarkm ac--; av++; 337657429Smarkm } else 337757429Smarkm errx(EX_DATAERR, "invalid source port %s", *av); 337857429Smarkm break; 337957429Smarkm 338057429Smarkm case TOK_DSTPORT: 338157429Smarkm NEED1("missing destination port"); 338257429Smarkm if (!strncmp(*av, "any", strlen(*av)) || 338357429Smarkm add_ports(cmd, *av, proto, O_IP_DSTPORT)) { 338457429Smarkm ac--; av++; 338576262Sgreen } else 338657429Smarkm errx(EX_DATAERR, "invalid destination port %s", 338757429Smarkm *av); 338857429Smarkm break; 338957429Smarkm 339057429Smarkm case TOK_MAC: 339192559Sdes if (add_mac(cmd, ac, av)) { 339257429Smarkm ac -= 2; av += 2; 339357429Smarkm } 339457429Smarkm break; 339557429Smarkm 3396126273Sdes case TOK_MACTYPE: 339757429Smarkm NEED1("missing mac type"); 339857429Smarkm if (!add_mactype(cmd, ac, *av)) 339992559Sdes errx(EX_DATAERR, "invalid mac type %s", *av); 340057429Smarkm ac--; av++; 340157429Smarkm break; 340257429Smarkm 340357429Smarkm case TOK_VERREVPATH: 3404162856Sdes fill_cmd(cmd, O_VERREVPATH, 0, 0); 340557429Smarkm break; 340657429Smarkm 3407224638Sbrooks case TOK_VERSRCREACH: 3408224638Sbrooks fill_cmd(cmd, O_VERSRCREACH, 0, 0); 3409224638Sbrooks break; 3410224638Sbrooks 3411224638Sbrooks case TOK_IPSEC: 3412224638Sbrooks fill_cmd(cmd, O_IPSEC, 0, 0); 3413224638Sbrooks break; 3414224638Sbrooks 3415224638Sbrooks case TOK_COMMENT: 3416224638Sbrooks fill_comment(cmd, ac, av); 341792559Sdes av += ac; 3418157019Sdes ac = 0; 341957429Smarkm break; 3420157019Sdes 342157429Smarkm default: 342292559Sdes errx(EX_USAGE, "unrecognised option [%d] %s\n", i, s); 342399063Sdes } 342499063Sdes if (F_LEN(cmd) > 0) { /* prepare to advance */ 342557429Smarkm prev = cmd; 342657429Smarkm cmd = next_cmd(cmd); 342792559Sdes } 3428192595Sdes } 342957429Smarkm 343057429Smarkmdone: 343157429Smarkm /* 343257429Smarkm * Now copy stuff into the rule. 343392559Sdes * If we have a keep-state option, the first instruction 343492559Sdes * must be a PROBE_STATE (which is generated here). 343592559Sdes * If we have a LOG option, it was stored as the first command, 343692559Sdes * and now must be moved to the top of the action part. 343792559Sdes */ 3438192595Sdes dst = (ipfw_insn *)rule->cmd; 3439162856Sdes 344092559Sdes /* 344192559Sdes * First thing to write into the command stream is the match probability. 344257429Smarkm */ 344357429Smarkm if (match_prob != 1) { /* 1 means always match */ 344457429Smarkm dst->opcode = O_PROB; 344557429Smarkm dst->len = 2; 3446192595Sdes *((int32_t *)(dst+1)) = (int32_t)(match_prob * 0x7fffffff); 3447192595Sdes dst += dst->len; 3448192595Sdes } 3449192595Sdes 3450192595Sdes /* 3451192595Sdes * generate O_PROBE_STATE if necessary 3452192595Sdes */ 3453192595Sdes if (have_state && have_state->opcode != O_CHECK_STATE) { 345460573Skris fill_cmd(dst, O_PROBE_STATE, 0, 0); 345560573Skris dst = next_cmd(dst); 345657429Smarkm } 3457162856Sdes /* 345857429Smarkm * copy all commands but O_LOG, O_KEEP_STATE, O_LIMIT 345960573Skris */ 346057429Smarkm for (src = (ipfw_insn *)cmdbuf; src != cmd; src += i) { 346157429Smarkm i = F_LEN(src); 3462162856Sdes 346357429Smarkm switch (src->opcode) { 346457429Smarkm case O_LOG: 346557429Smarkm case O_KEEP_STATE: 346657429Smarkm case O_LIMIT: 346757429Smarkm break; 346860573Skris default: 346957429Smarkm bcopy(src, dst, i * sizeof(uint32_t)); 347057429Smarkm dst += i; 347157429Smarkm } 347257429Smarkm } 347357429Smarkm 347457429Smarkm /* 3475192595Sdes * put back the have_state command as last opcode 3476192595Sdes */ 3477192595Sdes if (have_state && have_state->opcode != O_CHECK_STATE) { 3478192595Sdes i = F_LEN(have_state); 3479192595Sdes bcopy(have_state, dst, i * sizeof(uint32_t)); 3480192595Sdes dst += i; 3481192595Sdes } 3482192595Sdes /* 3483192595Sdes * start action section 3484192595Sdes */ 3485192595Sdes rule->act_ofs = dst - rule->cmd; 348657429Smarkm 348757429Smarkm /* 348857429Smarkm * put back O_LOG if necessary 348957429Smarkm */ 349057429Smarkm src = (ipfw_insn *)cmdbuf; 349157429Smarkm if (src->opcode == O_LOG) { 349257429Smarkm i = F_LEN(src); 3493162856Sdes bcopy(src, dst, i * sizeof(uint32_t)); 349457429Smarkm dst += i; 349592559Sdes } 349660573Skris /* 349757429Smarkm * copy all other actions 349857429Smarkm */ 349957429Smarkm for (src = (ipfw_insn *)actbuf; src != action; src += i) { 350057429Smarkm i = F_LEN(src); 350160573Skris bcopy(src, dst, i * sizeof(uint32_t)); 350257429Smarkm dst += i; 350357429Smarkm } 350460573Skris 350557429Smarkm rule->cmd_len = (uint32_t *)dst - (uint32_t *)(rule->cmd); 350657429Smarkm i = (char *)dst - (char *)rule; 350757429Smarkm if (do_cmd(IP_FW_ADD, rule, (uintptr_t)&i) == -1) 350857429Smarkm err(EX_UNAVAILABLE, "getsockopt(%s)", "IP_FW_ADD"); 350957429Smarkm if (!do_quiet) 351092559Sdes show_ipfw(rule, 0, 0); 351157429Smarkm} 351257429Smarkm 351357429Smarkmstatic void 351460573Skriszero(int ac, char *av[], int optname /* IP_FW_ZERO or IP_FW_RESETLOG */) 351557429Smarkm{ 351657429Smarkm int rulenum; 351757429Smarkm int failed = EX_OK; 3518162856Sdes char const *name = optname == IP_FW_ZERO ? "ZERO" : "RESETLOG"; 351957429Smarkm 352092559Sdes av++; ac--; 352160573Skris 352257429Smarkm if (!ac) { 352357429Smarkm /* clear all entries */ 352457429Smarkm if (do_cmd(optname, NULL, 0) < 0) 352557429Smarkm err(EX_UNAVAILABLE, "setsockopt(IP_FW_%s)", name); 352657429Smarkm if (!do_quiet) 352757429Smarkm printf("%s.\n", optname == IP_FW_ZERO ? 3528162856Sdes "Accounting cleared":"Logging counts reset"); 352957429Smarkm 3530181111Sdes return; 3531181111Sdes } 353260573Skris 353357429Smarkm while (ac) { 353457429Smarkm /* Rule number */ 353557429Smarkm if (isdigit(**av)) { 3536124207Sdes rulenum = atoi(*av); 353757429Smarkm av++; 3538124207Sdes ac--; 353960573Skris if (do_cmd(optname, &rulenum, sizeof rulenum)) { 354060573Skris warn("rule %u: setsockopt(IP_FW_%s)", 354160573Skris rulenum, name); 354260573Skris failed = EX_UNAVAILABLE; 3543162856Sdes } else if (!do_quiet) 354460573Skris printf("Entry %d %s.\n", rulenum, 354560573Skris optname == IP_FW_ZERO ? 354660573Skris "cleared" : "logging count reset"); 354760573Skris } else { 354860573Skris errx(EX_USAGE, "invalid rule number ``%s''", *av); 354960573Skris } 355057429Smarkm } 355157429Smarkm if (failed != EX_OK) 355257429Smarkm exit(failed); 3553162856Sdes} 355457429Smarkm 355560573Skrisstatic void 355657429Smarkmflush(int force) 355792559Sdes{ 355860573Skris int cmd = do_pipe ? IP_DUMMYNET_FLUSH : IP_FW_FLUSH; 355960573Skris 356057429Smarkm if (!force && !do_quiet) { /* need to ask user */ 356160573Skris int c; 356260573Skris 356360573Skris printf("Are you sure? [yn] "); 356460573Skris fflush(stdout); 356560573Skris do { 356657429Smarkm c = toupper(getc(stdin)); 3567162856Sdes while (c != '\n' && getc(stdin) != '\n') 356860573Skris if (feof(stdin)) 356992559Sdes return; /* and do not flush */ 357060573Skris } while (c != 'Y' && c != 'N'); 357192559Sdes printf("\n"); 357292559Sdes if (c == 'N') /* user said no */ 357360573Skris return; 357457429Smarkm } 357592559Sdes if (do_cmd(cmd, NULL, 0) < 0) 357657429Smarkm err(EX_UNAVAILABLE, "setsockopt(IP_%s_FLUSH)", 357792559Sdes do_pipe ? "DUMMYNET" : "FW"); 357892559Sdes if (!do_quiet) 357992559Sdes printf("Flushed all %s.\n", do_pipe ? "pipes" : "rules"); 358092559Sdes} 358160573Skris 358260573Skris/* 358360573Skris * Free a the (locally allocated) copy of command line arguments. 358492559Sdes */ 358560573Skrisstatic void 358660573Skrisfree_args(int ac, char **av) 358760573Skris{ 358892559Sdes int i; 358992559Sdes 359092559Sdes for (i=0; i < ac; i++) 359192559Sdes free(av[i]); 359292559Sdes free(av); 359392559Sdes} 359492559Sdes 359592559Sdes/* 3596124207Sdes * Called with the arguments (excluding program name). 359792559Sdes * Returns 0 if successful, 1 if empty command, errx() in case of errors. 359860573Skris */ 359960573Skrisstatic int 360092559Sdesipfw_main(int oldac, char **oldav) 360160573Skris{ 360260573Skris int ch, ac, save_ac; 360360573Skris char **av, **save_av; 360492559Sdes int do_acct = 0; /* Show packet/byte count */ 360592559Sdes 360660573Skris#define WHITESP " \t\f\v\n\r" 360792559Sdes if (oldac == 0) 360857429Smarkm return 1; 360957429Smarkm else if (oldac == 1) { 361069587Sgreen /* 3611162856Sdes * If we are called with a single string, try to split it into 361269587Sgreen * arguments for subsequent parsing. 361392559Sdes * But first, remove spaces after a ',', by copying the string 361469587Sgreen * in-place. 361569587Sgreen */ 3616106130Sdes char *arg = oldav[0]; /* The string... */ 361792559Sdes int l = strlen(arg); 361869587Sgreen int copy = 0; /* 1 if we need to copy, 0 otherwise */ 361969587Sgreen int i, j; 362069587Sgreen for (i = j = 0; i < l; i++) { 362169587Sgreen if (arg[i] == '#') /* comment marker */ 362269587Sgreen break; 362369587Sgreen if (copy) { 362469587Sgreen arg[j++] = arg[i]; 362592559Sdes copy = !index("," WHITESP, arg[i]); 362669587Sgreen } else { 362769587Sgreen copy = !index(WHITESP, arg[i]); 3628157019Sdes if (copy) 362969587Sgreen arg[j++] = arg[i]; 363069587Sgreen } 363169587Sgreen } 363269587Sgreen if (!copy && j > 0) /* last char was a 'blank', remove it */ 363369587Sgreen j--; 363457429Smarkm l = j; /* the new argument length */ 363557429Smarkm arg[j++] = '\0'; 363657429Smarkm if (l == 0) /* empty string! */ 363792559Sdes return 1; 363857429Smarkm 363960573Skris /* 3640149753Sdes * First, count number of arguments. Because of the previous 3641226046Sdes * processing, this is just the number of blanks plus 1. 364257429Smarkm */ 364376262Sgreen for (i = 0, ac = 1; i < l; i++) 3644149753Sdes if (index(WHITESP, arg[i]) != NULL) 364557429Smarkm ac++; 364657429Smarkm 364757429Smarkm av = calloc(ac, sizeof(char *)); 3648137019Sdes 364957429Smarkm /* 3650149753Sdes * Second, copy arguments from cmd[] to av[]. For each one, 3651149753Sdes * j is the initial character, i is the one past the end. 3652149753Sdes */ 3653149753Sdes for (ac = 0, i = j = 0; i < l; i++) 3654149753Sdes if (index(WHITESP, arg[i]) != NULL || i == l-1) { 3655149753Sdes if (i == l-1) 3656149753Sdes i++; 3657149753Sdes av[ac] = calloc(i-j+1, 1); 3658162856Sdes bcopy(arg+j, av[ac], i-j); 365957429Smarkm ac++; 366057429Smarkm j = i + 1; 366157429Smarkm } 3662162856Sdes } else { 366357429Smarkm /* 366457429Smarkm * If an argument ends with ',' join with the next one. 366557429Smarkm */ 3666149753Sdes int first, i, l; 3667149753Sdes 3668149753Sdes av = calloc(oldac, sizeof(char *)); 3669149753Sdes for (first = i = ac = 0, l = 0; i < oldac; i++) { 3670149753Sdes char *arg = oldav[i]; 3671149753Sdes int k = strlen(arg); 3672149753Sdes 3673149753Sdes l += k; 3674149753Sdes if (arg[k-1] != ',' || i == oldac-1) { 3675149753Sdes /* Time to copy. */ 3676149753Sdes av[ac] = calloc(l+1, 1); 3677149753Sdes for (l=0; first <= i; first++) { 3678149753Sdes strcat(av[ac]+l, oldav[first]); 3679149753Sdes l += strlen(oldav[first]); 3680149753Sdes } 3681149753Sdes ac++; 3682149753Sdes l = 0; 3683149753Sdes first = i+1; 3684149753Sdes } 3685149753Sdes } 3686149753Sdes } 368757429Smarkm 368857429Smarkm /* Set the force flag for non-interactive processes */ 368957429Smarkm if (!do_force) 3690149753Sdes do_force = !isatty(STDIN_FILENO); 369157429Smarkm 369257429Smarkm /* Save arguments for final freeing of memory. */ 369360573Skris save_ac = ac; 3694226046Sdes save_av = av; 369560573Skris 369660573Skris optind = optreset = 0; 369760573Skris while ((ch = getopt(ac, av, "abcdefhnNqs:STtv")) != -1) 369860573Skris switch (ch) { 369960573Skris case 'a': 370060573Skris do_acct = 1; 370157429Smarkm break; 370257429Smarkm 370357429Smarkm case 'b': 370457429Smarkm comment_only = 1; 370557429Smarkm do_compact = 1; 370657429Smarkm break; 370792559Sdes 370892559Sdes case 'c': 370992559Sdes do_compact = 1; 371057429Smarkm break; 371157429Smarkm 371260573Skris case 'd': 371392559Sdes do_dynamic = 1; 371457429Smarkm break; 371557429Smarkm 371657429Smarkm case 'e': 371757429Smarkm do_expired = 1; 371857429Smarkm break; 3719 3720 case 'f': 3721 do_force = 1; 3722 break; 3723 3724 case 'h': /* help */ 3725 free_args(save_ac, save_av); 3726 help(); 3727 break; /* NOTREACHED */ 3728 3729 case 'n': 3730 test_only = 1; 3731 break; 3732 3733 case 'N': 3734 do_resolv = 1; 3735 break; 3736 3737 case 'q': 3738 do_quiet = 1; 3739 break; 3740 3741 case 's': /* sort */ 3742 do_sort = atoi(optarg); 3743 break; 3744 3745 case 'S': 3746 show_sets = 1; 3747 break; 3748 3749 case 't': 3750 do_time = 1; 3751 break; 3752 3753 case 'T': 3754 do_time = 2; /* numeric timestamp */ 3755 break; 3756 3757 case 'v': /* verbose */ 3758 verbose = 1; 3759 break; 3760 3761 default: 3762 free_args(save_ac, save_av); 3763 return 1; 3764 } 3765 3766 ac -= optind; 3767 av += optind; 3768 NEED1("bad arguments, for usage summary ``ipfw''"); 3769 3770 /* 3771 * An undocumented behaviour of ipfw1 was to allow rule numbers first, 3772 * e.g. "100 add allow ..." instead of "add 100 allow ...". 3773 * In case, swap first and second argument to get the normal form. 3774 */ 3775 if (ac > 1 && isdigit(*av[0])) { 3776 char *p = av[0]; 3777 3778 av[0] = av[1]; 3779 av[1] = p; 3780 } 3781 3782 /* 3783 * optional: pipe or queue 3784 */ 3785 do_pipe = 0; 3786 if (!strncmp(*av, "pipe", strlen(*av))) 3787 do_pipe = 1; 3788 else if (!strncmp(*av, "queue", strlen(*av))) 3789 do_pipe = 2; 3790 if (do_pipe) { 3791 ac--; 3792 av++; 3793 } 3794 NEED1("missing command"); 3795 3796 /* 3797 * For pipes and queues we normally say 'pipe NN config' 3798 * but the code is easier to parse as 'pipe config NN' 3799 * so we swap the two arguments. 3800 */ 3801 if (do_pipe > 0 && ac > 1 && isdigit(*av[0])) { 3802 char *p = av[0]; 3803 3804 av[0] = av[1]; 3805 av[1] = p; 3806 } 3807 3808 if (!strncmp(*av, "add", strlen(*av))) 3809 add(ac, av); 3810 else if (do_pipe && !strncmp(*av, "config", strlen(*av))) 3811 config_pipe(ac, av); 3812 else if (!strncmp(*av, "delete", strlen(*av))) 3813 delete(ac, av); 3814 else if (!strncmp(*av, "flush", strlen(*av))) 3815 flush(do_force); 3816 else if (!strncmp(*av, "zero", strlen(*av))) 3817 zero(ac, av, IP_FW_ZERO); 3818 else if (!strncmp(*av, "resetlog", strlen(*av))) 3819 zero(ac, av, IP_FW_RESETLOG); 3820 else if (!strncmp(*av, "print", strlen(*av)) || 3821 !strncmp(*av, "list", strlen(*av))) 3822 list(ac, av, do_acct); 3823 else if (!strncmp(*av, "set", strlen(*av))) 3824 sets_handler(ac, av); 3825 else if (!strncmp(*av, "enable", strlen(*av))) 3826 sysctl_handler(ac, av, 1); 3827 else if (!strncmp(*av, "disable", strlen(*av))) 3828 sysctl_handler(ac, av, 0); 3829 else if (!strncmp(*av, "show", strlen(*av))) 3830 list(ac, av, 1 /* show counters */); 3831 else 3832 errx(EX_USAGE, "bad command `%s'", *av); 3833 3834 /* Free memory allocated in the argument parsing. */ 3835 free_args(save_ac, save_av); 3836 return 0; 3837} 3838 3839 3840static void 3841ipfw_readfile(int ac, char *av[]) 3842{ 3843#define MAX_ARGS 32 3844 char buf[BUFSIZ]; 3845 char *cmd = NULL, *filename = av[ac-1]; 3846 int c, lineno=0; 3847 FILE *f = NULL; 3848 pid_t preproc = 0; 3849 3850 filename = av[ac-1]; 3851 3852 while ((c = getopt(ac, av, "cfNnp:qS")) != -1) { 3853 switch(c) { 3854 case 'c': 3855 do_compact = 1; 3856 break; 3857 3858 case 'f': 3859 do_force = 1; 3860 break; 3861 3862 case 'N': 3863 do_resolv = 1; 3864 break; 3865 3866 case 'n': 3867 test_only = 1; 3868 break; 3869 3870 case 'p': 3871 cmd = optarg; 3872 /* 3873 * Skip previous args and delete last one, so we 3874 * pass all but the last argument to the preprocessor 3875 * via av[optind-1] 3876 */ 3877 av += optind - 1; 3878 ac -= optind - 1; 3879 av[ac-1] = NULL; 3880 fprintf(stderr, "command is %s\n", av[0]); 3881 break; 3882 3883 case 'q': 3884 do_quiet = 1; 3885 break; 3886 3887 case 'S': 3888 show_sets = 1; 3889 break; 3890 3891 default: 3892 errx(EX_USAGE, "bad arguments, for usage" 3893 " summary ``ipfw''"); 3894 } 3895 3896 if (cmd != NULL) 3897 break; 3898 } 3899 3900 if (cmd == NULL && ac != optind + 1) { 3901 fprintf(stderr, "ac %d, optind %d\n", ac, optind); 3902 errx(EX_USAGE, "extraneous filename arguments"); 3903 } 3904 3905 if ((f = fopen(filename, "r")) == NULL) 3906 err(EX_UNAVAILABLE, "fopen: %s", filename); 3907 3908 if (cmd != NULL) { /* pipe through preprocessor */ 3909 int pipedes[2]; 3910 3911 if (pipe(pipedes) == -1) 3912 err(EX_OSERR, "cannot create pipe"); 3913 3914 preproc = fork(); 3915 if (preproc == -1) 3916 err(EX_OSERR, "cannot fork"); 3917 3918 if (preproc == 0) { 3919 /* 3920 * Child, will run the preprocessor with the 3921 * file on stdin and the pipe on stdout. 3922 */ 3923 if (dup2(fileno(f), 0) == -1 3924 || dup2(pipedes[1], 1) == -1) 3925 err(EX_OSERR, "dup2()"); 3926 fclose(f); 3927 close(pipedes[1]); 3928 close(pipedes[0]); 3929 execvp(cmd, av); 3930 err(EX_OSERR, "execvp(%s) failed", cmd); 3931 } else { /* parent, will reopen f as the pipe */ 3932 fclose(f); 3933 close(pipedes[1]); 3934 if ((f = fdopen(pipedes[0], "r")) == NULL) { 3935 int savederrno = errno; 3936 3937 (void)kill(preproc, SIGTERM); 3938 errno = savederrno; 3939 err(EX_OSERR, "fdopen()"); 3940 } 3941 } 3942 } 3943 3944 while (fgets(buf, BUFSIZ, f)) { /* read commands */ 3945 char linename[10]; 3946 char *args[1]; 3947 3948 lineno++; 3949 sprintf(linename, "Line %d", lineno); 3950 setprogname(linename); /* XXX */ 3951 args[0] = buf; 3952 ipfw_main(1, args); 3953 } 3954 fclose(f); 3955 if (cmd != NULL) { 3956 int status; 3957 3958 if (waitpid(preproc, &status, 0) == -1) 3959 errx(EX_OSERR, "waitpid()"); 3960 if (WIFEXITED(status) && WEXITSTATUS(status) != EX_OK) 3961 errx(EX_UNAVAILABLE, 3962 "preprocessor exited with status %d", 3963 WEXITSTATUS(status)); 3964 else if (WIFSIGNALED(status)) 3965 errx(EX_UNAVAILABLE, 3966 "preprocessor exited with signal %d", 3967 WTERMSIG(status)); 3968 } 3969} 3970 3971int 3972main(int ac, char *av[]) 3973{ 3974 /* 3975 * If the last argument is an absolute pathname, interpret it 3976 * as a file to be preprocessed. 3977 */ 3978 3979 if (ac > 1 && av[ac - 1][0] == '/' && access(av[ac - 1], R_OK) == 0) 3980 ipfw_readfile(ac, av); 3981 else { 3982 if (ipfw_main(ac-1, av+1)) 3983 show_usage(); 3984 } 3985 return EX_OK; 3986} 3987